From fbaf50def1934ef6fe0967233f4419efc44f1c30 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 18 Apr 2020 00:33:49 +0300 Subject: support iframes inside open shadow dom --- ext/fg/js/frame-offset-forwarder.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'ext/fg/js/frame-offset-forwarder.js') diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index c658c55a..ac6e617d 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -79,9 +79,28 @@ class FrameOffsetForwarder { sourceFrame = frame; break; } + if (sourceFrame === null) { - this._forwardFrameOffsetOrigin(null, uniqueId); - return; + const getShadowRootElements = (documentOrElement) => { + const elements = Array.from(documentOrElement.querySelectorAll('*')) + .filter((el) => !!el.shadowRoot); + const childElements = elements + .map((el) => el.shadowRoot) + .map(getShadowRootElements); + elements.push(childElements.flat()); + + return elements.flat(); + }; + + sourceFrame = getShadowRootElements(document) + .map((el) => Array.from(el.shadowRoot.querySelectorAll('frame, iframe:not(.yomichan-float)'))) + .flat() + .find((el) => el.contentWindow === e.source); + + if (!sourceFrame) { + this._forwardFrameOffsetOrigin(null, uniqueId); + return; + } } const [forwardedX, forwardedY] = offset; -- cgit v1.2.3 From b786e2da1912dfa7d707db628d54fb914189f7d1 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 18 Apr 2020 00:55:16 +0300 Subject: move open shadow root iframe finder to a function --- ext/fg/js/frame-offset-forwarder.js | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) (limited to 'ext/fg/js/frame-offset-forwarder.js') diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index ac6e617d..2b48ba26 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -81,23 +81,9 @@ class FrameOffsetForwarder { } if (sourceFrame === null) { - const getShadowRootElements = (documentOrElement) => { - const elements = Array.from(documentOrElement.querySelectorAll('*')) - .filter((el) => !!el.shadowRoot); - const childElements = elements - .map((el) => el.shadowRoot) - .map(getShadowRootElements); - elements.push(childElements.flat()); - - return elements.flat(); - }; - - sourceFrame = getShadowRootElements(document) - .map((el) => Array.from(el.shadowRoot.querySelectorAll('frame, iframe:not(.yomichan-float)'))) - .flat() - .find((el) => el.contentWindow === e.source); - + sourceFrame = this._getOpenShadowRootSourceFrame(e.source); if (!sourceFrame) { + // closed shadow root etc. this._forwardFrameOffsetOrigin(null, uniqueId); return; } @@ -110,6 +96,24 @@ class FrameOffsetForwarder { this._forwardFrameOffset(offset, uniqueId); } + _getOpenShadowRootSourceFrame(sourceWindow) { + const getShadowRootElements = (documentOrElement) => { + const elements = Array.from(documentOrElement.querySelectorAll('*')) + .filter((el) => !!el.shadowRoot); + const childElements = elements + .map((el) => el.shadowRoot) + .map(getShadowRootElements); + elements.push(childElements.flat()); + + return elements.flat(); + }; + + return getShadowRootElements(document) + .map((el) => Array.from(el.shadowRoot.querySelectorAll('frame, iframe:not(.yomichan-float)'))) + .flat() + .find((el) => el.contentWindow === sourceWindow); + } + _forwardFrameOffsetParent(offset, uniqueId) { window.parent.postMessage({action: 'getFrameOffset', params: {offset, uniqueId}}, '*'); } -- cgit v1.2.3 From bb3ad78e373b01b64a24fc46712f24964528a24f Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 18 Apr 2020 16:48:49 +0300 Subject: optimize source frame finding --- ext/fg/js/frame-offset-forwarder.js | 66 +++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 29 deletions(-) (limited to 'ext/fg/js/frame-offset-forwarder.js') diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index 2b48ba26..c2df7581 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -22,6 +22,7 @@ class FrameOffsetForwarder { constructor() { this._started = false; + this._frameCache = new Set(); this._forwardFrameOffset = ( window !== window.parent ? @@ -73,20 +74,11 @@ class FrameOffsetForwarder { } _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; - } - + const sourceFrame = this._findFrameWithContentWindow(e.source); if (sourceFrame === null) { - sourceFrame = this._getOpenShadowRootSourceFrame(e.source); - if (!sourceFrame) { - // closed shadow root etc. - this._forwardFrameOffsetOrigin(null, uniqueId); - return; - } + // closed shadow root etc. + this._forwardFrameOffsetOrigin(null, uniqueId); + return; } const [forwardedX, forwardedY] = offset; @@ -96,22 +88,38 @@ class FrameOffsetForwarder { this._forwardFrameOffset(offset, uniqueId); } - _getOpenShadowRootSourceFrame(sourceWindow) { - const getShadowRootElements = (documentOrElement) => { - const elements = Array.from(documentOrElement.querySelectorAll('*')) - .filter((el) => !!el.shadowRoot); - const childElements = elements - .map((el) => el.shadowRoot) - .map(getShadowRootElements); - elements.push(childElements.flat()); - - return elements.flat(); - }; - - return getShadowRootElements(document) - .map((el) => Array.from(el.shadowRoot.querySelectorAll('frame, iframe:not(.yomichan-float)'))) - .flat() - .find((el) => el.contentWindow === sourceWindow); + _findFrameWithContentWindow(contentWindow) { + const elements = [ + ...this._frameCache, + // will contain duplicates, but frame elements are cheap to handle + ...document.querySelectorAll('frame, iframe:not(.yomichan-float)'), + document.documentElement + ]; + const ELEMENT_NODE = Node.ELEMENT_NODE; + while (elements.length > 0) { + const element = elements.shift(); + if (element.contentWindow === contentWindow) { + this._frameCache.add(element); + return element; + } + + const shadowRoot = element.shadowRoot; + if (shadowRoot) { + for (const child of shadowRoot.children) { + if (child.nodeType === ELEMENT_NODE) { + elements.push(child); + } + } + } + + for (const child of element.children) { + if (child.nodeType === ELEMENT_NODE) { + elements.push(child); + } + } + } + + return null; } _forwardFrameOffsetParent(offset, uniqueId) { -- cgit v1.2.3 From 66354f1f9e866fd31f6bb0365024a39697a54079 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 18 Apr 2020 17:18:33 +0300 Subject: lazy load element sources --- ext/fg/js/frame-offset-forwarder.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'ext/fg/js/frame-offset-forwarder.js') diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index c2df7581..4b77d5ed 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -89,14 +89,27 @@ class FrameOffsetForwarder { } _findFrameWithContentWindow(contentWindow) { - const elements = [ - ...this._frameCache, + const elementSources = [ + () => [...this._frameCache], // will contain duplicates, but frame elements are cheap to handle - ...document.querySelectorAll('frame, iframe:not(.yomichan-float)'), - document.documentElement + () => [...document.querySelectorAll('frame, iframe:not(.yomichan-float)')], + () => [document.documentElement] ]; + const getMoreElements = () => { + while (true) { + const source = elementSources.shift(); + if (source) { + const elements = source(); + if (elements.length === 0) { continue; } + return elements; + } + return []; + } + }; + + const elements = []; const ELEMENT_NODE = Node.ELEMENT_NODE; - while (elements.length > 0) { + while (elements.length > 0 || elements.push(...getMoreElements())) { const element = elements.shift(); if (element.contentWindow === contentWindow) { this._frameCache.add(element); -- cgit v1.2.3 From 691b7398490bbf247070cd38603e51c7a6b66121 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 18 Apr 2020 17:54:49 +0300 Subject: cache closed shadow dom content windows --- ext/fg/js/frame-offset-forwarder.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'ext/fg/js/frame-offset-forwarder.js') diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index 4b77d5ed..72731605 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -23,6 +23,7 @@ class FrameOffsetForwarder { constructor() { this._started = false; this._frameCache = new Set(); + this._unreachableContentWindowCache = new Set(); this._forwardFrameOffset = ( window !== window.parent ? @@ -74,9 +75,13 @@ class FrameOffsetForwarder { } _onGetFrameOffset(offset, uniqueId, e) { - const sourceFrame = this._findFrameWithContentWindow(e.source); + let sourceFrame = null; + if (!this._unreachableContentWindowCache.has(e.source)) { + sourceFrame = this._findFrameWithContentWindow(e.source); + } if (sourceFrame === null) { // closed shadow root etc. + this._unreachableContentWindowCache.add(e.source); this._forwardFrameOffsetOrigin(null, uniqueId); return; } -- cgit v1.2.3 From a81c33b60aac0752ccca06f5183632146f6c6bf0 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 18 Apr 2020 22:08:38 +0300 Subject: simplify element source lazy load --- ext/fg/js/frame-offset-forwarder.js | 56 +++++++++++++++---------------------- 1 file changed, 23 insertions(+), 33 deletions(-) (limited to 'ext/fg/js/frame-offset-forwarder.js') diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index 72731605..f40c642d 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -94,52 +94,42 @@ class FrameOffsetForwarder { } _findFrameWithContentWindow(contentWindow) { - const elementSources = [ - () => [...this._frameCache], - // will contain duplicates, but frame elements are cheap to handle - () => [...document.querySelectorAll('frame, iframe:not(.yomichan-float)')], - () => [document.documentElement] - ]; - const getMoreElements = () => { - while (true) { - const source = elementSources.shift(); - if (source) { - const elements = source(); - if (elements.length === 0) { continue; } - return elements; + const ELEMENT_NODE = Node.ELEMENT_NODE; + for (const elements of this._getFrameElementSources()) { + while (elements.length > 0) { + const element = elements.shift(); + if (element.contentWindow === contentWindow) { + this._frameCache.add(element); + return element; } - return []; - } - }; - const elements = []; - const ELEMENT_NODE = Node.ELEMENT_NODE; - while (elements.length > 0 || elements.push(...getMoreElements())) { - const element = elements.shift(); - if (element.contentWindow === contentWindow) { - this._frameCache.add(element); - return element; - } + const shadowRoot = element.shadowRoot; + if (shadowRoot) { + for (const child of shadowRoot.children) { + if (child.nodeType === ELEMENT_NODE) { + elements.push(child); + } + } + } - const shadowRoot = element.shadowRoot; - if (shadowRoot) { - for (const child of shadowRoot.children) { + for (const child of element.children) { if (child.nodeType === ELEMENT_NODE) { elements.push(child); } } } - - for (const child of element.children) { - if (child.nodeType === ELEMENT_NODE) { - elements.push(child); - } - } } return null; } + *_getFrameElementSources() { + yield [...this._frameCache]; + // will contain duplicates, but frame elements are cheap to handle + yield [...document.querySelectorAll('frame, iframe:not(.yomichan-float)')]; + yield [document.documentElement]; + } + _forwardFrameOffsetParent(offset, uniqueId) { window.parent.postMessage({action: 'getFrameOffset', params: {offset, uniqueId}}, '*'); } -- cgit v1.2.3 From d66ca93ce4d6a4c9814bac4cc508c24ff87b8f69 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 18 Apr 2020 22:26:11 +0300 Subject: cache invalidation --- ext/fg/js/frame-offset-forwarder.js | 29 ++++++++++++++++++++++++++--- ext/manifest.json | 2 +- 2 files changed, 27 insertions(+), 4 deletions(-) (limited to 'ext/fg/js/frame-offset-forwarder.js') diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index f40c642d..1a2f3e1e 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -22,6 +22,8 @@ class FrameOffsetForwarder { constructor() { this._started = false; + + this._cacheMaxSize = 1000; this._frameCache = new Set(); this._unreachableContentWindowCache = new Set(); @@ -81,7 +83,7 @@ class FrameOffsetForwarder { } if (sourceFrame === null) { // closed shadow root etc. - this._unreachableContentWindowCache.add(e.source); + this._addToCache(this._unreachableContentWindowCache, e.source); this._forwardFrameOffsetOrigin(null, uniqueId); return; } @@ -99,7 +101,7 @@ class FrameOffsetForwarder { while (elements.length > 0) { const element = elements.shift(); if (element.contentWindow === contentWindow) { - this._frameCache.add(element); + this._addToCache(this._frameCache, element); return element; } @@ -124,12 +126,33 @@ class FrameOffsetForwarder { } *_getFrameElementSources() { - yield [...this._frameCache]; + const frameCache = []; + for (const frame of this._frameCache) { + // removed from DOM + if (!frame.isConnected) { + this._frameCache.delete(frame); + continue; + } + frameCache.push(frame); + } + yield frameCache; // will contain duplicates, but frame elements are cheap to handle yield [...document.querySelectorAll('frame, iframe:not(.yomichan-float)')]; yield [document.documentElement]; } + _addToCache(cache, value) { + let freeSlots = this._cacheMaxSize - cache.size; + if (freeSlots <= 0) { + for (const cachedValue of cache) { + cache.delete(cachedValue); + ++freeSlots; + if (freeSlots > 0) { break; } + } + } + cache.add(value); + } + _forwardFrameOffsetParent(offset, uniqueId) { window.parent.postMessage({action: 'getFrameOffset', params: {offset, uniqueId}}, '*'); } diff --git a/ext/manifest.json b/ext/manifest.json index 452b642c..d383dab0 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -71,7 +71,7 @@ "applications": { "gecko": { "id": "alex@foosoft.net", - "strict_min_version": "52.0" + "strict_min_version": "53.0" } } } -- cgit v1.2.3 From dd6c3015c4af6db5f1deca797366ed6269b7d82c Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 10 May 2020 14:10:02 -0400 Subject: Use both shadowRoot and openOrClosedShadowRoot (#534) --- ext/fg/js/frame-offset-forwarder.js | 5 ++++- ext/fg/js/popup.js | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'ext/fg/js/frame-offset-forwarder.js') diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index 1a2f3e1e..9b68d34e 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -105,7 +105,10 @@ class FrameOffsetForwarder { return element; } - const shadowRoot = element.shadowRoot; + const shadowRoot = ( + element.shadowRoot || + element.openOrClosedShadowRoot // Available to Firefox 63+ for WebExtensions + ); if (shadowRoot) { for (const child of shadowRoot.children) { if (child.nodeType === ELEMENT_NODE) { diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index d45c7c2a..9e9debd8 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -530,7 +530,11 @@ class Popup { _getFrameParentElement() { const defaultParent = document.body; const fullscreenElement = DOM.getFullscreenElement(); - if (fullscreenElement === null || fullscreenElement.shadowRoot) { + if ( + fullscreenElement === null || + fullscreenElement.shadowRoot || + fullscreenElement.openOrClosedShadowRoot // Available to Firefox 63+ for WebExtensions + ) { return defaultParent; } -- cgit v1.2.3