From 5c7d998f5ccf94b3b905588009b04f558c8ec27d Mon Sep 17 00:00:00 2001 From: Sidney Nemzer Date: Tue, 11 Jun 2024 20:08:32 -0400 Subject: [PATCH] remove external messaging (connecting to other extensions) Previously, we used ports to know which external contexts were interested in store updates. We don't use ports anymore, so there isn't an obvious mechanism to know which external contexts are interested in store updates. This means webext-redux can't be used across extensions anymore. I'm not sure how often this was used [1], but if there's use-cases for this, we could consider tracking contexts that recently requested messages and broadcast state updates to them, in addition to broadcasting state updates to the open tabs and our own extension pages. 1: Tried searching GitHub but found no results. This indicates the `extensionId` option is never used in open-source code. Or I didn't use the right search query. https://github.com/search?q=webext-redux+extensionId&type=code --- src/store/Store.js | 11 ++++------ src/wrap-store/wrapStore.js | 6 ------ test/Store.test.js | 39 ++++++++++-------------------------- test/applyMiddleware.test.js | 2 +- 4 files changed, 16 insertions(+), 42 deletions(-) diff --git a/src/store/Store.js b/src/store/Store.js index 160f848..689cce3 100644 --- a/src/store/Store.js +++ b/src/store/Store.js @@ -18,7 +18,6 @@ const backgroundErrPrefix = '\nLooks like there is an error in the background pa const defaultOpts = { portName: DEFAULT_PORT_NAME, state: {}, - extensionId: null, serializer: noop, deserializer: noop, patchStrategy: shallowDiff @@ -27,9 +26,9 @@ const defaultOpts = { class Store { /** * Creates a new Proxy store - * @param {object} options An object of form {portName, state, extensionId, serializer, deserializer, diffStrategy}, where `portName` is a required string and defines the name of the port for state transition changes, `state` is the initial state of this store (default `{}`) `extensionId` is the extension id as defined by browserAPI when extension is loaded (default `''`), `serializer` is a function to serialize outgoing message payloads (default is passthrough), `deserializer` is a function to deserialize incoming message payloads (default is passthrough), and patchStrategy is one of the included patching strategies (default is shallow diff) or a custom patching function. + * @param {object} options An object of form {portName, state, serializer, deserializer, diffStrategy}, where `portName` is a required string and defines the name of the port for state transition changes, `state` is the initial state of this store (default `{}`) `serializer` is a function to serialize outgoing message payloads (default is passthrough), `deserializer` is a function to deserialize incoming message payloads (default is passthrough), and patchStrategy is one of the included patching strategies (default is shallow diff) or a custom patching function. */ - constructor({portName = defaultOpts.portName, state = defaultOpts.state, extensionId = defaultOpts.extensionId, serializer = defaultOpts.serializer, deserializer = defaultOpts.deserializer, patchStrategy = defaultOpts.patchStrategy} = defaultOpts) { + constructor({portName = defaultOpts.portName, state = defaultOpts.state, serializer = defaultOpts.serializer, deserializer = defaultOpts.deserializer, patchStrategy = defaultOpts.patchStrategy} = defaultOpts) { if (!portName) { throw new Error('portName is required in options'); } @@ -48,17 +47,16 @@ class Store { this.readyPromise = new Promise(resolve => this.readyResolve = resolve); this.browserAPI = getBrowserAPI(); - this.extensionId = extensionId; // keep the extensionId as an instance variable this.initializeStore = this.initializeStore.bind(this); // We request the latest available state data to initialise our store this.browserAPI.runtime.sendMessage( - this.extensionId, { type: FETCH_STATE_TYPE, portName }, undefined, this.initializeStore + { type: FETCH_STATE_TYPE, portName }, undefined, this.initializeStore ); this.deserializer = deserializer; this.serializedPortListener = withDeserializer(deserializer)((...args) => this.browserAPI.runtime.onMessage.addListener(...args)); - this.serializedMessageSender = withSerializer(serializer)((...args) => this.browserAPI.runtime.sendMessage(...args), 1); + this.serializedMessageSender = withSerializer(serializer)((...args) => this.browserAPI.runtime.sendMessage(...args), 0); this.listeners = []; this.state = state; this.patchStrategy = patchStrategy; @@ -157,7 +155,6 @@ class Store { dispatch(data) { return new Promise((resolve, reject) => { this.serializedMessageSender( - this.extensionId, { type: DISPATCH_TYPE, portName: this.portName, diff --git a/src/wrap-store/wrapStore.js b/src/wrap-store/wrapStore.js index c155f2b..656528e 100644 --- a/src/wrap-store/wrapStore.js +++ b/src/wrap-store/wrapStore.js @@ -76,12 +76,6 @@ export default () => { browserAPI.runtime.onMessage.addListener(stateProviderListener.listener); browserAPI.runtime.onMessage.addListener(actionListener.listener); - if (browserAPI.runtime.onMessageExternal) { - browserAPI.runtime.onMessageExternal.addListener(actionListener.listener); - } else { - console.warn("runtime.onMessageExternal is not supported"); - } - return ( store, { diff --git a/test/Store.test.js b/test/Store.test.js index f4dce1d..df629e3 100644 --- a/test/Store.test.js +++ b/test/Store.test.js @@ -26,7 +26,7 @@ describe("Store", function () { }, }; }, - sendMessage(extensionId, data, options, cb) { + sendMessage(data, options, cb) { cb(); }, onMessage: { @@ -61,7 +61,7 @@ describe("Store", function () { spy.calledOnce.should.eql(true); spy - .alwaysCalledWith(null, { + .alwaysCalledWith({ type: FETCH_STATE_TYPE, portName, }) @@ -149,7 +149,7 @@ describe("Store", function () { const initializeStoreListener = []; // override mock chrome API for this test - self.chrome.runtime.sendMessage = (extensionId, message, options, listener) => { + self.chrome.runtime.sendMessage = (message, options, listener) => { initializeStoreListener.push(listener); }; @@ -323,20 +323,7 @@ describe("Store", function () { }); describe("#dispatch()", function () { - it("should send a message with the correct dispatch type and payload given an extensionId", function () { - const spy = (self.chrome.runtime.sendMessage = sinon.spy()); - const store = new Store({ portName, extensionId: "xxxxxxxxxxxx" }); - - store.dispatch({ a: "a" }); - - spy.callCount.should.eql(2); - spy.args[0][0].should.eql("xxxxxxxxxxxx"); - spy.args[0][1].should.eql({ type: FETCH_STATE_TYPE, portName: "test" }); - spy.args[1][0].should.eql("xxxxxxxxxxxx"); - spy.args[1][1].should.eql({ type: DISPATCH_TYPE, portName: "test", payload: { a: "a" } }); - }); - - it("should send a message with the correct dispatch type and payload not given an extensionId", function () { + it("should send a message with the correct dispatch type and payload", function () { const spy = (self.chrome.runtime.sendMessage = sinon.spy()), store = new Store({ portName }); @@ -344,10 +331,8 @@ describe("Store", function () { spy.callCount.should.eql(2); - should(spy.args[0][0]).eql(null); - spy.args[0][1].should.eql({ type: FETCH_STATE_TYPE, portName: "test" }); - should(spy.args[1][0]).eql(null); - spy.args[1][1].should.eql({ type: DISPATCH_TYPE, portName: "test", payload: { a: "a" } }); + spy.args[0][0].should.eql({ type: FETCH_STATE_TYPE, portName: "test" }); + spy.args[1][0].should.eql({ type: DISPATCH_TYPE, portName: "test", payload: { a: "a" } }); }); it("should serialize payloads before sending", function () { @@ -360,14 +345,12 @@ describe("Store", function () { spy.callCount.should.eql(2); - should(spy.args[0][0]).eql(null); - spy.args[0][1].should.eql({ type: FETCH_STATE_TYPE, portName: "test" }); - should(spy.args[1][0]).eql(null); - spy.args[1][1].should.eql({ type: DISPATCH_TYPE, portName: "test", payload: JSON.stringify({ a: "a" }) }); + spy.args[0][0].should.eql({ type: FETCH_STATE_TYPE, portName: "test" }); + spy.args[1][0].should.eql({ type: DISPATCH_TYPE, portName: "test", payload: JSON.stringify({ a: "a" }) }); }); it("should return a promise that resolves with successful action", function () { - self.chrome.runtime.sendMessage = (extensionId, data, options, cb) => { + self.chrome.runtime.sendMessage = (data, options, cb) => { cb({ value: { payload: "hello" } }); }; @@ -378,7 +361,7 @@ describe("Store", function () { }); it("should return a promise that rejects with an action error", function () { - self.chrome.runtime.sendMessage = (extensionId, data, options, cb) => { + self.chrome.runtime.sendMessage = (data, options, cb) => { cb({ value: { payload: "hello" }, error: { extraMsg: "test" } }); }; @@ -389,7 +372,7 @@ describe("Store", function () { }); it("should return a promise that resolves with undefined for an undefined return value", function () { - self.chrome.runtime.sendMessage = (extensionId, data, options, cb) => { + self.chrome.runtime.sendMessage = (data, options, cb) => { cb({ value: undefined }); }; diff --git a/test/applyMiddleware.test.js b/test/applyMiddleware.test.js index e60192d..10cbb17 100644 --- a/test/applyMiddleware.test.js +++ b/test/applyMiddleware.test.js @@ -55,7 +55,7 @@ describe('applyMiddleware', function () { }); it('passes recursive dispatches through the middleware chain', () => { - self.chrome.runtime.sendMessage = (extensionId, data, options, cb) => { + self.chrome.runtime.sendMessage = (data, options, cb) => { cb(data.payload); }; function test(spyOnMethods) {