From 48b59375eb50a3c11ab1cbee659164e6991827ac Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 14 Feb 2021 18:18:02 -0500 Subject: [PATCH] Cleanup yomichan api (#1394) * Move invokeMessageHandler to core.js * Move getMessageResponseResult to backghend.js * Replace getTemporaryListenerResult --- .eslintrc.json | 1 + ext/js/app/frontend.js | 51 +++++++++----- ext/js/background/backend.js | 19 +++++- ext/js/comm/cross-frame-api.js | 2 +- ext/js/core.js | 36 ++++++++++ ext/js/display/display.js | 2 +- ext/js/display/search-display-controller.js | 2 +- ext/js/yomichan.js | 75 +-------------------- 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 @@ -341,6 +341,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. */ 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() {