diff options
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/bg/data/options-schema.json | 7 | ||||
| -rw-r--r-- | ext/bg/js/options.js | 3 | ||||
| -rw-r--r-- | ext/bg/js/search-frontend.js | 1 | ||||
| -rw-r--r-- | ext/bg/js/settings/main.js | 2 | ||||
| -rw-r--r-- | ext/bg/settings.html | 4 | ||||
| -rw-r--r-- | ext/fg/js/frame-offset-forwarder.js | 102 | ||||
| -rw-r--r-- | ext/fg/js/frontend-initialize.js | 30 | ||||
| -rw-r--r-- | ext/fg/js/frontend.js | 11 | ||||
| -rw-r--r-- | ext/fg/js/popup-proxy-host.js | 12 | ||||
| -rw-r--r-- | ext/fg/js/popup-proxy.js | 104 | ||||
| -rw-r--r-- | ext/fg/js/popup.js | 83 | ||||
| -rw-r--r-- | ext/manifest.json | 3 | ||||
| -rw-r--r-- | ext/mixed/js/core.js | 51 | 
13 files changed, 306 insertions, 107 deletions
| diff --git a/ext/bg/data/options-schema.json b/ext/bg/data/options-schema.json index cb759b72..da1f1ce0 100644 --- a/ext/bg/data/options-schema.json +++ b/ext/bg/data/options-schema.json @@ -108,7 +108,8 @@                                      "enableClipboardMonitor",                                      "showPitchAccentDownstepNotation",                                      "showPitchAccentPositionNotation", -                                    "showPitchAccentGraph" +                                    "showPitchAccentGraph", +                                    "showIframePopupsInRootFrame"                                  ],                                  "properties": {                                      "enable": { @@ -242,6 +243,10 @@                                      "showPitchAccentGraph": {                                          "type": "boolean",                                          "default": false +                                    }, +                                    "showIframePopupsInRootFrame": { +                                        "type": "boolean", +                                        "default": false                                      }                                  }                              }, diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index b36fe812..5c68c403 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -127,7 +127,8 @@ function profileOptionsCreateDefaults() {              enableClipboardMonitor: false,              showPitchAccentDownstepNotation: true,              showPitchAccentPositionNotation: true, -            showPitchAccentGraph: false +            showPitchAccentGraph: false, +            showIframePopupsInRootFrame: false          },          audio: { diff --git a/ext/bg/js/search-frontend.js b/ext/bg/js/search-frontend.js index 2d2aa8d4..f130a6fa 100644 --- a/ext/bg/js/search-frontend.js +++ b/ext/bg/js/search-frontend.js @@ -35,6 +35,7 @@ async function searchFrontendSetup() {      const scriptSrcs = [          '/mixed/js/text-scanner.js',          '/fg/js/frontend-api-receiver.js', +        '/fg/js/frame-offset-forwarder.js',          '/fg/js/popup.js',          '/fg/js/popup-proxy-host.js',          '/fg/js/frontend.js', diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js index 7caeaea0..1653ee35 100644 --- a/ext/bg/js/settings/main.js +++ b/ext/bg/js/settings/main.js @@ -87,6 +87,7 @@ async function formRead(options) {      options.general.showPitchAccentDownstepNotation = $('#show-pitch-accent-downstep-notation').prop('checked');      options.general.showPitchAccentPositionNotation = $('#show-pitch-accent-position-notation').prop('checked');      options.general.showPitchAccentGraph = $('#show-pitch-accent-graph').prop('checked'); +    options.general.showIframePopupsInRootFrame = $('#show-iframe-popups-in-root-frame').prop('checked');      options.general.popupTheme = $('#popup-theme').val();      options.general.popupOuterTheme = $('#popup-outer-theme').val();      options.general.customPopupCss = $('#custom-popup-css').val(); @@ -167,6 +168,7 @@ async function formWrite(options) {      $('#show-pitch-accent-downstep-notation').prop('checked', options.general.showPitchAccentDownstepNotation);      $('#show-pitch-accent-position-notation').prop('checked', options.general.showPitchAccentPositionNotation);      $('#show-pitch-accent-graph').prop('checked', options.general.showPitchAccentGraph); +    $('#show-iframe-popups-in-root-frame').prop('checked', options.general.showIframePopupsInRootFrame);      $('#popup-theme').val(options.general.popupTheme);      $('#popup-outer-theme').val(options.general.popupOuterTheme);      $('#custom-popup-css').val(options.general.customPopupCss); diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 0b2e4f9c..237162c7 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -175,6 +175,10 @@                  </div>                  <div class="checkbox options-advanced"> +                    <label><input type="checkbox" id="show-iframe-popups-in-root-frame"> Show iframe popups in root frame</label> +                </div> + +                <div class="checkbox options-advanced">                      <label><input type="checkbox" id="show-debug-info"> Show debug information</label>                  </div> diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js new file mode 100644 index 00000000..7b417b6e --- /dev/null +++ b/ext/fg/js/frame-offset-forwarder.js @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020  Alex Yatskov <alex@foosoft.net> + * Author: Alex Yatskov <alex@foosoft.net> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <https://www.gnu.org/licenses/>. + */ + +/* global + * apiForward + */ + +class FrameOffsetForwarder { +    constructor() { +        this._started = false; + +        this._forwardFrameOffset = ( +            window !== window.parent ? +            this._forwardFrameOffsetParent.bind(this) : +            this._forwardFrameOffsetOrigin.bind(this) +        ); + +        this._windowMessageHandlers = new Map([ +            ['getFrameOffset', ({offset, uniqueId}, e) => this._onGetFrameOffset(offset, uniqueId, e)] +        ]); +    } + +    start() { +        if (this._started) { return; } +        window.addEventListener('message', this.onMessage.bind(this), false); +        this._started = true; +    } + +    async getOffset() { +        const uniqueId = yomichan.generateId(16); + +        const frameOffsetPromise = yomichan.getTemporaryListenerResult( +            chrome.runtime.onMessage, +            ({action, params}, {resolve}) => { +                if (action === 'frameOffset' && isObject(params) && params.uniqueId === uniqueId) { +                    resolve(params); +                } +            }, +            5000 +        ); + +        window.parent.postMessage({ +            action: 'getFrameOffset', +            params: { +                uniqueId, +                offset: [0, 0] +            } +        }, '*'); + +        const {offset} = await frameOffsetPromise; +        return offset; +    } + +    onMessage(e) { +        const {action, params} = e.data; +        const handler = this._windowMessageHandlers.get(action); +        if (typeof handler !== 'function') { return; } +        handler(params, e); +    } + +    _onGetFrameOffset(offset, uniqueId, e) { +        let sourceFrame = null; +        for (const frame of document.querySelectorAll('frame, iframe:not(.yomichan-float)')) { +            if (frame.contentWindow !== e.source) { continue; } +            sourceFrame = frame; +            break; +        } +        if (sourceFrame === null) { +            this._forwardFrameOffsetOrigin(null, uniqueId); +            return; +        } + +        const [forwardedX, forwardedY] = offset; +        const {x, y} = sourceFrame.getBoundingClientRect(); +        offset = [forwardedX + x, forwardedY + y]; + +        this._forwardFrameOffset(offset, uniqueId); +    } + +    _forwardFrameOffsetParent(offset, uniqueId) { +        window.parent.postMessage({action: 'getFrameOffset', params: {offset, uniqueId}}, '*'); +    } + +    _forwardFrameOffsetOrigin(offset, uniqueId) { +        apiForward('frameOffset', {offset, uniqueId}); +    } +} diff --git a/ext/fg/js/frontend-initialize.js b/ext/fg/js/frontend-initialize.js index 3a191247..4a1409db 100644 --- a/ext/fg/js/frontend-initialize.js +++ b/ext/fg/js/frontend-initialize.js @@ -17,9 +17,12 @@   */  /* global + * FrameOffsetForwarder   * Frontend   * PopupProxy   * PopupProxyHost + * apiForward + * apiOptionsGet   */  async function main() { @@ -28,10 +31,35 @@ async function main() {      const data = window.frontendInitializationData || {};      const {id, depth=0, parentFrameId, url, proxy=false} = data; +    const optionsContext = {depth, url}; +    const options = await apiOptionsGet(optionsContext); +      let popup; -    if (proxy) { +    if (!proxy && (window !== window.parent) && options.general.showIframePopupsInRootFrame) { +        const rootPopupInformationPromise = yomichan.getTemporaryListenerResult( +            chrome.runtime.onMessage, +            ({action, params}, {resolve}) => { +                if (action === 'rootPopupInformation') { +                    resolve(params); +                } +            } +        ); +        apiForward('rootPopupRequestInformationBroadcast'); +        const {popupId, frameId} = await rootPopupInformationPromise; + +        const frameOffsetForwarder = new FrameOffsetForwarder(); +        frameOffsetForwarder.start(); +        const getFrameOffset = frameOffsetForwarder.getOffset.bind(frameOffsetForwarder); + +        popup = new PopupProxy(popupId, 0, null, frameId, url, getFrameOffset); +        await popup.prepare(); +    } else if (proxy) {          popup = new PopupProxy(null, depth + 1, id, parentFrameId, url); +        await popup.prepare();      } else { +        const frameOffsetForwarder = new FrameOffsetForwarder(); +        frameOffsetForwarder.start(); +          const popupHost = new PopupProxyHost();          await popupHost.prepare(); diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index d6c5eac6..4e9d474c 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -18,6 +18,7 @@  /* global   * TextScanner + * apiForward   * apiGetZoom   * apiKanjiFind   * apiOptionsGet @@ -52,7 +53,8 @@ class Frontend extends TextScanner {          ]);          this._runtimeMessageHandlers = new Map([ -            ['popupSetVisibleOverride', ({visible}) => { this.popup.setVisibleOverride(visible); }] +            ['popupSetVisibleOverride', ({visible}) => { this.popup.setVisibleOverride(visible); }], +            ['rootPopupRequestInformationBroadcast', () => { this._broadcastRootPopupInformation(); }]          ]);      } @@ -76,6 +78,7 @@ class Frontend extends TextScanner {              chrome.runtime.onMessage.addListener(this.onRuntimeMessage.bind(this));              this._updateContentScale(); +            this._broadcastRootPopupInformation();          } catch (e) {              this.onError(e);          } @@ -255,6 +258,12 @@ class Frontend extends TextScanner {          this._updatePopupPosition();      } +    _broadcastRootPopupInformation() { +        if (!this.popup.isProxy() && this.popup.depth === 0) { +            apiForward('rootPopupInformation', {popupId: this.popup.id, frameId: this.popup.frameId}); +        } +    } +      async _updatePopupPosition() {          const textSource = this.getCurrentTextSource();          if (textSource !== null && await this.popup.isVisible()) { diff --git a/ext/fg/js/popup-proxy-host.js b/ext/fg/js/popup-proxy-host.js index 6f1c13c6..4b136e41 100644 --- a/ext/fg/js/popup-proxy-host.js +++ b/ext/fg/js/popup-proxy-host.js @@ -26,17 +26,17 @@ class PopupProxyHost {      constructor() {          this._popups = new Map();          this._apiReceiver = null; -        this._frameIdPromise = null; +        this._frameId = null;      }      // Public functions      async prepare() { -        this._frameIdPromise = apiFrameInformationGet(); -        const {frameId} = await this._frameIdPromise; +        const {frameId} = await apiFrameInformationGet();          if (typeof frameId !== 'number') { return; } +        this._frameId = frameId; -        this._apiReceiver = new FrontendApiReceiver(`popup-proxy-host#${frameId}`, new Map([ +        this._apiReceiver = new FrontendApiReceiver(`popup-proxy-host#${this._frameId}`, new Map([              ['getOrCreatePopup', this._onApiGetOrCreatePopup.bind(this)],              ['setOptions', this._onApiSetOptions.bind(this)],              ['hide', this._onApiHide.bind(this)], @@ -87,7 +87,7 @@ class PopupProxyHost {          } else if (depth === null) {              depth = 0;          } -        const popup = new Popup(id, depth, this._frameIdPromise); +        const popup = new Popup(id, depth, this._frameId);          if (parent !== null) {              popup.setParent(parent);          } @@ -95,7 +95,7 @@ class PopupProxyHost {          return popup;      } -    // Message handlers +    // API message handlers      async _onApiGetOrCreatePopup({id, parentId}) {          const popup = this.getOrCreatePopup(id, parentId); diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index 997b1317..966198a9 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -21,14 +21,18 @@   */  class PopupProxy { -    constructor(id, depth, parentId, parentFrameId, url) { +    constructor(id, depth, parentId, parentFrameId, url, getFrameOffset=null) {          this._parentId = parentId;          this._parentFrameId = parentFrameId;          this._id = id; -        this._idPromise = null;          this._depth = depth;          this._url = url;          this._apiSender = new FrontendApiSender(); +        this._getFrameOffset = getFrameOffset; + +        this._frameOffset = null; +        this._frameOffsetPromise = null; +        this._frameOffsetUpdatedAt = null;      }      // Public properties @@ -51,92 +55,106 @@ class PopupProxy {      // Public functions +    async prepare() { +        const {id} = await this._invokeHostApi('getOrCreatePopup', {id: this._id, parentId: this._parentId}); +        this._id = id; +    } +      isProxy() {          return true;      }      async setOptions(options) { -        const id = await this._getPopupId(); -        return await this._invokeHostApi('setOptions', {id, options}); +        return await this._invokeHostApi('setOptions', {id: this._id, options});      }      hide(changeFocus) { -        if (this._id === null) { -            return; -        }          this._invokeHostApi('hide', {id: this._id, changeFocus});      }      async isVisible() { -        const id = await this._getPopupId(); -        return await this._invokeHostApi('isVisible', {id}); +        return await this._invokeHostApi('isVisible', {id: this._id});      }      setVisibleOverride(visible) { -        if (this._id === null) { -            return; -        }          this._invokeHostApi('setVisibleOverride', {id: this._id, visible});      }      async containsPoint(x, y) { -        if (this._id === null) { -            return false; +        if (this._getFrameOffset !== null) { +            await this._updateFrameOffset(); +            [x, y] = this._applyFrameOffset(x, y);          }          return await this._invokeHostApi('containsPoint', {id: this._id, x, y});      }      async showContent(elementRect, writingMode, type=null, details=null) { -        const id = await this._getPopupId(); -        elementRect = PopupProxy._convertDOMRectToJson(elementRect); -        return await this._invokeHostApi('showContent', {id, elementRect, writingMode, type, details}); +        let {x, y, width, height} = elementRect; +        if (this._getFrameOffset !== null) { +            await this._updateFrameOffset(); +            [x, y] = this._applyFrameOffset(x, y); +        } +        elementRect = {x, y, width, height}; +        return await this._invokeHostApi('showContent', {id: this._id, elementRect, writingMode, type, details});      }      async setCustomCss(css) { -        const id = await this._getPopupId(); -        return await this._invokeHostApi('setCustomCss', {id, css}); +        return await this._invokeHostApi('setCustomCss', {id: this._id, css});      }      clearAutoPlayTimer() { -        if (this._id === null) { -            return; -        }          this._invokeHostApi('clearAutoPlayTimer', {id: this._id});      }      async setContentScale(scale) { -        const id = await this._getPopupId(); -        this._invokeHostApi('setContentScale', {id, scale}); +        this._invokeHostApi('setContentScale', {id: this._id, scale});      }      // Private -    _getPopupId() { -        if (this._idPromise === null) { -            this._idPromise = this._getPopupIdAsync(); +    _invokeHostApi(action, params={}) { +        if (typeof this._parentFrameId !== 'number') { +            return Promise.reject(new Error('Invalid frame'));          } -        return this._idPromise; +        return this._apiSender.invoke(action, params, `popup-proxy-host#${this._parentFrameId}`);      } -    async _getPopupIdAsync() { -        const {id} = await this._invokeHostApi('getOrCreatePopup', {id: this._id, parentId: this._parentId}); -        this._id = id; -        return id; +    async _updateFrameOffset() { +        const now = Date.now(); +        const firstRun = this._frameOffsetUpdatedAt === null; +        const expired = firstRun || this._frameOffsetUpdatedAt < now - PopupProxy._frameOffsetExpireTimeout; +        if (this._frameOffsetPromise === null && !expired) { return; } + +        if (this._frameOffsetPromise !== null) { +            if (firstRun) { +                await this._frameOffsetPromise; +            } +            return; +        } + +        const promise = this._updateFrameOffsetInner(now); +        if (firstRun) { +            await promise; +        }      } -    _invokeHostApi(action, params={}) { -        if (typeof this._parentFrameId !== 'number') { -            return Promise.reject(new Error('Invalid frame')); +    async _updateFrameOffsetInner(now) { +        this._frameOffsetPromise = this._getFrameOffset(); +        try { +            const offset = await this._frameOffsetPromise; +            this._frameOffset = offset !== null ? offset : [0, 0]; +            this._frameOffsetUpdatedAt = now; +        } catch (e) { +            logError(e); +        } finally { +            this._frameOffsetPromise = null;          } -        return this._apiSender.invoke(action, params, `popup-proxy-host#${this._parentFrameId}`);      } -    static _convertDOMRectToJson(domRect) { -        return { -            x: domRect.x, -            y: domRect.y, -            width: domRect.width, -            height: domRect.height -        }; +    _applyFrameOffset(x, y) { +        const [offsetX, offsetY] = this._frameOffset; +        return [x + offsetX, y + offsetY];      }  } + +PopupProxy._frameOffsetExpireTimeout = 1000; diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index e6e93a76..60dc16dd 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -22,11 +22,10 @@   */  class Popup { -    constructor(id, depth, frameIdPromise) { +    constructor(id, depth, frameId) {          this._id = id;          this._depth = depth; -        this._frameIdPromise = frameIdPromise; -        this._frameId = null; +        this._frameId = frameId;          this._parent = null;          this._child = null;          this._childrenSupported = true; @@ -69,6 +68,10 @@ class Popup {          return this._depth;      } +    get frameId() { +        return this._frameId; +    } +      get url() {          return window.location.href;      } @@ -193,40 +196,42 @@ class Popup {      }      async _createInjectPromise() { -        try { -            const {frameId} = await this._frameIdPromise; -            if (typeof frameId === 'number') { -                this._frameId = frameId; -            } -        } catch (e) { -            // NOP -        } -          if (this._messageToken === null) {              this._messageToken = await apiGetMessageToken();          } -        return new Promise((resolve) => { -            const parentFrameId = (typeof this._frameId === 'number' ? this._frameId : null); -            this._container.setAttribute('src', chrome.runtime.getURL('/fg/float.html')); -            this._container.addEventListener('load', () => { -                this._listenForDisplayPrepareCompleted(resolve); - -                this._invokeApi('prepare', { -                    popupInfo: { -                        id: this._id, -                        depth: this._depth, -                        parentFrameId -                    }, -                    url: this.url, -                    childrenSupported: this._childrenSupported, -                    scale: this._contentScale -                }); +        const popupPreparedPromise = yomichan.getTemporaryListenerResult( +            chrome.runtime.onMessage, +            ({action, params}, {resolve}) => { +                if ( +                    action === 'popupPrepareCompleted' && +                    isObject(params) && +                    params.targetPopupId === this._id +                ) { +                    resolve(); +                } +            } +        ); + +        const parentFrameId = (typeof this._frameId === 'number' ? this._frameId : null); +        this._container.setAttribute('src', chrome.runtime.getURL('/fg/float.html')); +        this._container.addEventListener('load', () => { +            this._invokeApi('prepare', { +                popupInfo: { +                    id: this._id, +                    depth: this._depth, +                    parentFrameId +                }, +                url: this.url, +                childrenSupported: this._childrenSupported, +                scale: this._contentScale              }); -            this._observeFullscreen(true); -            this._onFullscreenChanged(); -            this._injectStyles();          }); +        this._observeFullscreen(true); +        this._onFullscreenChanged(); +        this._injectStyles(); + +        return popupPreparedPromise;      }      async _injectStyles() { @@ -361,22 +366,6 @@ class Popup {          contentWindow.postMessage({action, params, token}, this._targetOrigin);      } -    _listenForDisplayPrepareCompleted(resolve) { -        const runtimeMessageCallback = ({action, params}, sender, callback) => { -            if ( -                action === 'popupPrepareCompleted' && -                isObject(params) && -                params.targetPopupId === this._id -            ) { -                chrome.runtime.onMessage.removeListener(runtimeMessageCallback); -                callback(); -                resolve(); -                return false; -            } -        }; -        chrome.runtime.onMessage.addListener(runtimeMessageCallback); -    } -      static _getFullscreenElement() {          return (              document.fullscreenElement || diff --git a/ext/manifest.json b/ext/manifest.json index 3cea8647..98965389 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -23,9 +23,12 @@              "mixed/js/api.js",              "mixed/js/text-scanner.js",              "fg/js/document.js", +            "fg/js/frontend-api-sender.js",              "fg/js/frontend-api-receiver.js",              "fg/js/popup.js",              "fg/js/source.js", +            "fg/js/frame-offset-forwarder.js", +            "fg/js/popup-proxy.js",              "fg/js/popup-proxy-host.js",              "fg/js/frontend.js",              "fg/js/frontend-initialize.js" diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index fd762e97..db7fc69b 100644 --- a/ext/mixed/js/core.js +++ b/ext/mixed/js/core.js @@ -278,11 +278,16 @@ const yomichan = (() => {          constructor() {              super(); -            this._isBackendPreparedResolve = null; -            this._isBackendPreparedPromise = new Promise((resolve) => (this._isBackendPreparedResolve = resolve)); +            this._isBackendPreparedPromise = this.getTemporaryListenerResult( +                chrome.runtime.onMessage, +                ({action}, {resolve}) => { +                    if (action === 'backendPrepared') { +                        resolve(); +                    } +                } +            );              this._messageHandlers = new Map([ -                ['backendPrepared', this._onBackendPrepared.bind(this)],                  ['getUrl', this._onMessageGetUrl.bind(this)],                  ['optionsUpdated', this._onMessageOptionsUpdated.bind(this)],                  ['zoomChanged', this._onMessageZoomChanged.bind(this)] @@ -312,6 +317,42 @@ const yomichan = (() => {              this.trigger('orphaned', {error});          } +        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 = window.setTimeout(() => { +                            timeoutId = null; +                            eventHandler.removeListener(runtimeMessageCallback); +                            reject(new Error(`Listener timed out in ${timeout} ms`)); +                        }, timeout); +                    } + +                    const cleanupResolve = (value) => { +                        if (timeoutId !== null) { +                            window.clearTimeout(timeoutId); +                            timeoutId = null; +                        } +                        eventHandler.removeListener(runtimeMessageCallback); +                        sendResponse(); +                        resolve(value); +                    }; + +                    userCallback({action, params}, {resolve: cleanupResolve, sender}); +                }; + +                eventHandler.addListener(runtimeMessageCallback); +            }); +        } +          // Private          _onMessage({action, params}, sender, callback) { @@ -323,10 +364,6 @@ const yomichan = (() => {              return false;          } -        _onBackendPrepared() { -            this._isBackendPreparedResolve(); -        } -          _onMessageGetUrl() {              return {url: window.location.href};          } |