diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index 09eb89a6..781a9018 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -44,17 +44,14 @@ class FrameOffsetForwarder { async applyOffset(x, y) { const uniqueId = yomichan.generateId(16); - let frameOffsetResolve = null; - const frameOffsetPromise = new Promise((resolve) => (frameOffsetResolve = resolve)); - - const runtimeMessageCallback = ({action, params}, sender, callback) => { - if (action === 'frameOffset' && isObject(params) && params.uniqueId === uniqueId) { - chrome.runtime.onMessage.removeListener(runtimeMessageCallback); - callback(); - frameOffsetResolve(params); + const frameOffsetPromise = yomichan.getTemporaryListenerResult( + chrome.runtime.onMessage, + ({action, params}, {resolve}) => { + if (action === 'frameOffset' && isObject(params) && params.uniqueId === uniqueId) { + resolve(params); + } } - }; - chrome.runtime.onMessage.addListener(runtimeMessageCallback); + ); window.parent.postMessage({ action: 'getFrameOffset', diff --git a/ext/fg/js/frontend-initialize.js b/ext/fg/js/frontend-initialize.js index 51fa8d7a..97e315b5 100644 --- a/ext/fg/js/frontend-initialize.js +++ b/ext/fg/js/frontend-initialize.js @@ -32,19 +32,15 @@ async function main() { let popup; if (!proxy && (window !== window.parent)) { - let rootPopupInformationResolve; - const rootPopupInformationPromise = new Promise((resolve) => (rootPopupInformationResolve = resolve)); - - const runtimeMessageCallback = ({action, params}, sender, callback) => { - if (action === 'rootPopupInformation') { - chrome.runtime.onMessage.removeListener(runtimeMessageCallback); - callback(); - rootPopupInformationResolve(params); + const rootPopupInformationPromise = yomichan.getTemporaryListenerResult( + chrome.runtime.onMessage, + ({action, params}, {resolve}) => { + if (action === 'rootPopupInformation') { + resolve(params); + } } - }; - chrome.runtime.onMessage.addListener(runtimeMessageCallback); + ); apiForward('rootPopupRequestInformationBroadcast'); - const {popupId, frameId} = await rootPopupInformationPromise; const frameOffsetForwarder = new FrameOffsetForwarder(); diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index fd762e97..f21bac23 100644 --- a/ext/mixed/js/core.js +++ b/ext/mixed/js/core.js @@ -312,6 +312,42 @@ const yomichan = (() => { this.trigger('orphaned', {error}); } + getTemporaryListenerResult(eventHandler, userCallback, timeout=30000) { + let resolved = false; + let resolve; + let reject; + const listenerPromise = new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; + }); + + if (eventHandler === chrome.runtime.onMessage) { + const runtimeMessageCallback = ({action, params}, sender, sendResponse) => { + const cleanupResolve = (value) => { + resolved = true; + eventHandler.removeListener(runtimeMessageCallback); + sendResponse(); + resolve(value); + }; + + setTimeout(() => { + if (!resolved) { + reject(new Error(`Listener timed out in ${timeout} ms`)); + eventHandler.removeListener(runtimeMessageCallback); + } + }, timeout); + + userCallback({action, params}, {resolve: cleanupResolve, sender}); + }; + + eventHandler.addListener(runtimeMessageCallback); + } else { + throw new Error('Event handler type not supported'); + } + + return listenerPromise; + } + // Private _onMessage({action, params}, sender, callback) {