diff options
-rw-r--r-- | ext/bg/js/search-frontend.js | 1 | ||||
-rw-r--r-- | ext/fg/js/frame-offset-forwarder.js | 94 | ||||
-rw-r--r-- | ext/fg/js/frontend-initialize.js | 6 | ||||
-rw-r--r-- | ext/fg/js/popup-proxy-host.js | 31 | ||||
-rw-r--r-- | ext/fg/js/popup-proxy.js | 67 | ||||
-rw-r--r-- | ext/manifest.json | 1 |
6 files changed, 105 insertions, 95 deletions
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 <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._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", |