diff options
author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2021-02-13 23:13:53 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-13 23:13:53 -0500 |
commit | 7a74c3c31ece7788e82c46f22cb4327ffe08307a (patch) | |
tree | 7d4aee53b1dab15bdf317729ee1559291c04a4b2 /ext/fg/js/frame-ancestry-handler.js | |
parent | 6a271e067fa917614f4c81f473533e24c6d04404 (diff) |
Move fg/js (#1384)
* Move fg/js/frame-ancestry-handler.js to js/comm/frame-ancestry-handler.js
* Move fg/js/frame-offset-forwarder.js to js/comm/frame-offset-forwarder.js
* Move fg/js/dom-text-scanner.js to js/dom/dom-text-scanner.js
* Move fg/js/text-source-element.js to js/dom/text-source-element.js
* Move fg/js/text-source-range.js to js/dom/text-source-range.js
* Move fg/js/float-main.js to js/display/popup-main.js
* Move fg/js/content-script-main.js to js/app/content-script-main.js
* Move fg/js/frontend.js to js/app/frontend.js
* Move fg/js/popup-factory.js to js/app/popup-factory.js
* Move fg/js/popup-proxy.js to js/app/popup-proxy.js
* Move fg/js/popup-window.js to js/app/popup-window.js
* Move fg/js/popup.js to js/app/popup.js
Diffstat (limited to 'ext/fg/js/frame-ancestry-handler.js')
-rw-r--r-- | ext/fg/js/frame-ancestry-handler.js | 269 |
1 files changed, 0 insertions, 269 deletions
diff --git a/ext/fg/js/frame-ancestry-handler.js b/ext/fg/js/frame-ancestry-handler.js deleted file mode 100644 index b1ed7114..00000000 --- a/ext/fg/js/frame-ancestry-handler.js +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (C) 2021 Yomichan Authors - * - * 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 - * api - */ - -/** - * This class is used to return the ancestor frame IDs for the current frame. - * This is a workaround to using the `webNavigation.getAllFrames` API, which - * would require an additional permission that is otherwise unnecessary. - * It is also used to track the correlation between child frame elements and their IDs. - */ -class FrameAncestryHandler { - /** - * Creates a new instance. - * @param frameId The frame ID of the current frame the instance is instantiated in. - */ - constructor(frameId) { - this._frameId = frameId; - this._isPrepared = false; - this._requestMessageId = 'FrameAncestryHandler.requestFrameInfo'; - this._responseMessageIdBase = `${this._requestMessageId}.response.`; - this._getFrameAncestryInfoPromise = null; - this._childFrameMap = new Map(); - } - - /** - * Gets the frame ID that the instance is instantiated in. - */ - get frameId() { - return this._frameId; - } - - /** - * Initializes event event listening. - */ - prepare() { - if (this._isPrepared) { return; } - window.addEventListener('message', this._onWindowMessage.bind(this), false); - this._isPrepared = true; - } - - /** - * Returns whether or not this frame is the root frame in the tab. - * @returns `true` if it is the root, otherwise `false`. - */ - isRootFrame() { - return (window === window.parent); - } - - /** - * Gets the frame ancestry information for the current frame. If the frame is the - * root frame, an empty array is returned. Otherwise, an array of frame IDs is returned, - * starting from the nearest ancestor. - * @param timeout The maximum time to wait to receive a response to frame information requests. - * @returns An array of frame IDs corresponding to the ancestors of the current frame. - */ - async getFrameAncestryInfo() { - if (this._getFrameAncestryInfoPromise === null) { - this._getFrameAncestryInfoPromise = this._getFrameAncestryInfo(5000); - } - return await this._getFrameAncestryInfoPromise; - } - - /** - * Gets the frame element of a child frame given a frame ID. - * For this function to work, the `getFrameAncestryInfo` function needs to have - * been invoked previously. - * @param frameId The frame ID of the child frame to get. - * @returns The element corresponding to the frame with ID `frameId`, otherwise `null`. - */ - getChildFrameElement(frameId) { - const frameInfo = this._childFrameMap.get(frameId); - if (typeof frameInfo === 'undefined') { return null; } - - let {frameElement} = frameInfo; - if (typeof frameElement === 'undefined') { - frameElement = this._findFrameElementWithContentWindow(frameInfo.window); - frameInfo.frameElement = frameElement; - } - - return frameElement; - } - - // Private - - _getFrameAncestryInfo(timeout=5000) { - return new Promise((resolve, reject) => { - const targetWindow = window.parent; - if (window === targetWindow) { - resolve([]); - return; - } - - const uniqueId = generateId(16); - let nonce = generateId(16); - const responseMessageId = `${this._responseMessageIdBase}${uniqueId}`; - const results = []; - let timer = null; - - const cleanup = () => { - if (timer !== null) { - clearTimeout(timer); - timer = null; - } - api.crossFrame.unregisterHandler(responseMessageId); - }; - const onMessage = (params) => { - if (params.nonce !== nonce) { return null; } - - // Add result - const {frameId, more} = params; - results.push(frameId); - nonce = generateId(16); - - if (!more) { - // Cleanup - cleanup(); - - // Finish - resolve(results); - } - return {nonce}; - }; - const onTimeout = () => { - timer = null; - cleanup(); - reject(new Error(`Request for parent frame ID timed out after ${timeout}ms`)); - }; - const resetTimeout = () => { - if (timer !== null) { clearTimeout(timer); } - timer = setTimeout(onTimeout, timeout); - }; - - // Start - api.crossFrame.registerHandlers([[responseMessageId, {async: false, handler: onMessage}]]); - resetTimeout(); - const frameId = this._frameId; - this._requestFrameInfo(targetWindow, frameId, frameId, uniqueId, nonce); - }); - } - - _onWindowMessage(event) { - const {source} = event; - if (source === window || source.parent !== window) { return; } - - const {data} = event; - if ( - typeof data === 'object' && - data !== null && - data.action === this._requestMessageId - ) { - this._onRequestFrameInfo(data.params, source); - } - } - - async _onRequestFrameInfo(params, source) { - try { - let {originFrameId, childFrameId, uniqueId, nonce} = params; - if ( - !this._isNonNegativeInteger(originFrameId) || - typeof uniqueId !== 'string' || - typeof nonce !== 'string' - ) { - return; - } - - const frameId = this._frameId; - const {parent} = window; - const more = (window !== parent); - const responseParams = {frameId, nonce, more}; - const responseMessageId = `${this._responseMessageIdBase}${uniqueId}`; - - try { - const response = await api.crossFrame.invoke(originFrameId, responseMessageId, responseParams); - if (response === null) { return; } - nonce = response.nonce; - } catch (e) { - return; - } - - if (!this._childFrameMap.has(childFrameId)) { - this._childFrameMap.set(childFrameId, {window: source, frameElement: void 0}); - } - - if (more) { - this._requestFrameInfo(parent, originFrameId, frameId, uniqueId, nonce); - } - } catch (e) { - // NOP - } - } - - _requestFrameInfo(targetWindow, originFrameId, childFrameId, uniqueId, nonce) { - targetWindow.postMessage({ - action: this._requestMessageId, - params: {originFrameId, childFrameId, uniqueId, nonce} - }, '*'); - } - - _isNonNegativeInteger(value) { - return ( - typeof value === 'number' && - Number.isFinite(value) && - value >= 0 && - Math.floor(value) === value - ); - } - - _findFrameElementWithContentWindow(contentWindow) { - // Check frameElement, for non-null same-origin frames - try { - const {frameElement} = contentWindow; - if (frameElement !== null) { return frameElement; } - } catch (e) { - // NOP - } - - // Check frames - const frameTypes = ['iframe', 'frame', 'embed']; - for (const frameType of frameTypes) { - for (const frame of document.getElementsByTagName(frameType)) { - if (frame.contentWindow === contentWindow) { - return frame; - } - } - } - - // Check for shadow roots - const rootElements = [document.documentElement]; - while (rootElements.length > 0) { - const rootElement = rootElements.shift(); - const walker = document.createTreeWalker(rootElement, NodeFilter.SHOW_ELEMENT); - while (walker.nextNode()) { - const element = walker.currentNode; - - if (element.contentWindow === contentWindow) { - return element; - } - - const shadowRoot = ( - element.shadowRoot || - element.openOrClosedShadowRoot // Available to Firefox 63+ for WebExtensions - ); - if (shadowRoot) { - rootElements.push(shadowRoot); - } - } - } - - // Not found - return null; - } -} |