summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/bg/js/search-frontend.js1
-rw-r--r--ext/fg/js/frame-offset-forwarder.js94
-rw-r--r--ext/fg/js/frontend-initialize.js6
-rw-r--r--ext/fg/js/popup-proxy-host.js31
-rw-r--r--ext/fg/js/popup-proxy.js67
-rw-r--r--ext/manifest.json1
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",