Cleanup yomichan api (#1394)

* Move invokeMessageHandler to core.js

* Move getMessageResponseResult to backghend.js

* Replace getTemporaryListenerResult
This commit is contained in:
toasted-nutbread 2021-02-14 18:18:02 -05:00 committed by GitHub
parent 9279ced686
commit 48b59375eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 93 additions and 95 deletions

View File

@ -111,6 +111,7 @@
"deepEqual": "readonly", "deepEqual": "readonly",
"generateId": "readonly", "generateId": "readonly",
"promiseAnimationFrame": "readonly", "promiseAnimationFrame": "readonly",
"invokeMessageHandler": "readonly",
"log": "readonly", "log": "readonly",
"DynamicProperty": "readonly", "DynamicProperty": "readonly",
"EventDispatcher": "readonly", "EventDispatcher": "readonly",

View File

@ -238,7 +238,7 @@ class Frontend {
_onRuntimeMessage({action, params}, sender, callback) { _onRuntimeMessage({action, params}, sender, callback) {
const messageHandler = this._runtimeMessageHandlers.get(action); const messageHandler = this._runtimeMessageHandlers.get(action);
if (typeof messageHandler === 'undefined') { return false; } if (typeof messageHandler === 'undefined') { return false; }
return yomichan.invokeMessageHandler(messageHandler, params, callback, sender); return invokeMessageHandler(messageHandler, params, callback, sender);
} }
_onZoomChanged({newZoomFactor}) { _onZoomChanged({newZoomFactor}) {
@ -455,7 +455,7 @@ class Frontend {
async _getIframeProxyPopup() { async _getIframeProxyPopup() {
const targetFrameId = 0; // Root frameId const targetFrameId = 0; // Root frameId
try { try {
await this._waitForFrontendReady(targetFrameId); await this._waitForFrontendReady(targetFrameId, 10000);
} catch (e) { } catch (e) {
// Root frame not available // Root frame not available
return await this._getDefaultPopup(); return await this._getDefaultPopup();
@ -613,21 +613,40 @@ class Frontend {
} }
} }
async _waitForFrontendReady(frameId) { async _waitForFrontendReady(frameId, timeout) {
const promise = yomichan.getTemporaryListenerResult( return new Promise((resolve, reject) => {
chrome.runtime.onMessage, let timeoutId = null;
({action, params}, {resolve}) => {
if ( const cleanup = () => {
action === 'frontendReady' && if (timeoutId !== null) {
params.frameId === frameId clearTimeout(timeoutId);
) { timeoutId = null;
resolve();
} }
}, chrome.runtime.onMessage.removeListener(onMessage);
10000 };
); const onMessage = (message, sender, sendResponse) => {
yomichan.api.broadcastTab('requestFrontendReadyBroadcast', {frameId: this._frameId}); try {
await promise; 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) { _getPreventMiddleMouseValueForPageType(preventMiddleMouseOptions) {

View File

@ -307,7 +307,7 @@ class Backend {
} }
} }
return yomichan.invokeMessageHandler(messageHandler, params, callback, sender); return invokeMessageHandler(messageHandler, params, callback, sender);
} }
_onConnect(port) { _onConnect(port) {
@ -1563,7 +1563,7 @@ class Backend {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const callback = (response) => { const callback = (response) => {
try { try {
resolve(yomichan.getMessageResponseResult(response)); resolve(this._getMessageResponseResult(response));
} catch (error) { } catch (error) {
reject(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) { async _checkTabUrl(tabId, urlPredicate) {
let tab; let tab;
try { try {

View File

@ -191,7 +191,7 @@ class CrossFrameAPIPort extends EventDispatcher {
} }
const callback = (data) => this._sendResult(id, data); const callback = (data) => this._sendResult(id, data);
return yomichan.invokeMessageHandler(messageHandler, params, callback); return invokeMessageHandler(messageHandler, params, callback);
} }
_sendResponse(data) { _sendResponse(data) {

View File

@ -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. * Base class controls basic event dispatching.
*/ */

View File

@ -463,7 +463,7 @@ class Display extends EventDispatcher {
if (typeof messageHandler === 'undefined') { return; } if (typeof messageHandler === 'undefined') { return; }
const callback = () => {}; // NOP const callback = () => {}; // NOP
yomichan.invokeMessageHandler(messageHandler, params, callback); invokeMessageHandler(messageHandler, params, callback);
} }
_onMessageSetOptionsContext({optionsContext}) { _onMessageSetOptionsContext({optionsContext}) {

View File

@ -104,7 +104,7 @@ class SearchDisplayController {
_onMessage({action, params}, sender, callback) { _onMessage({action, params}, sender, callback) {
const messageHandler = this._messageHandlers.get(action); const messageHandler = this._messageHandlers.get(action);
if (typeof messageHandler === 'undefined') { return false; } if (typeof messageHandler === 'undefined') { return false; }
return yomichan.invokeMessageHandler(messageHandler, params, callback, sender); return invokeMessageHandler(messageHandler, params, callback, sender);
} }
_onKeyDown(e) { _onKeyDown(e) {

View File

@ -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) { sendMessage(...args) {
try { try {
return chrome.runtime.sendMessage(...args); 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() { triggerExtensionUnloaded() {
this._isExtensionUnloaded = true; this._isExtensionUnloaded = true;
if (this._isTriggeringExtensionUnloaded) { return; } if (this._isTriggeringExtensionUnloaded) { return; }
@ -235,7 +162,7 @@ class Yomichan extends EventDispatcher {
_onMessage({action, params}, sender, callback) { _onMessage({action, params}, sender, callback) {
const messageHandler = this._messageHandlers.get(action); const messageHandler = this._messageHandlers.get(action);
if (typeof messageHandler === 'undefined') { return false; } if (typeof messageHandler === 'undefined') { return false; }
return this.invokeMessageHandler(messageHandler, params, callback, sender); return invokeMessageHandler(messageHandler, params, callback, sender);
} }
_onMessageIsReady() { _onMessageIsReady() {