Create mainWorldContentScriptBridge
These classes are the base of a communication bridge between the main
world and content scripts, which has been extracted from the main world
options watcher.
This will be used by the main world i18n component.
Bug: twpowertools:157
Change-Id: I08553b05648ad8453203ab08ecf01e824af15fea
diff --git a/src/common/mainWorldContentScriptBridge/Client.js b/src/common/mainWorldContentScriptBridge/Client.js
new file mode 100644
index 0000000..70e567b
--- /dev/null
+++ b/src/common/mainWorldContentScriptBridge/Client.js
@@ -0,0 +1,50 @@
+export const kDefaultTimeout = 10 * 1000; // 10 seconds
+
+export default class MainWorldContentScriptBridgeClient {
+ constructor(CSTarget, MWTarget, timeout) {
+ if (!CSTarget || !MWTarget) {
+ throw new Error(
+ `[MWOptionsWatcherClient] CSTarget and MWTarget are compulsory.`);
+ }
+
+ this.CSTarget = CSTarget;
+ this.MWTarget = MWTarget;
+ this.timeout = timeout ?? kDefaultTimeout;
+ }
+
+ _sendRequestWithoutCallback(action, request, uuid) {
+ if (!uuid) uuid = self.crypto.randomUUID();
+ const data = {
+ target: this.CSTarget,
+ uuid,
+ action,
+ request,
+ };
+ window.postMessage(data, '*');
+ }
+
+ _sendRequest(action, request) {
+ return new Promise((res, rej) => {
+ const uuid = self.crypto.randomUUID();
+
+ let timeoutId;
+ let listener = e => {
+ if (e.source !== window || e.data?.target !== this.MWTarget ||
+ e.data?.uuid !== uuid)
+ return;
+
+ window.removeEventListener('message', listener);
+ clearTimeout(timeoutId);
+ res(e.data?.response);
+ };
+ window.addEventListener('message', listener);
+
+ timeoutId = setTimeout(() => {
+ window.removeEventListener('message', listener);
+ rej(new Error('Timed out while waiting response.'));
+ }, this.timeout);
+
+ this._sendRequestWithoutCallback(action, request, uuid);
+ });
+ }
+}
diff --git a/src/common/mainWorldContentScriptBridge/Server.js b/src/common/mainWorldContentScriptBridge/Server.js
new file mode 100644
index 0000000..321a068
--- /dev/null
+++ b/src/common/mainWorldContentScriptBridge/Server.js
@@ -0,0 +1,37 @@
+export default class MainWorldContentScriptBridgeServer {
+ constructor(CSTarget, MWTarget) {
+ if (!CSTarget || !MWTarget) {
+ throw new Error(
+ `[MWOptionsWatcherServer] CSTarget and MWTarget are compulsory.`);
+ }
+
+ this.CSTarget = CSTarget;
+ this.MWTarget = MWTarget;
+ this.handler = () => {};
+ }
+
+ // Handler should be an action of the form (uuid, action, request) => {...}.
+ setUpHandler(handler) {
+ this.handler = handler;
+ window.addEventListener('message', e => this.#handleMessage(e));
+ }
+
+ #handleMessage(e) {
+ const uuid = e.data?.uuid;
+ if (e.source !== window || e.data?.target !== this.CSTarget || !uuid)
+ return;
+
+ const action = e.data?.action;
+ const request = e.data?.request;
+ return this.handler(uuid, action, request);
+ }
+
+ _respond(uuid, response) {
+ const data = {
+ target: this.MWTarget,
+ uuid,
+ response,
+ };
+ window.postMessage(data, window.origin);
+ }
+}
diff --git a/src/common/mainWorldOptionsWatcher/Client.js b/src/common/mainWorldOptionsWatcher/Client.js
index 3e5a9f9..a37b394 100644
--- a/src/common/mainWorldOptionsWatcher/Client.js
+++ b/src/common/mainWorldOptionsWatcher/Client.js
@@ -1,76 +1,35 @@
-export const kDefaultTimeout = 10 * 1000; // 10 seconds
+import MainWorldContentScriptBridgeClient from '../mainWorldContentScriptBridge/Client.js';
// Main World OptionsWatcher client (used in scripts injected into the Main
// World (MW) to get the options).
-export default class MWOptionsWatcherClient {
+export default class MWOptionsWatcherClient extends
+ MainWorldContentScriptBridgeClient {
constructor(options, CSTarget, MWTarget, timeout) {
- if (!CSTarget || !MWTarget)
- throw new Error(
- `[MWOptionsWatcherClient] CSTarget and MWTarget are compulsory.`);
-
- this.CSTarget = CSTarget;
- this.MWTarget = MWTarget;
- this.timeout = timeout ?? kDefaultTimeout;
+ super(CSTarget, MWTarget, timeout);
this.#setUp(options);
}
#setUp(options) {
- this.#sendRequestWithoutCallback('setUp', {options});
+ this._sendRequestWithoutCallback('setUp', {options});
}
async getOption(option) {
if (!option) return null;
- return this.#sendRequest('getOption', {option});
+ return this._sendRequest('getOption', {option});
}
async getOptions(options) {
if (!options || options?.length === 0) return [];
- return this.#sendRequest('getOptions', {options});
+ return this._sendRequest('getOptions', {options});
}
async isEnabled(option) {
if (!option) return null;
- return this.#sendRequest('isEnabled', {option});
+ return this._sendRequest('isEnabled', {option});
}
async areEnabled(options) {
if (!options || options?.length === 0) return [];
- return this.#sendRequest('areEnabled', {options});
- }
-
- #sendRequestWithoutCallback(action, request, uuid) {
- if (!uuid) uuid = self.crypto.randomUUID();
- const data = {
- target: this.CSTarget,
- uuid,
- action,
- request,
- };
- window.postMessage(data, '*');
- }
-
- #sendRequest(action, request) {
- return new Promise((res, rej) => {
- const uuid = self.crypto.randomUUID();
-
- let timeoutId;
- let listener = e => {
- if (e.source !== window || e.data?.target !== this.MWTarget ||
- e.data?.uuid !== uuid)
- return;
-
- window.removeEventListener('message', listener);
- clearTimeout(timeoutId);
- res(e.data?.response);
- };
- window.addEventListener('message', listener);
-
- timeoutId = setTimeout(() => {
- window.removeEventListener('message', listener);
- rej(new Error('Timed out while waiting response.'));
- }, this.timeout);
-
- this.#sendRequestWithoutCallback(action, request, uuid);
- });
+ return this._sendRequest('areEnabled', {options});
}
}
diff --git a/src/common/mainWorldOptionsWatcher/Server.js b/src/common/mainWorldOptionsWatcher/Server.js
index 0d30aac..353c63d 100644
--- a/src/common/mainWorldOptionsWatcher/Server.js
+++ b/src/common/mainWorldOptionsWatcher/Server.js
@@ -1,46 +1,38 @@
import OptionsWatcher from '../../common/optionsWatcher.js';
+import MainWorldContentScriptBridgeServer from '../mainWorldContentScriptBridge/Server.js';
// Main World OptionsWatcher server (used in content scripts to be able to serve
// the options to Main World (MW) scripts).
-export default class MWOptionsWatcherServer {
+export default class MWOptionsWatcherServer extends
+ MainWorldContentScriptBridgeServer {
constructor(CSTarget, MWTarget) {
- if (!CSTarget || !MWTarget)
- throw new Error(
- `[MWOptionsWatcherServer] CSTarget and MWTarget are compulsory.`);
-
+ super(CSTarget, MWTarget);
this.optionsWatcher = null;
- this.CSTarget = CSTarget;
- this.MWTarget = MWTarget;
-
- window.addEventListener('message', e => this.handleMessage(e));
+ this.setUpHandler(this.handleMessage);
}
- handleMessage(e) {
- const uuid = e.data?.uuid;
- if (e.source !== window || e.data?.target !== this.CSTarget || !uuid)
- return;
-
- if (e.data?.action === 'setUp') {
- this.optionsWatcher = new OptionsWatcher(e.data?.request?.options);
+ handleMessage(uuid, action, request) {
+ if (action === 'setUp') {
+ this.optionsWatcher = new OptionsWatcher(request?.options);
return;
}
if (!this.optionsWatcher) {
console.warn(`[MWOptionsWatcherServer] Action '${
- e.data?.action}' called before setting up options watcher.`);
+ action}' called before setting up options watcher.`);
return;
}
- switch (e.data?.action) {
+ switch (action) {
case 'getOption':
- this.optionsWatcher.getOption(e.data?.request?.option).then(value => {
- this.respond(uuid, value);
+ this.optionsWatcher.getOption(request?.option).then(value => {
+ this._respond(uuid, value);
});
return;
case 'getOptions':
var promises = [];
- var options = e.data?.request?.options ?? [];
+ var options = request?.options ?? [];
for (const option of options) {
promises.push(this.optionsWatcher.getOption(option));
}
@@ -49,19 +41,19 @@
for (let i = 0; i < responses.length; i++) {
response[options[i]] = responses[i];
}
- this.respond(uuid, response);
+ this._respond(uuid, response);
});
return;
case 'isEnabled':
- this.optionsWatcher.isEnabled(e.data?.request?.option).then(value => {
- this.respond(uuid, value);
+ this.optionsWatcher.isEnabled(request?.option).then(value => {
+ this._respond(uuid, value);
});
return;
case 'areEnabled':
var promises = [];
- var options = e.data?.request?.options ?? [];
+ var options = request?.options ?? [];
for (const option of options) {
promises.push(this.optionsWatcher.isEnabled(option));
}
@@ -70,22 +62,13 @@
for (let i = 0; i < responses.length; i++) {
response[options[i]] = responses[i];
}
- this.respond(uuid, response);
+ this._respond(uuid, response);
});
return;
default:
- console.error(`[MWOptionsWatcherServer] Invalid action received (${
- e.data?.action})`);
+ console.error(
+ `[MWOptionsWatcherServer] Invalid action received (${action})`);
}
}
-
- respond(uuid, response) {
- const data = {
- target: this.MWTarget,
- uuid,
- response,
- };
- window.postMessage(data, window.origin);
- }
}