From d20ece9f074bb9d241a902f29344e5906e3c8210 Mon Sep 17 00:00:00 2001
From: siikamiika <siikamiika@users.noreply.github.com>
Date: Thu, 19 Mar 2020 17:46:05 +0200
Subject: move frame offset forwarding code to a class

---
 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 ++------------------------
 4 files changed, 103 insertions(+), 95 deletions(-)
 create mode 100644 ext/fg/js/frame-offset-forwarder.js

(limited to 'ext/fg/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;
-    }
 }
-- 
cgit v1.2.3