Cleanup yomichan api (#1394)
* Move invokeMessageHandler to core.js * Move getMessageResponseResult to backghend.js * Replace getTemporaryListenerResult
This commit is contained in:
parent
9279ced686
commit
48b59375eb
@ -111,6 +111,7 @@
|
||||
"deepEqual": "readonly",
|
||||
"generateId": "readonly",
|
||||
"promiseAnimationFrame": "readonly",
|
||||
"invokeMessageHandler": "readonly",
|
||||
"log": "readonly",
|
||||
"DynamicProperty": "readonly",
|
||||
"EventDispatcher": "readonly",
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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}) {
|
||||
|
@ -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) {
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user