From 6806e7055f312ad1ff382118255ffe1004b7eace Mon Sep 17 00:00:00 2001 From: siikamiika Date: Thu, 19 Mar 2020 00:00:42 +0200 Subject: [PATCH 01/24] show iframe popups in root frame --- ext/fg/js/frontend-initialize.js | 21 ++++++++++++++++++++- ext/fg/js/frontend.js | 3 ++- ext/fg/js/popup.js | 19 +++++++++++++++++++ ext/manifest.json | 2 ++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/ext/fg/js/frontend-initialize.js b/ext/fg/js/frontend-initialize.js index 3a191247..7f70d9c4 100644 --- a/ext/fg/js/frontend-initialize.js +++ b/ext/fg/js/frontend-initialize.js @@ -20,6 +20,7 @@ * Frontend * PopupProxy * PopupProxyHost + * apiForward */ async function main() { @@ -29,7 +30,25 @@ async function main() { const {id, depth=0, parentFrameId, url, proxy=false} = data; let popup; - if (proxy) { + 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); + return false; + } + }; + chrome.runtime.onMessage.addListener(runtimeMessageCallback); + apiForward('rootPopupInformationGet'); + + const {popupId, frameId} = await rootPopupInformationPromise; + + popup = new PopupProxy(popupId, 0, null, frameId, url); + } else if (proxy) { popup = new PopupProxy(null, depth + 1, id, parentFrameId, url); } else { const popupHost = new PopupProxyHost(); diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index d6c5eac6..af5341c4 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -52,7 +52,8 @@ class Frontend extends TextScanner { ]); this._runtimeMessageHandlers = new Map([ - ['popupSetVisibleOverride', ({visible}) => { this.popup.setVisibleOverride(visible); }] + ['popupSetVisibleOverride', ({visible}) => { this.popup.setVisibleOverride(visible); }], + ['rootPopupInformationGet', () => { this.popup.broadcastRootPopupInformation(); }] ]); } diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index e6e93a76..e6596a1a 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -17,6 +17,7 @@ */ /* global + * apiForward * apiGetMessageToken * apiInjectStylesheet */ @@ -79,6 +80,20 @@ class Popup { return false; } + async broadcastRootPopupInformation() { + if (this._depth === 0) { + try { + const {frameId} = await this._frameIdPromise; + if (typeof frameId === 'number') { + this._frameId = frameId; + } + } catch (e) { + // NOP + } + apiForward('rootPopupInformation', {popupId: this._id, frameId: this._frameId}); + } + } + async setOptions(options) { this._options = options; this.updateTheme(); @@ -202,6 +217,10 @@ class Popup { // NOP } + if (this._depth === 0) { + apiForward('rootPopupInformation', {popupId: this._id, frameId: this._frameId}); + } + if (this._messageToken === null) { this._messageToken = await apiGetMessageToken(); } diff --git a/ext/manifest.json b/ext/manifest.json index 3cea8647..97d59e49 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -23,9 +23,11 @@ "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/popup-proxy.js", "fg/js/popup-proxy-host.js", "fg/js/frontend.js", "fg/js/frontend-initialize.js" From b6c4914b79d96b06760093be8957fbb1730eff3b Mon Sep 17 00:00:00 2001 From: siikamiika Date: Thu, 19 Mar 2020 00:24:14 +0200 Subject: [PATCH 02/24] simplify popup prepare --- ext/fg/js/popup-proxy-host.js | 10 +++++----- ext/fg/js/popup.js | 28 ++++------------------------ 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/ext/fg/js/popup-proxy-host.js b/ext/fg/js/popup-proxy-host.js index 6f1c13c6..4dc79943 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); } diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index e6596a1a..5b8724ae 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -23,11 +23,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; @@ -80,16 +79,8 @@ class Popup { return false; } - async broadcastRootPopupInformation() { + broadcastRootPopupInformation() { if (this._depth === 0) { - try { - const {frameId} = await this._frameIdPromise; - if (typeof frameId === 'number') { - this._frameId = frameId; - } - } catch (e) { - // NOP - } apiForward('rootPopupInformation', {popupId: this._id, frameId: this._frameId}); } } @@ -208,18 +199,7 @@ class Popup { } async _createInjectPromise() { - try { - const {frameId} = await this._frameIdPromise; - if (typeof frameId === 'number') { - this._frameId = frameId; - } - } catch (e) { - // NOP - } - - if (this._depth === 0) { - apiForward('rootPopupInformation', {popupId: this._id, frameId: this._frameId}); - } + this.broadcastRootPopupInformation(); if (this._messageToken === null) { this._messageToken = await apiGetMessageToken(); From 4814db8df12598c167b1e97096511f43e32c5e36 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Thu, 19 Mar 2020 02:55:31 +0200 Subject: [PATCH 03/24] adjust iframe popup position to root page --- ext/fg/js/popup-proxy-host.js | 33 ++++++++++++++++- ext/fg/js/popup-proxy.js | 69 ++++++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/ext/fg/js/popup-proxy-host.js b/ext/fg/js/popup-proxy-host.js index 4dc79943..487dda90 100644 --- a/ext/fg/js/popup-proxy-host.js +++ b/ext/fg/js/popup-proxy-host.js @@ -19,6 +19,7 @@ /* global * FrontendApiReceiver * Popup + * apiForward * apiFrameInformationGet */ @@ -48,6 +49,12 @@ class PopupProxyHost { ['clearAutoPlayTimer', this._onApiClearAutoPlayTimer.bind(this)], ['setContentScale', this._onApiSetContentScale.bind(this)] ])); + + this._windowMessageHandlers = new Map([ + ['getIframeOffset', ({offset, uniqueId}, e) => { return this._onGetIframeOffset(offset, uniqueId, e); }] + ]); + + window.addEventListener('message', this.onMessage.bind(this), false); } getOrCreatePopup(id=null, parentId=null, depth=null) { @@ -95,7 +102,7 @@ class PopupProxyHost { return popup; } - // Message handlers + // API message handlers async _onApiGetOrCreatePopup({id, parentId}) { const popup = this.getOrCreatePopup(id, parentId); @@ -152,6 +159,30 @@ class PopupProxyHost { return popup.setContentScale(scale); } + // Window message handlers + + onMessage(e) { + const {action, params} = e.data; + const handler = this._windowMessageHandlers.get(action); + if (typeof handler !== 'function') { return; } + handler(params, e); + } + + _onGetIframeOffset(offset, uniqueId, e) { + let sourceIframe = null; + for (const iframe of document.querySelectorAll('iframe:not(.yomichan-float)')) { + if (iframe.contentWindow !== e.source) { continue; } + sourceIframe = iframe; + break; + } + if (sourceIframe === null) { return; } + + const [forwardedX, forwardedY] = offset; + const {x, y} = sourceIframe.getBoundingClientRect(); + offset = [forwardedX + x, forwardedY + y]; + apiForward('iframeOffset', {offset, uniqueId}); + } + // Private functions _getPopup(id) { diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index 997b1317..c1ee76ce 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -29,6 +29,12 @@ class PopupProxy { this._depth = depth; this._url = url; this._apiSender = new FrontendApiSender(); + + this._windowMessageHandlers = new Map([ + ['getIframeOffset', ({offset, uniqueId}, e) => { return this._onGetIframeOffset(offset, uniqueId, e); }] + ]); + + window.addEventListener('message', this.onMessage.bind(this), false); } // Public properties @@ -83,12 +89,19 @@ class PopupProxy { if (this._id === null) { return false; } + if (this._depth === 0) { + [x, y] = await PopupProxy._convertIframePointToRootPagePoint(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); + let {x, y, width, height} = PopupProxy._convertDOMRectToJson(elementRect); + if (this._depth === 0) { + [x, y] = await PopupProxy._convertIframePointToRootPagePoint(x, y); + elementRect = {x, y, width, height}; + } return await this._invokeHostApi('showContent', {id, elementRect, writingMode, type, details}); } @@ -109,6 +122,31 @@ class PopupProxy { this._invokeHostApi('setContentScale', {id, scale}); } + // Window message handlers + + onMessage(e) { + const {action, params} = e.data; + const handler = this._windowMessageHandlers.get(action); + if (typeof handler !== 'function') { return; } + handler(params, e); + } + + _onGetIframeOffset(offset, uniqueId, e) { + let sourceIframe = null; + for (const iframe of document.querySelectorAll('iframe:not(.yomichan-float)')) { + if (iframe.contentWindow !== e.source) { continue; } + sourceIframe = iframe; + break; + } + if (sourceIframe === null) { return; } + + const [forwardedX, forwardedY] = offset; + const {x, y} = sourceIframe.getBoundingClientRect(); + offset = [forwardedX + x, forwardedY + y]; + window.parent.postMessage({action: 'getIframeOffset', params: {offset, uniqueId}}, '*'); + } + + // Private _getPopupId() { @@ -131,6 +169,35 @@ class PopupProxy { return this._apiSender.invoke(action, params, `popup-proxy-host#${this._parentFrameId}`); } + static async _convertIframePointToRootPagePoint(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 === 'iframeOffset' && isObject(params) && params.uniqueId === uniqueId) { + chrome.runtime.onMessage.removeListener(runtimeMessageCallback); + callback(); + frameOffsetResolve(params); + return false; + } + }; + chrome.runtime.onMessage.addListener(runtimeMessageCallback); + + window.parent.postMessage({ + action: 'getIframeOffset', + params: { + uniqueId, + offset: [x, y] + } + }, '*'); + + const {offset} = await frameOffsetPromise; + + return offset; + } + static _convertDOMRectToJson(domRect) { return { x: domRect.x, From b996d0b1e0838673001db0580430ed4ad6d102f2 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Thu, 19 Mar 2020 15:29:49 +0200 Subject: [PATCH 04/24] fix and simplify --- ext/fg/js/frontend.js | 1 + ext/fg/js/popup-proxy.js | 17 ++++++----------- ext/fg/js/popup.js | 2 -- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index af5341c4..d6fe7af4 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -77,6 +77,7 @@ class Frontend extends TextScanner { chrome.runtime.onMessage.addListener(this.onRuntimeMessage.bind(this)); this._updateContentScale(); + this.popup.broadcastRootPopupInformation(); } catch (e) { this.onError(e); } diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index c1ee76ce..242da04c 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -61,6 +61,10 @@ class PopupProxy { return true; } + broadcastRootPopupInformation() { + // NOP + } + async setOptions(options) { const id = await this._getPopupId(); return await this._invokeHostApi('setOptions', {id, options}); @@ -97,11 +101,11 @@ class PopupProxy { async showContent(elementRect, writingMode, type=null, details=null) { const id = await this._getPopupId(); - let {x, y, width, height} = PopupProxy._convertDOMRectToJson(elementRect); + let {x, y, width, height} = elementRect; if (this._depth === 0) { [x, y] = await PopupProxy._convertIframePointToRootPagePoint(x, y); - elementRect = {x, y, width, height}; } + elementRect = {x, y, width, height}; return await this._invokeHostApi('showContent', {id, elementRect, writingMode, type, details}); } @@ -197,13 +201,4 @@ class PopupProxy { return offset; } - - static _convertDOMRectToJson(domRect) { - return { - x: domRect.x, - y: domRect.y, - width: domRect.width, - height: domRect.height - }; - } } diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 5b8724ae..4c979911 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -199,8 +199,6 @@ class Popup { } async _createInjectPromise() { - this.broadcastRootPopupInformation(); - if (this._messageToken === null) { this._messageToken = await apiGetMessageToken(); } From 09151a1a860f35b8dcbdbe9a25c09be5abeb4e37 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Thu, 19 Mar 2020 15:55:12 +0200 Subject: [PATCH 05/24] simplify popup proxy prepare --- ext/fg/js/frontend-initialize.js | 2 ++ ext/fg/js/popup-proxy.js | 46 +++++++------------------------- 2 files changed, 12 insertions(+), 36 deletions(-) diff --git a/ext/fg/js/frontend-initialize.js b/ext/fg/js/frontend-initialize.js index 7f70d9c4..352c6bd9 100644 --- a/ext/fg/js/frontend-initialize.js +++ b/ext/fg/js/frontend-initialize.js @@ -48,8 +48,10 @@ async function main() { const {popupId, frameId} = await rootPopupInformationPromise; popup = new PopupProxy(popupId, 0, null, frameId, url); + await popup.prepare(); } else if (proxy) { popup = new PopupProxy(null, depth + 1, id, parentFrameId, url); + await popup.prepare(); } else { const popupHost = new PopupProxyHost(); await popupHost.prepare(); diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index 242da04c..8693ef17 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -25,7 +25,6 @@ class PopupProxy { this._parentId = parentId; this._parentFrameId = parentFrameId; this._id = id; - this._idPromise = null; this._depth = depth; this._url = url; this._apiSender = new FrontendApiSender(); @@ -57,6 +56,11 @@ class PopupProxy { // Public functions + async prepare() { + const {id} = await this._invokeHostApi('getOrCreatePopup', {id: this._id, parentId: this._parentId}); + this._id = id; + } + isProxy() { return true; } @@ -66,33 +70,22 @@ class PopupProxy { } 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._depth === 0) { [x, y] = await PopupProxy._convertIframePointToRootPagePoint(x, y); } @@ -100,30 +93,24 @@ class PopupProxy { } async showContent(elementRect, writingMode, type=null, details=null) { - const id = await this._getPopupId(); let {x, y, width, height} = elementRect; if (this._depth === 0) { [x, y] = await PopupProxy._convertIframePointToRootPagePoint(x, y); } elementRect = {x, y, width, height}; - return await this._invokeHostApi('showContent', {id, elementRect, writingMode, type, details}); + 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}); } // Window message handlers @@ -153,19 +140,6 @@ class PopupProxy { // Private - _getPopupId() { - if (this._idPromise === null) { - this._idPromise = this._getPopupIdAsync(); - } - return this._idPromise; - } - - async _getPopupIdAsync() { - const {id} = await this._invokeHostApi('getOrCreatePopup', {id: this._id, parentId: this._parentId}); - this._id = id; - return id; - } - _invokeHostApi(action, params={}) { if (typeof this._parentFrameId !== 'number') { return Promise.reject(new Error('Invalid frame')); From d20ece9f074bb9d241a902f29344e5906e3c8210 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Thu, 19 Mar 2020 17:46:05 +0200 Subject: [PATCH 06/24] move frame offset forwarding code to a class --- ext/bg/js/search-frontend.js | 1 + ext/fg/js/frame-offset-forwarder.js | 94 +++++++++++++++++++++++++++++ ext/fg/js/frontend-initialize.js | 6 +- ext/fg/js/popup-proxy-host.js | 31 ---------- ext/fg/js/popup-proxy.js | 67 ++------------------ ext/manifest.json | 1 + 6 files changed, 105 insertions(+), 95 deletions(-) create mode 100644 ext/fg/js/frame-offset-forwarder.js 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/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js new file mode 100644 index 00000000..b3715c2a --- /dev/null +++ b/ext/fg/js/frame-offset-forwarder.js @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2020 Alex Yatskov + * Author: Alex Yatskov + * + * 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 . + */ + +/* global + * apiForward + */ + +class FrameOffsetForwarder { + constructor() { + this._forwardFrameOffset = window !== window.parent ? + this._forwardFrameOffsetParent.bind(this) : + this._forwardFrameOffsetOrigin.bind(this); + + this._windowMessageHandlers = new Map([ + ['getFrameOffset', ({offset, uniqueId}, e) => { return this._onGetFrameOffset(offset, uniqueId, e); }] + ]); + + window.addEventListener('message', this.onMessage.bind(this), false); + } + + 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); + return false; + } + }; + chrome.runtime.onMessage.addListener(runtimeMessageCallback); + + window.parent.postMessage({ + action: 'getFrameOffset', + params: { + uniqueId, + offset: [x, y] + } + }, '*'); + + 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) { 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 352c6bd9..777291fe 100644 --- a/ext/fg/js/frontend-initialize.js +++ b/ext/fg/js/frontend-initialize.js @@ -17,6 +17,7 @@ */ /* global + * FrameOffsetForwarder * Frontend * PopupProxy * PopupProxyHost @@ -47,12 +48,15 @@ async function main() { const {popupId, frameId} = await rootPopupInformationPromise; - popup = new PopupProxy(popupId, 0, null, frameId, url); + window._frameOffsetForwarder = new FrameOffsetForwarder(); + const applyFrameOffset = window._frameOffsetForwarder.applyOffset.bind(window._frameOffsetForwarder); + popup = new PopupProxy(popupId, 0, null, frameId, url, applyFrameOffset); await popup.prepare(); } else if (proxy) { popup = new PopupProxy(null, depth + 1, id, parentFrameId, url); await popup.prepare(); } else { + window._frameOffsetForwarder = new FrameOffsetForwarder(); const popupHost = new PopupProxyHost(); await popupHost.prepare(); diff --git a/ext/fg/js/popup-proxy-host.js b/ext/fg/js/popup-proxy-host.js index 487dda90..4b136e41 100644 --- a/ext/fg/js/popup-proxy-host.js +++ b/ext/fg/js/popup-proxy-host.js @@ -19,7 +19,6 @@ /* global * FrontendApiReceiver * Popup - * apiForward * apiFrameInformationGet */ @@ -49,12 +48,6 @@ class PopupProxyHost { ['clearAutoPlayTimer', this._onApiClearAutoPlayTimer.bind(this)], ['setContentScale', this._onApiSetContentScale.bind(this)] ])); - - this._windowMessageHandlers = new Map([ - ['getIframeOffset', ({offset, uniqueId}, e) => { return this._onGetIframeOffset(offset, uniqueId, e); }] - ]); - - window.addEventListener('message', this.onMessage.bind(this), false); } getOrCreatePopup(id=null, parentId=null, depth=null) { @@ -159,30 +152,6 @@ class PopupProxyHost { return popup.setContentScale(scale); } - // Window message handlers - - onMessage(e) { - const {action, params} = e.data; - const handler = this._windowMessageHandlers.get(action); - if (typeof handler !== 'function') { return; } - handler(params, e); - } - - _onGetIframeOffset(offset, uniqueId, e) { - let sourceIframe = null; - for (const iframe of document.querySelectorAll('iframe:not(.yomichan-float)')) { - if (iframe.contentWindow !== e.source) { continue; } - sourceIframe = iframe; - break; - } - if (sourceIframe === null) { return; } - - const [forwardedX, forwardedY] = offset; - const {x, y} = sourceIframe.getBoundingClientRect(); - offset = [forwardedX + x, forwardedY + y]; - apiForward('iframeOffset', {offset, uniqueId}); - } - // Private functions _getPopup(id) { diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index 8693ef17..73148eee 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -21,19 +21,14 @@ */ class PopupProxy { - constructor(id, depth, parentId, parentFrameId, url) { + constructor(id, depth, parentId, parentFrameId, url, applyFrameOffset=async (x, y) => [x, y]) { this._parentId = parentId; this._parentFrameId = parentFrameId; this._id = id; this._depth = depth; this._url = url; this._apiSender = new FrontendApiSender(); - - this._windowMessageHandlers = new Map([ - ['getIframeOffset', ({offset, uniqueId}, e) => { return this._onGetIframeOffset(offset, uniqueId, e); }] - ]); - - window.addEventListener('message', this.onMessage.bind(this), false); + this._applyFrameOffset = applyFrameOffset; } // Public properties @@ -87,7 +82,7 @@ class PopupProxy { async containsPoint(x, y) { if (this._depth === 0) { - [x, y] = await PopupProxy._convertIframePointToRootPagePoint(x, y); + [x, y] = await this._applyFrameOffset(x, y); } return await this._invokeHostApi('containsPoint', {id: this._id, x, y}); } @@ -95,7 +90,7 @@ class PopupProxy { async showContent(elementRect, writingMode, type=null, details=null) { let {x, y, width, height} = elementRect; if (this._depth === 0) { - [x, y] = await PopupProxy._convertIframePointToRootPagePoint(x, y); + [x, y] = await this._applyFrameOffset(x, y); } elementRect = {x, y, width, height}; return await this._invokeHostApi('showContent', {id: this._id, elementRect, writingMode, type, details}); @@ -113,31 +108,6 @@ class PopupProxy { this._invokeHostApi('setContentScale', {id: this._id, scale}); } - // Window message handlers - - onMessage(e) { - const {action, params} = e.data; - const handler = this._windowMessageHandlers.get(action); - if (typeof handler !== 'function') { return; } - handler(params, e); - } - - _onGetIframeOffset(offset, uniqueId, e) { - let sourceIframe = null; - for (const iframe of document.querySelectorAll('iframe:not(.yomichan-float)')) { - if (iframe.contentWindow !== e.source) { continue; } - sourceIframe = iframe; - break; - } - if (sourceIframe === null) { return; } - - const [forwardedX, forwardedY] = offset; - const {x, y} = sourceIframe.getBoundingClientRect(); - offset = [forwardedX + x, forwardedY + y]; - window.parent.postMessage({action: 'getIframeOffset', params: {offset, uniqueId}}, '*'); - } - - // Private _invokeHostApi(action, params={}) { @@ -146,33 +116,4 @@ class PopupProxy { } return this._apiSender.invoke(action, params, `popup-proxy-host#${this._parentFrameId}`); } - - static async _convertIframePointToRootPagePoint(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 === 'iframeOffset' && isObject(params) && params.uniqueId === uniqueId) { - chrome.runtime.onMessage.removeListener(runtimeMessageCallback); - callback(); - frameOffsetResolve(params); - return false; - } - }; - chrome.runtime.onMessage.addListener(runtimeMessageCallback); - - window.parent.postMessage({ - action: 'getIframeOffset', - params: { - uniqueId, - offset: [x, y] - } - }, '*'); - - const {offset} = await frameOffsetPromise; - - return offset; - } } diff --git a/ext/manifest.json b/ext/manifest.json index 97d59e49..98965389 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -27,6 +27,7 @@ "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", From 9fe1e38afb056164dbcc4369160bb9fd4790a2c0 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 22 Mar 2020 03:29:09 +0200 Subject: [PATCH 07/24] refactor --- ext/fg/js/frame-offset-forwarder.js | 15 +++++++++++---- ext/fg/js/frontend-initialize.js | 13 ++++++++----- ext/fg/js/frontend.js | 2 +- ext/fg/js/popup-proxy.js | 6 +++--- ext/fg/js/popup.js | 1 - 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index b3715c2a..09eb89a6 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -22,15 +22,23 @@ class FrameOffsetForwarder { constructor() { - this._forwardFrameOffset = window !== window.parent ? + this._started = false; + + this._forwardFrameOffset = ( + window !== window.parent ? this._forwardFrameOffsetParent.bind(this) : - this._forwardFrameOffsetOrigin.bind(this); + this._forwardFrameOffsetOrigin.bind(this) + ); this._windowMessageHandlers = new Map([ - ['getFrameOffset', ({offset, uniqueId}, e) => { return this._onGetFrameOffset(offset, uniqueId, e); }] + ['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 applyOffset(x, y) { @@ -44,7 +52,6 @@ class FrameOffsetForwarder { chrome.runtime.onMessage.removeListener(runtimeMessageCallback); callback(); frameOffsetResolve(params); - return false; } }; chrome.runtime.onMessage.addListener(runtimeMessageCallback); diff --git a/ext/fg/js/frontend-initialize.js b/ext/fg/js/frontend-initialize.js index 777291fe..51fa8d7a 100644 --- a/ext/fg/js/frontend-initialize.js +++ b/ext/fg/js/frontend-initialize.js @@ -40,23 +40,26 @@ async function main() { chrome.runtime.onMessage.removeListener(runtimeMessageCallback); callback(); rootPopupInformationResolve(params); - return false; } }; chrome.runtime.onMessage.addListener(runtimeMessageCallback); - apiForward('rootPopupInformationGet'); + apiForward('rootPopupRequestInformationBroadcast'); const {popupId, frameId} = await rootPopupInformationPromise; - window._frameOffsetForwarder = new FrameOffsetForwarder(); - const applyFrameOffset = window._frameOffsetForwarder.applyOffset.bind(window._frameOffsetForwarder); + const frameOffsetForwarder = new FrameOffsetForwarder(); + frameOffsetForwarder.start(); + const applyFrameOffset = frameOffsetForwarder.applyOffset.bind(frameOffsetForwarder); + popup = new PopupProxy(popupId, 0, null, frameId, url, applyFrameOffset); await popup.prepare(); } else if (proxy) { popup = new PopupProxy(null, depth + 1, id, parentFrameId, url); await popup.prepare(); } else { - window._frameOffsetForwarder = new FrameOffsetForwarder(); + 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 d6fe7af4..c160b9e3 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -53,7 +53,7 @@ class Frontend extends TextScanner { this._runtimeMessageHandlers = new Map([ ['popupSetVisibleOverride', ({visible}) => { this.popup.setVisibleOverride(visible); }], - ['rootPopupInformationGet', () => { this.popup.broadcastRootPopupInformation(); }] + ['rootPopupRequestInformationBroadcast', () => { this.popup.broadcastRootPopupInformation(); }] ]); } diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index 73148eee..a25f9183 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -21,7 +21,7 @@ */ class PopupProxy { - constructor(id, depth, parentId, parentFrameId, url, applyFrameOffset=async (x, y) => [x, y]) { + constructor(id, depth, parentId, parentFrameId, url, applyFrameOffset=null) { this._parentId = parentId; this._parentFrameId = parentFrameId; this._id = id; @@ -81,7 +81,7 @@ class PopupProxy { } async containsPoint(x, y) { - if (this._depth === 0) { + if (this._applyFrameOffset !== null) { [x, y] = await this._applyFrameOffset(x, y); } return await this._invokeHostApi('containsPoint', {id: this._id, x, y}); @@ -89,7 +89,7 @@ class PopupProxy { async showContent(elementRect, writingMode, type=null, details=null) { let {x, y, width, height} = elementRect; - if (this._depth === 0) { + if (this._applyFrameOffset !== null) { [x, y] = await this._applyFrameOffset(x, y); } elementRect = {x, y, width, height}; diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 4c979911..47e32963 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -368,7 +368,6 @@ class Popup { chrome.runtime.onMessage.removeListener(runtimeMessageCallback); callback(); resolve(); - return false; } }; chrome.runtime.onMessage.addListener(runtimeMessageCallback); From d88635cbb28862e0efd79080e5c67865d0410238 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 22 Mar 2020 04:55:16 +0200 Subject: [PATCH 08/24] temporary listener abstraction --- ext/fg/js/frame-offset-forwarder.js | 17 ++++++-------- ext/fg/js/frontend-initialize.js | 18 ++++++--------- ext/mixed/js/core.js | 36 +++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 21 deletions(-) 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) { From fa65ab351293fa6af882479e0fad6aaca3152569 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 22 Mar 2020 12:45:58 +0200 Subject: [PATCH 09/24] refactor getTemporaryListenerResult timeout --- ext/fg/js/frame-offset-forwarder.js | 3 ++- ext/mixed/js/core.js | 23 +++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index 781a9018..38fc0b03 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -50,7 +50,8 @@ class FrameOffsetForwarder { if (action === 'frameOffset' && isObject(params) && params.uniqueId === uniqueId) { resolve(params); } - } + }, + 5000 ); window.parent.postMessage({ diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index f21bac23..616f48b8 100644 --- a/ext/mixed/js/core.js +++ b/ext/mixed/js/core.js @@ -312,8 +312,7 @@ const yomichan = (() => { this.trigger('orphaned', {error}); } - getTemporaryListenerResult(eventHandler, userCallback, timeout=30000) { - let resolved = false; + getTemporaryListenerResult(eventHandler, userCallback, timeout=null) { let resolve; let reject; const listenerPromise = new Promise((_resolve, _reject) => { @@ -323,20 +322,24 @@ const yomichan = (() => { if (eventHandler === chrome.runtime.onMessage) { const runtimeMessageCallback = ({action, params}, sender, sendResponse) => { + let timeoutId = null; + if (timeout !== null) { + timeoutId = window.setTimeout(() => { + timeoutId = null; + reject(new Error(`Listener timed out in ${timeout} ms`)); + eventHandler.removeListener(runtimeMessageCallback); + }, timeout); + } + const cleanupResolve = (value) => { - resolved = true; + if (timeoutId !== null) { + window.clearTimeout(timeoutId); + } 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}); }; From 9f9ea1d5fb9f383992523a2a9a8a02259ced838e Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 22 Mar 2020 14:11:43 +0200 Subject: [PATCH 10/24] throttle frame offset getting --- ext/fg/js/frame-offset-forwarder.js | 4 +-- ext/fg/js/frontend-initialize.js | 4 +-- ext/fg/js/popup-proxy.js | 46 +++++++++++++++++++++++++---- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index 38fc0b03..c32c2ec1 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -41,7 +41,7 @@ class FrameOffsetForwarder { this._started = true; } - async applyOffset(x, y) { + async getOffset() { const uniqueId = yomichan.generateId(16); const frameOffsetPromise = yomichan.getTemporaryListenerResult( @@ -58,7 +58,7 @@ class FrameOffsetForwarder { action: 'getFrameOffset', params: { uniqueId, - offset: [x, y] + offset: [0, 0] } }, '*'); diff --git a/ext/fg/js/frontend-initialize.js b/ext/fg/js/frontend-initialize.js index 97e315b5..2f86f5c8 100644 --- a/ext/fg/js/frontend-initialize.js +++ b/ext/fg/js/frontend-initialize.js @@ -45,9 +45,9 @@ async function main() { const frameOffsetForwarder = new FrameOffsetForwarder(); frameOffsetForwarder.start(); - const applyFrameOffset = frameOffsetForwarder.applyOffset.bind(frameOffsetForwarder); + const getFrameOffset = frameOffsetForwarder.getOffset.bind(frameOffsetForwarder); - popup = new PopupProxy(popupId, 0, null, frameId, url, applyFrameOffset); + popup = new PopupProxy(popupId, 0, null, frameId, url, getFrameOffset); await popup.prepare(); } else if (proxy) { popup = new PopupProxy(null, depth + 1, id, parentFrameId, url); diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index a25f9183..0cd5bbad 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, applyFrameOffset=null) { + constructor(id, depth, parentId, parentFrameId, url, getFrameOffset=null) { this._parentId = parentId; this._parentFrameId = parentFrameId; this._id = id; this._depth = depth; this._url = url; this._apiSender = new FrontendApiSender(); - this._applyFrameOffset = applyFrameOffset; + this._getFrameOffset = getFrameOffset; + + this._frameOffset = null; + this._frameOffsetPromise = null; + this._frameOffsetUpdatedAt = null; } // Public properties @@ -81,16 +85,18 @@ class PopupProxy { } async containsPoint(x, y) { - if (this._applyFrameOffset !== null) { - [x, y] = await this._applyFrameOffset(x, y); + 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) { let {x, y, width, height} = elementRect; - if (this._applyFrameOffset !== null) { - [x, y] = await this._applyFrameOffset(x, y); + 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}); @@ -116,4 +122,32 @@ class PopupProxy { } return this._apiSender.invoke(action, params, `popup-proxy-host#${this._parentFrameId}`); } + + async _updateFrameOffset() { + const firstRun = this._frameOffsetUpdatedAt === null; + const expired = firstRun || this._frameOffsetUpdatedAt < Date.now() - 1000; + if (this._frameOffsetPromise === null && !expired) { return; } + + if (this._frameOffsetPromise !== null) { + await this._frameOffsetPromise; + return; + } + + if (firstRun) { + this._frameOffsetPromise = this._getFrameOffset(); + this._frameOffset = await this._frameOffsetPromise; + this._frameOffsetPromise = null; + this._frameOffsetUpdatedAt = Date.now(); + } else { + this._getFrameOffset().then((offset) => { + this._frameOffset = offset; + this._frameOffsetUpdatedAt = Date.now(); + }); + } + } + + _applyFrameOffset(x, y) { + const [offsetX, offsetY] = this._frameOffset; + return [x + offsetX, y + offsetY]; + } } From 7928c5d71324b3eebece7e2a26e9783e139066d5 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 22 Mar 2020 14:39:07 +0200 Subject: [PATCH 11/24] error handling --- ext/fg/js/popup-proxy.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index 0cd5bbad..fd07ee46 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -129,19 +129,29 @@ class PopupProxy { if (this._frameOffsetPromise === null && !expired) { return; } if (this._frameOffsetPromise !== null) { - await this._frameOffsetPromise; + if (firstRun) { + await this._frameOffsetPromise; + } return; } + this._frameOffsetPromise = this._getFrameOffset(); if (firstRun) { - this._frameOffsetPromise = this._getFrameOffset(); - this._frameOffset = await this._frameOffsetPromise; + try { + this._frameOffset = await this._frameOffsetPromise; + this._frameOffsetUpdatedAt = Date.now(); + } catch (e) { + console.error(e); + } this._frameOffsetPromise = null; - this._frameOffsetUpdatedAt = Date.now(); } else { - this._getFrameOffset().then((offset) => { + this._frameOffsetPromise.then((offset) => { this._frameOffset = offset; this._frameOffsetUpdatedAt = Date.now(); + this._frameOffsetPromise = null; + }, (e) => { + console.error(e); + this._frameOffsetPromise = null; }); } } From c171503d77bbae21ddfa2eb49c6a571440f41763 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Wed, 25 Mar 2020 04:17:17 +0200 Subject: [PATCH 12/24] use getTemporaryListenerResult in other places --- ext/fg/js/popup.js | 62 +++++++++++++++++++++----------------------- ext/mixed/js/core.js | 15 ++++++----- 2 files changed, 37 insertions(+), 40 deletions(-) diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 47e32963..fb3e56b4 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -203,27 +203,38 @@ class Popup { 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); + const popupPreparedPromise = yomichan.getTemporaryListenerResult( + chrome.runtime.onMessage, + ({action, params}, {resolve}) => { + if ( + action === 'popupPrepareCompleted' && + isObject(params) && + params.targetPopupId === this._id + ) { + resolve(); + } + } + ); - this._invokeApi('prepare', { - popupInfo: { - id: this._id, - depth: this._depth, - parentFrameId - }, - url: this.url, - childrenSupported: this._childrenSupported, - scale: this._contentScale - }); + 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() { @@ -358,21 +369,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(); - } - }; - chrome.runtime.onMessage.addListener(runtimeMessageCallback); - } - static _getFullscreenElement() { return ( document.fullscreenElement || diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index 616f48b8..ca37a26a 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)] @@ -362,10 +367,6 @@ const yomichan = (() => { return false; } - _onBackendPrepared() { - this._isBackendPreparedResolve(); - } - _onMessageGetUrl() { return {url: window.location.href}; } From 559033f0a6c2c8c298bd305065e475f0b385cd17 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 4 Apr 2020 16:48:35 +0300 Subject: [PATCH 13/24] default to [0, 0] when sourceFrame is not found --- ext/fg/js/frame-offset-forwarder.js | 5 ++++- ext/fg/js/popup-proxy.js | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index c32c2ec1..7b417b6e 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -80,7 +80,10 @@ class FrameOffsetForwarder { sourceFrame = frame; break; } - if (sourceFrame === null) { return; } + if (sourceFrame === null) { + this._forwardFrameOffsetOrigin(null, uniqueId); + return; + } const [forwardedX, forwardedY] = offset; const {x, y} = sourceFrame.getBoundingClientRect(); diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index fd07ee46..67b0b2c6 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -138,7 +138,8 @@ class PopupProxy { this._frameOffsetPromise = this._getFrameOffset(); if (firstRun) { try { - this._frameOffset = await this._frameOffsetPromise; + const offset = await this._frameOffsetPromise; + this._frameOffset = offset !== null ? offset : [0, 0]; this._frameOffsetUpdatedAt = Date.now(); } catch (e) { console.error(e); @@ -146,7 +147,7 @@ class PopupProxy { this._frameOffsetPromise = null; } else { this._frameOffsetPromise.then((offset) => { - this._frameOffset = offset; + this._frameOffset = offset !== null ? offset : [0, 0]; this._frameOffsetUpdatedAt = Date.now(); this._frameOffsetPromise = null; }, (e) => { From 067c26982e4b374fbb51fa49d9930ddf0c8e00fd Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 4 Apr 2020 16:53:01 +0300 Subject: [PATCH 14/24] setTimeout refactoring --- ext/mixed/js/core.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index ca37a26a..4314926c 100644 --- a/ext/mixed/js/core.js +++ b/ext/mixed/js/core.js @@ -331,14 +331,15 @@ const yomichan = (() => { if (timeout !== null) { timeoutId = window.setTimeout(() => { timeoutId = null; - reject(new Error(`Listener timed out in ${timeout} ms`)); 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(); From 123e725049e5c6b5c3b7b53ee37aaf93e7bb0c61 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 4 Apr 2020 16:59:17 +0300 Subject: [PATCH 15/24] use duck typing for event listener --- ext/mixed/js/core.js | 55 +++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index 4314926c..7fb3c32e 100644 --- a/ext/mixed/js/core.js +++ b/ext/mixed/js/core.js @@ -325,35 +325,38 @@ const yomichan = (() => { reject = _reject; }); - if (eventHandler === chrome.runtime.onMessage) { - 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); - } else { + if ( + typeof eventHandler.addListener === 'undefined' || + typeof eventHandler.removeListener === 'undefined' + ) { throw new Error('Event handler type not supported'); } + 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); + return listenerPromise; } From 867902523f43929393500574076f793c4f62c225 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 4 Apr 2020 17:02:54 +0300 Subject: [PATCH 16/24] simplify promise --- ext/mixed/js/core.js | 53 +++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index 7fb3c32e..5f9e172d 100644 --- a/ext/mixed/js/core.js +++ b/ext/mixed/js/core.js @@ -318,13 +318,6 @@ const yomichan = (() => { } getTemporaryListenerResult(eventHandler, userCallback, timeout=null) { - let resolve; - let reject; - const listenerPromise = new Promise((_resolve, _reject) => { - resolve = _resolve; - reject = _reject; - }); - if ( typeof eventHandler.addListener === 'undefined' || typeof eventHandler.removeListener === 'undefined' @@ -332,32 +325,32 @@ const yomichan = (() => { throw new Error('Event handler type not supported'); } - 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; + 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); } - eventHandler.removeListener(runtimeMessageCallback); - sendResponse(); - resolve(value); + + const cleanupResolve = (value) => { + if (timeoutId !== null) { + window.clearTimeout(timeoutId); + timeoutId = null; + } + eventHandler.removeListener(runtimeMessageCallback); + sendResponse(); + resolve(value); + }; + + userCallback({action, params}, {resolve: cleanupResolve, sender}); }; - userCallback({action, params}, {resolve: cleanupResolve, sender}); - }; - - eventHandler.addListener(runtimeMessageCallback); - - return listenerPromise; + eventHandler.addListener(runtimeMessageCallback); + }); } // Private From 668be6224b5db22e6c55afbb4bc676b7d1a149d9 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 4 Apr 2020 17:06:06 +0300 Subject: [PATCH 17/24] add fake chrome.runtime.onMessage.removeListener --- test/test-database.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test-database.js b/test/test-database.js index c4bfb793..bab15aa4 100644 --- a/test/test-database.js +++ b/test/test-database.js @@ -27,7 +27,8 @@ require('fake-indexeddb/auto'); const chrome = { runtime: { onMessage: { - addListener() { /* NOP */ } + addListener() { /* NOP */ }, + removeListener() { /* NOP */ } }, getURL(path2) { return url.pathToFileURL(path.join(__dirname, '..', 'ext', path2.replace(/^\//, ''))); From e4fc53480f36b56abba0ab1cb20f6aad7bf4a004 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 4 Apr 2020 17:32:58 +0300 Subject: [PATCH 18/24] move broadcastRootPopupInformation to Frontend --- ext/fg/js/frontend.js | 11 +++++++++-- ext/fg/js/popup-proxy.js | 4 ---- ext/fg/js/popup.js | 11 ++++------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index c160b9e3..4e9d474c 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -18,6 +18,7 @@ /* global * TextScanner + * apiForward * apiGetZoom * apiKanjiFind * apiOptionsGet @@ -53,7 +54,7 @@ class Frontend extends TextScanner { this._runtimeMessageHandlers = new Map([ ['popupSetVisibleOverride', ({visible}) => { this.popup.setVisibleOverride(visible); }], - ['rootPopupRequestInformationBroadcast', () => { this.popup.broadcastRootPopupInformation(); }] + ['rootPopupRequestInformationBroadcast', () => { this._broadcastRootPopupInformation(); }] ]); } @@ -77,7 +78,7 @@ class Frontend extends TextScanner { chrome.runtime.onMessage.addListener(this.onRuntimeMessage.bind(this)); this._updateContentScale(); - this.popup.broadcastRootPopupInformation(); + this._broadcastRootPopupInformation(); } catch (e) { this.onError(e); } @@ -257,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.js b/ext/fg/js/popup-proxy.js index 67b0b2c6..cb264af2 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -64,10 +64,6 @@ class PopupProxy { return true; } - broadcastRootPopupInformation() { - // NOP - } - async setOptions(options) { return await this._invokeHostApi('setOptions', {id: this._id, options}); } diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index fb3e56b4..60dc16dd 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -17,7 +17,6 @@ */ /* global - * apiForward * apiGetMessageToken * apiInjectStylesheet */ @@ -69,6 +68,10 @@ class Popup { return this._depth; } + get frameId() { + return this._frameId; + } + get url() { return window.location.href; } @@ -79,12 +82,6 @@ class Popup { return false; } - broadcastRootPopupInformation() { - if (this._depth === 0) { - apiForward('rootPopupInformation', {popupId: this._id, frameId: this._frameId}); - } - } - async setOptions(options) { this._options = options; this.updateTheme(); From 0d76cef43437fda336a3314dc62db1388537042f Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 4 Apr 2020 17:39:26 +0300 Subject: [PATCH 19/24] give a name to a timeout --- ext/fg/js/popup-proxy.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index cb264af2..7405b552 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -121,7 +121,7 @@ class PopupProxy { async _updateFrameOffset() { const firstRun = this._frameOffsetUpdatedAt === null; - const expired = firstRun || this._frameOffsetUpdatedAt < Date.now() - 1000; + const expired = firstRun || this._frameOffsetUpdatedAt < Date.now() - PopupProxy._frameOffsetExpireTimeout; if (this._frameOffsetPromise === null && !expired) { return; } if (this._frameOffsetPromise !== null) { @@ -158,3 +158,5 @@ class PopupProxy { return [x + offsetX, y + offsetY]; } } + +PopupProxy._frameOffsetExpireTimeout = 1000; From 31a326fe636683e71fa61f11ed25b4f2adaead44 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 5 Apr 2020 01:43:12 +0300 Subject: [PATCH 20/24] add option for iframe popups --- ext/bg/data/options-schema.json | 7 ++++++- ext/bg/js/options.js | 3 ++- ext/bg/js/settings/main.js | 2 ++ ext/bg/settings.html | 4 ++++ ext/fg/js/frontend-initialize.js | 6 +++++- 5 files changed, 19 insertions(+), 3 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/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 @@ -174,6 +174,10 @@ +
+ +
+
diff --git a/ext/fg/js/frontend-initialize.js b/ext/fg/js/frontend-initialize.js index 2f86f5c8..4a1409db 100644 --- a/ext/fg/js/frontend-initialize.js +++ b/ext/fg/js/frontend-initialize.js @@ -22,6 +22,7 @@ * PopupProxy * PopupProxyHost * apiForward + * apiOptionsGet */ async function main() { @@ -30,8 +31,11 @@ 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 && (window !== window.parent)) { + if (!proxy && (window !== window.parent) && options.general.showIframePopupsInRootFrame) { const rootPopupInformationPromise = yomichan.getTemporaryListenerResult( chrome.runtime.onMessage, ({action, params}, {resolve}) => { From abd056e5637be5cca3033296843dd442593c73e7 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 5 Apr 2020 15:32:57 +0300 Subject: [PATCH 21/24] reduce code duplication --- ext/fg/js/popup-proxy.js | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index 7405b552..f1618c9f 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -132,24 +132,26 @@ class PopupProxy { } this._frameOffsetPromise = this._getFrameOffset(); + + const handleOffset = (offset) => { + this._frameOffset = offset !== null ? offset : [0, 0]; + this._frameOffsetUpdatedAt = Date.now(); + this._frameOffsetPromise = null; + }; + + const handleError = (e) => { + console.error(e); + this._frameOffsetPromise = null; + }; + if (firstRun) { try { - const offset = await this._frameOffsetPromise; - this._frameOffset = offset !== null ? offset : [0, 0]; - this._frameOffsetUpdatedAt = Date.now(); + handleOffset(await this._frameOffsetPromise); } catch (e) { - console.error(e); + handleError(e); } - this._frameOffsetPromise = null; } else { - this._frameOffsetPromise.then((offset) => { - this._frameOffset = offset !== null ? offset : [0, 0]; - this._frameOffsetUpdatedAt = Date.now(); - this._frameOffsetPromise = null; - }, (e) => { - console.error(e); - this._frameOffsetPromise = null; - }); + this._frameOffsetPromise.then(handleOffset, handleError); } } From aea7c590d1b381786978cd7871aed545be96ce34 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 5 Apr 2020 19:21:06 +0300 Subject: [PATCH 22/24] refactor _updateFrameOffset --- ext/fg/js/popup-proxy.js | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index f1618c9f..966198a9 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -120,8 +120,9 @@ class PopupProxy { } async _updateFrameOffset() { + const now = Date.now(); const firstRun = this._frameOffsetUpdatedAt === null; - const expired = firstRun || this._frameOffsetUpdatedAt < Date.now() - PopupProxy._frameOffsetExpireTimeout; + const expired = firstRun || this._frameOffsetUpdatedAt < now - PopupProxy._frameOffsetExpireTimeout; if (this._frameOffsetPromise === null && !expired) { return; } if (this._frameOffsetPromise !== null) { @@ -131,27 +132,22 @@ class PopupProxy { return; } - this._frameOffsetPromise = this._getFrameOffset(); - - const handleOffset = (offset) => { - this._frameOffset = offset !== null ? offset : [0, 0]; - this._frameOffsetUpdatedAt = Date.now(); - this._frameOffsetPromise = null; - }; - - const handleError = (e) => { - console.error(e); - this._frameOffsetPromise = null; - }; - + const promise = this._updateFrameOffsetInner(now); if (firstRun) { - try { - handleOffset(await this._frameOffsetPromise); - } catch (e) { - handleError(e); - } - } else { - this._frameOffsetPromise.then(handleOffset, handleError); + await promise; + } + } + + 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; } } From 5596a8f6f7e23f6dfe45dc83bf077b0b688b84fb Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 5 Apr 2020 19:24:13 +0300 Subject: [PATCH 23/24] strict object property type check --- ext/mixed/js/core.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index 5f9e172d..6d0a5f0e 100644 --- a/ext/mixed/js/core.js +++ b/ext/mixed/js/core.js @@ -318,10 +318,10 @@ const yomichan = (() => { } getTemporaryListenerResult(eventHandler, userCallback, timeout=null) { - if ( - typeof eventHandler.addListener === 'undefined' || - typeof eventHandler.removeListener === 'undefined' - ) { + if (!( + typeof eventHandler.addListener === 'function' || + typeof eventHandler.removeListener === 'function' + )) { throw new Error('Event handler type not supported'); } From 22a97d916fc6ecab1200b0ffea18cf2d5c9923d4 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 5 Apr 2020 20:42:23 +0300 Subject: [PATCH 24/24] fix NOT (a OR B) to NOT (a AND b) --- ext/mixed/js/core.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index 6d0a5f0e..db7fc69b 100644 --- a/ext/mixed/js/core.js +++ b/ext/mixed/js/core.js @@ -319,7 +319,7 @@ const yomichan = (() => { getTemporaryListenerResult(eventHandler, userCallback, timeout=null) { if (!( - typeof eventHandler.addListener === 'function' || + typeof eventHandler.addListener === 'function' && typeof eventHandler.removeListener === 'function' )) { throw new Error('Event handler type not supported');