aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.json1
-rw-r--r--ext/js/app/frontend.js51
-rw-r--r--ext/js/background/backend.js19
-rw-r--r--ext/js/comm/cross-frame-api.js2
-rw-r--r--ext/js/core.js36
-rw-r--r--ext/js/display/display.js2
-rw-r--r--ext/js/display/search-display-controller.js2
-rw-r--r--ext/js/yomichan.js75
8 files changed, 93 insertions, 95 deletions
diff --git a/.eslintrc.json b/.eslintrc.json
index fca1449b..369d26ab 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -111,6 +111,7 @@
"deepEqual": "readonly",
"generateId": "readonly",
"promiseAnimationFrame": "readonly",
+ "invokeMessageHandler": "readonly",
"log": "readonly",
"DynamicProperty": "readonly",
"EventDispatcher": "readonly",
diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js
index 02707f41..92366d6e 100644
--- a/ext/js/app/frontend.js
+++ b/ext/js/app/frontend.js
@@ -238,7 +238,7 @@ class Frontend {
_onRuntimeMessage({action, params}, sender, callback) {
const messageHandler = this._runtimeMessageHandlers.get(action);
if (typeof messageHandler === 'undefined') { return false; }
- return yomichan.invokeMessageHandler(messageHandler, params, callback, sender);
+ return invokeMessageHandler(messageHandler, params, callback, sender);
}
_onZoomChanged({newZoomFactor}) {
@@ -455,7 +455,7 @@ class Frontend {
async _getIframeProxyPopup() {
const targetFrameId = 0; // Root frameId
try {
- await this._waitForFrontendReady(targetFrameId);
+ await this._waitForFrontendReady(targetFrameId, 10000);
} catch (e) {
// Root frame not available
return await this._getDefaultPopup();
@@ -613,21 +613,40 @@ class Frontend {
}
}
- async _waitForFrontendReady(frameId) {
- const promise = yomichan.getTemporaryListenerResult(
- chrome.runtime.onMessage,
- ({action, params}, {resolve}) => {
- if (
- action === 'frontendReady' &&
- params.frameId === frameId
- ) {
- resolve();
+ async _waitForFrontendReady(frameId, timeout) {
+ return new Promise((resolve, reject) => {
+ let timeoutId = null;
+
+ const cleanup = () => {
+ if (timeoutId !== null) {
+ clearTimeout(timeoutId);
+ timeoutId = null;
}
- },
- 10000
- );
- yomichan.api.broadcastTab('requestFrontendReadyBroadcast', {frameId: this._frameId});
- await promise;
+ chrome.runtime.onMessage.removeListener(onMessage);
+ };
+ const onMessage = (message, sender, sendResponse) => {
+ try {
+ const {action, params} = message;
+ if (action === 'frontendReady' && params.frameId === frameId) {
+ cleanup();
+ resolve();
+ sendResponse();
+ }
+ } catch (e) {
+ // NOP
+ }
+ };
+
+ if (timeout !== null) {
+ timeoutId = setTimeout(() => {
+ timeoutId = null;
+ cleanup();
+ reject(new Error(`Wait for frontend ready timed out after ${timeout}ms`));
+ }, timeout);
+ }
+
+ chrome.runtime.onMessage.addListener(onMessage);
+ });
}
_getPreventMiddleMouseValueForPageType(preventMiddleMouseOptions) {
diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js
index 698724fc..d88c5853 100644
--- a/ext/js/background/backend.js
+++ b/ext/js/background/backend.js
@@ -307,7 +307,7 @@ class Backend {
}
}
- return yomichan.invokeMessageHandler(messageHandler, params, callback, sender);
+ return invokeMessageHandler(messageHandler, params, callback, sender);
}
_onConnect(port) {
@@ -1563,7 +1563,7 @@ class Backend {
return new Promise((resolve, reject) => {
const callback = (response) => {
try {
- resolve(yomichan.getMessageResponseResult(response));
+ resolve(this._getMessageResponseResult(response));
} catch (error) {
reject(error);
}
@@ -1573,6 +1573,21 @@ class Backend {
});
}
+ _getMessageResponseResult(response) {
+ let error = chrome.runtime.lastError;
+ if (error) {
+ throw new Error(error.message);
+ }
+ if (!isObject(response)) {
+ throw new Error('Tab did not respond');
+ }
+ error = response.error;
+ if (error) {
+ throw deserializeError(error);
+ }
+ return response.result;
+ }
+
async _checkTabUrl(tabId, urlPredicate) {
let tab;
try {
diff --git a/ext/js/comm/cross-frame-api.js b/ext/js/comm/cross-frame-api.js
index 7dbfb411..461ad45d 100644
--- a/ext/js/comm/cross-frame-api.js
+++ b/ext/js/comm/cross-frame-api.js
@@ -191,7 +191,7 @@ class CrossFrameAPIPort extends EventDispatcher {
}
const callback = (data) => this._sendResult(id, data);
- return yomichan.invokeMessageHandler(messageHandler, params, callback);
+ return invokeMessageHandler(messageHandler, params, callback);
}
_sendResponse(data) {
diff --git a/ext/js/core.js b/ext/js/core.js
index 4384d9f0..9b95c407 100644
--- a/ext/js/core.js
+++ b/ext/js/core.js
@@ -342,6 +342,42 @@ function promiseAnimationFrame(timeout=null) {
}
/**
+ * Invokes a standard message handler. This function is used to react and respond
+ * to communication messages within the extension.
+ * @param handler A handler function which is passed `params` and `...extraArgs` as arguments.
+ * @param async Whether or not the handler is async or not. Values include `false`, `true`, or `'dynamic'`.
+ * When the value is `'dynamic'`, the handler should return an object of the format `{async: boolean, result: any}`.
+ * @param params Information which was passed with the original message.
+ * @param callback A callback function which is invoked after the handler has completed. The value passed
+ * to the function is in the format:
+ * * `{result: any}` if the handler invoked successfully.
+ * * `{error: object}` if the handler thew an error. The error is serialized.
+ * @param extraArgs Additional arguments which are passed to the `handler` function.
+ * @returns `true` if the function is invoked asynchronously, `false` otherwise.
+ */
+function invokeMessageHandler({handler, async}, params, callback, ...extraArgs) {
+ try {
+ let promiseOrResult = handler(params, ...extraArgs);
+ if (async === 'dynamic') {
+ ({async, result: promiseOrResult} = promiseOrResult);
+ }
+ if (async) {
+ promiseOrResult.then(
+ (result) => { callback({result}); },
+ (error) => { callback({error: serializeError(error)}); }
+ );
+ return true;
+ } else {
+ callback({result: promiseOrResult});
+ return false;
+ }
+ } catch (error) {
+ callback({error: serializeError(error)});
+ return false;
+ }
+}
+
+/**
* Base class controls basic event dispatching.
*/
class EventDispatcher {
diff --git a/ext/js/display/display.js b/ext/js/display/display.js
index b7477bb8..a7c45f19 100644
--- a/ext/js/display/display.js
+++ b/ext/js/display/display.js
@@ -463,7 +463,7 @@ class Display extends EventDispatcher {
if (typeof messageHandler === 'undefined') { return; }
const callback = () => {}; // NOP
- yomichan.invokeMessageHandler(messageHandler, params, callback);
+ invokeMessageHandler(messageHandler, params, callback);
}
_onMessageSetOptionsContext({optionsContext}) {
diff --git a/ext/js/display/search-display-controller.js b/ext/js/display/search-display-controller.js
index 3b48af44..d1a4593f 100644
--- a/ext/js/display/search-display-controller.js
+++ b/ext/js/display/search-display-controller.js
@@ -104,7 +104,7 @@ class SearchDisplayController {
_onMessage({action, params}, sender, callback) {
const messageHandler = this._messageHandlers.get(action);
if (typeof messageHandler === 'undefined') { return false; }
- return yomichan.invokeMessageHandler(messageHandler, params, callback, sender);
+ return invokeMessageHandler(messageHandler, params, callback, sender);
}
_onKeyDown(e) {
diff --git a/ext/js/yomichan.js b/ext/js/yomichan.js
index 107694e9..1eae2d4d 100644
--- a/ext/js/yomichan.js
+++ b/ext/js/yomichan.js
@@ -120,42 +120,6 @@ class Yomichan extends EventDispatcher {
}
}
- getTemporaryListenerResult(eventHandler, userCallback, timeout=null) {
- if (!(
- typeof eventHandler.addListener === 'function' &&
- typeof eventHandler.removeListener === 'function'
- )) {
- throw new Error('Event handler type not supported');
- }
-
- return new Promise((resolve, reject) => {
- const runtimeMessageCallback = ({action, params}, sender, sendResponse) => {
- let timeoutId = null;
- if (timeout !== null) {
- timeoutId = setTimeout(() => {
- timeoutId = null;
- eventHandler.removeListener(runtimeMessageCallback);
- reject(new Error(`Listener timed out in ${timeout} ms`));
- }, timeout);
- }
-
- const cleanupResolve = (value) => {
- if (timeoutId !== null) {
- clearTimeout(timeoutId);
- timeoutId = null;
- }
- eventHandler.removeListener(runtimeMessageCallback);
- sendResponse();
- resolve(value);
- };
-
- userCallback({action, params}, {resolve: cleanupResolve, sender});
- };
-
- eventHandler.addListener(runtimeMessageCallback);
- });
- }
-
sendMessage(...args) {
try {
return chrome.runtime.sendMessage(...args);
@@ -174,43 +138,6 @@ class Yomichan extends EventDispatcher {
}
}
- getMessageResponseResult(response) {
- let error = chrome.runtime.lastError;
- if (error) {
- throw new Error(error.message);
- }
- if (!isObject(response)) {
- throw new Error('Tab did not respond');
- }
- error = response.error;
- if (error) {
- throw deserializeError(error);
- }
- return response.result;
- }
-
- invokeMessageHandler({handler, async}, params, callback, ...extraArgs) {
- try {
- let promiseOrResult = handler(params, ...extraArgs);
- if (async === 'dynamic') {
- ({async, result: promiseOrResult} = promiseOrResult);
- }
- if (async) {
- promiseOrResult.then(
- (result) => { callback({result}); },
- (error) => { callback({error: serializeError(error)}); }
- );
- return true;
- } else {
- callback({result: promiseOrResult});
- return false;
- }
- } catch (error) {
- callback({error: serializeError(error)});
- return false;
- }
- }
-
triggerExtensionUnloaded() {
this._isExtensionUnloaded = true;
if (this._isTriggeringExtensionUnloaded) { return; }
@@ -235,7 +162,7 @@ class Yomichan extends EventDispatcher {
_onMessage({action, params}, sender, callback) {
const messageHandler = this._messageHandlers.get(action);
if (typeof messageHandler === 'undefined') { return false; }
- return this.invokeMessageHandler(messageHandler, params, callback, sender);
+ return invokeMessageHandler(messageHandler, params, callback, sender);
}
_onMessageIsReady() {