move frame offset forwarding code to a class

This commit is contained in:
siikamiika 2020-03-19 17:46:05 +02:00
parent 09151a1a86
commit d20ece9f07
6 changed files with 105 additions and 95 deletions

View File

@ -35,6 +35,7 @@ async function searchFrontendSetup() {
const scriptSrcs = [ const scriptSrcs = [
'/mixed/js/text-scanner.js', '/mixed/js/text-scanner.js',
'/fg/js/frontend-api-receiver.js', '/fg/js/frontend-api-receiver.js',
'/fg/js/frame-offset-forwarder.js',
'/fg/js/popup.js', '/fg/js/popup.js',
'/fg/js/popup-proxy-host.js', '/fg/js/popup-proxy-host.js',
'/fg/js/frontend.js', '/fg/js/frontend.js',

View File

@ -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});
}
}

View File

@ -17,6 +17,7 @@
*/ */
/* global /* global
* FrameOffsetForwarder
* Frontend * Frontend
* PopupProxy * PopupProxy
* PopupProxyHost * PopupProxyHost
@ -47,12 +48,15 @@ async function main() {
const {popupId, frameId} = await rootPopupInformationPromise; 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(); await popup.prepare();
} else if (proxy) { } else if (proxy) {
popup = new PopupProxy(null, depth + 1, id, parentFrameId, url); popup = new PopupProxy(null, depth + 1, id, parentFrameId, url);
await popup.prepare(); await popup.prepare();
} else { } else {
window._frameOffsetForwarder = new FrameOffsetForwarder();
const popupHost = new PopupProxyHost(); const popupHost = new PopupProxyHost();
await popupHost.prepare(); await popupHost.prepare();

View File

@ -19,7 +19,6 @@
/* global /* global
* FrontendApiReceiver * FrontendApiReceiver
* Popup * Popup
* apiForward
* apiFrameInformationGet * apiFrameInformationGet
*/ */
@ -49,12 +48,6 @@ class PopupProxyHost {
['clearAutoPlayTimer', this._onApiClearAutoPlayTimer.bind(this)], ['clearAutoPlayTimer', this._onApiClearAutoPlayTimer.bind(this)],
['setContentScale', this._onApiSetContentScale.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) { getOrCreatePopup(id=null, parentId=null, depth=null) {
@ -159,30 +152,6 @@ class PopupProxyHost {
return popup.setContentScale(scale); 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 // Private functions
_getPopup(id) { _getPopup(id) {

View File

@ -21,19 +21,14 @@
*/ */
class PopupProxy { class PopupProxy {
constructor(id, depth, parentId, parentFrameId, url) { constructor(id, depth, parentId, parentFrameId, url, applyFrameOffset=async (x, y) => [x, y]) {
this._parentId = parentId; this._parentId = parentId;
this._parentFrameId = parentFrameId; this._parentFrameId = parentFrameId;
this._id = id; this._id = id;
this._depth = depth; this._depth = depth;
this._url = url; this._url = url;
this._apiSender = new FrontendApiSender(); this._apiSender = new FrontendApiSender();
this._applyFrameOffset = applyFrameOffset;
this._windowMessageHandlers = new Map([
['getIframeOffset', ({offset, uniqueId}, e) => { return this._onGetIframeOffset(offset, uniqueId, e); }]
]);
window.addEventListener('message', this.onMessage.bind(this), false);
} }
// Public properties // Public properties
@ -87,7 +82,7 @@ class PopupProxy {
async containsPoint(x, y) { async containsPoint(x, y) {
if (this._depth === 0) { 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}); return await this._invokeHostApi('containsPoint', {id: this._id, x, y});
} }
@ -95,7 +90,7 @@ class PopupProxy {
async showContent(elementRect, writingMode, type=null, details=null) { async showContent(elementRect, writingMode, type=null, details=null) {
let {x, y, width, height} = elementRect; let {x, y, width, height} = elementRect;
if (this._depth === 0) { if (this._depth === 0) {
[x, y] = await PopupProxy._convertIframePointToRootPagePoint(x, y); [x, y] = await this._applyFrameOffset(x, y);
} }
elementRect = {x, y, width, height}; elementRect = {x, y, width, height};
return await this._invokeHostApi('showContent', {id: this._id, elementRect, writingMode, type, details}); 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}); 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 // Private
_invokeHostApi(action, params={}) { _invokeHostApi(action, params={}) {
@ -146,33 +116,4 @@ class PopupProxy {
} }
return this._apiSender.invoke(action, params, `popup-proxy-host#${this._parentFrameId}`); 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;
}
} }

View File

@ -27,6 +27,7 @@
"fg/js/frontend-api-receiver.js", "fg/js/frontend-api-receiver.js",
"fg/js/popup.js", "fg/js/popup.js",
"fg/js/source.js", "fg/js/source.js",
"fg/js/frame-offset-forwarder.js",
"fg/js/popup-proxy.js", "fg/js/popup-proxy.js",
"fg/js/popup-proxy-host.js", "fg/js/popup-proxy-host.js",
"fg/js/frontend.js", "fg/js/frontend.js",