diff options
Diffstat (limited to 'ext/fg/js/popup-proxy.js')
-rw-r--r-- | ext/fg/js/popup-proxy.js | 69 |
1 files changed, 68 insertions, 1 deletions
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, |