diff options
| -rw-r--r-- | ext/js/background/backend.js | 71 | ||||
| -rw-r--r-- | types/ext/api.d.ts | 4 | ||||
| -rw-r--r-- | types/ext/application.d.ts | 4 | 
3 files changed, 59 insertions, 20 deletions
| diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index 38d82496..db7a3c0f 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -144,10 +144,13 @@ export class Backend {          this._permissions = null;          /** @type {PermissionsUtil} */          this._permissionsUtil = new PermissionsUtil(); +        /** @type {Map<string, (() => void)[]>} */ +        this._applicationReadyHandlers = new Map();          /* eslint-disable no-multi-spaces */          /** @type {import('api').ApiMap} */          this._apiMap = createApiMap([ +            ['applicationReady',             this._onApiApplicationReady.bind(this)],              ['requestBackendReadySignal',    this._onApiRequestBackendReadySignal.bind(this)],              ['optionsGet',                   this._onApiOptionsGet.bind(this)],              ['optionsGetFull',               this._onApiOptionsGetFull.bind(this)], @@ -425,6 +428,21 @@ export class Backend {      // Message handlers +    /** @type {import('api').ApiHandler<'applicationReady'>} */ +    _onApiApplicationReady(_params, sender) { +        const {tab, frameId} = sender; +        if (!tab || typeof frameId !== 'number') { return; } +        const {id} = tab; +        if (typeof id !== 'number') { return; } +        const key = `${id}:${frameId}`; +        const handlers = this._applicationReadyHandlers.get(key); +        if (typeof handlers === 'undefined') { return; } +        for (const handler of handlers) { +            handler(); +        } +        this._applicationReadyHandlers.delete(key); +    } +      /** @type {import('api').ApiHandler<'requestBackendReadySignal'>} */      _onApiRequestBackendReadySignal(_params, sender) {          // tab ID isn't set in background (e.g. browser_action) @@ -1806,18 +1824,8 @@ export class Backend {          return new Promise((resolve, reject) => {              /** @type {?import('core').Timeout} */              let timer = null; -            /** @type {?import('extension').ChromeRuntimeOnMessageCallback<import('application').ApiMessageAny>} */ -            let onMessage = (message, sender) => { -                if ( -                    !sender.tab || -                    sender.tab.id !== tabId || -                    sender.frameId !== frameId || -                    !(typeof message === 'object' && message !== null) || -                    message.action !== 'applicationReady' -                ) { -                    return; -                } +            const readyHandler = () => {                  cleanup();                  resolve();              }; @@ -1826,13 +1834,10 @@ export class Backend {                      clearTimeout(timer);                      timer = null;                  } -                if (onMessage !== null) { -                    chrome.runtime.onMessage.removeListener(onMessage); -                    onMessage = null; -                } +                this._removeApplicationReadyHandler(tabId, frameId, readyHandler);              }; -            chrome.runtime.onMessage.addListener(onMessage); +            this._addApplicationReadyHandler(tabId, frameId, readyHandler);              this._sendMessageTabPromise(tabId, {action: 'applicationIsReady'}, {frameId})                  .then( @@ -2686,4 +2691,38 @@ export class Backend {                  return defaultValue;          }      } + +    /** +     * @param {number} tabId +     * @param {number} frameId +     * @param {() => void} handler +     */ +    _addApplicationReadyHandler(tabId, frameId, handler) { +        const key = `${tabId}:${frameId}`; +        let handlers = this._applicationReadyHandlers.get(key); +        if (typeof handlers === 'undefined') { +            handlers = []; +            this._applicationReadyHandlers.set(key, handlers); +        } +        handlers.push(handler); +    } + +    /** +     * @param {number} tabId +     * @param {number} frameId +     * @param {() => void} handler +     * @returns {boolean} +     */ +    _removeApplicationReadyHandler(tabId, frameId, handler) { +        const key = `${tabId}:${frameId}`; +        const handlers = this._applicationReadyHandlers.get(key); +        if (typeof handlers === 'undefined') { return false; } +        const index = handlers.indexOf(handler); +        if (index < 0) { return false; } +        handlers.splice(index, 1); +        if (handlers.length === 0) { +            this._applicationReadyHandlers.delete(key); +        } +        return true; +    }  } diff --git a/types/ext/api.d.ts b/types/ext/api.d.ts index 3a639a9c..4f1b9026 100644 --- a/types/ext/api.d.ts +++ b/types/ext/api.d.ts @@ -112,6 +112,10 @@ export type GetTermFrequenciesDetailsTermReadingListItem = {  };  type ApiSurface = { +    applicationReady: { +        params: void; +        return: void; +    };      optionsGet: {          params: {              optionsContext: Settings.OptionsContext; diff --git a/types/ext/application.d.ts b/types/ext/application.d.ts index 3adc53f3..903c8e45 100644 --- a/types/ext/application.d.ts +++ b/types/ext/application.d.ts @@ -46,10 +46,6 @@ export type ApiSurface = {          };          return: void;      }; -    applicationReady: { -        params: void; -        return: void; -    };      applicationIsReady: {          params: void;          return: boolean; |