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",
"generateId": "readonly",
"promiseAnimationFrame": "readonly",
"invokeMessageHandler": "readonly",
"log": "readonly",
"DynamicProperty": "readonly",
"EventDispatcher": "readonly",

View File

@ -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) {

View File

@ -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 {

View File

@ -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) {

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.
*/

View File

@ -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}) {

View File

@ -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) {

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) {
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() {