diff options
author | siikamiika <siikamiika@users.noreply.github.com> | 2020-04-18 23:39:21 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-18 23:39:21 +0300 |
commit | 7a03ce0194fafb0c2e49994dc6efd33d5fdb6a07 (patch) | |
tree | 68a6bc438180e9b3871cee4755a78b18ba3fa3e1 /ext | |
parent | 5b3d7fadc3534ba27eee971dac4ba5ee475c4e3d (diff) | |
parent | d66ca93ce4d6a4c9814bac4cc508c24ff87b8f69 (diff) |
Merge pull request #460 from siikamiika/iframe-popup-edge-cases
Iframe popup edge cases
Diffstat (limited to 'ext')
-rw-r--r-- | ext/fg/js/frame-offset-forwarder.js | 70 | ||||
-rw-r--r-- | ext/fg/js/frontend-initialize.js | 17 | ||||
-rw-r--r-- | ext/fg/js/popup-proxy.js | 7 | ||||
-rw-r--r-- | ext/fg/js/popup.js | 13 | ||||
-rw-r--r-- | ext/manifest.json | 2 | ||||
-rw-r--r-- | ext/mixed/js/dom.js | 10 |
6 files changed, 98 insertions, 21 deletions
diff --git a/ext/fg/js/frame-offset-forwarder.js b/ext/fg/js/frame-offset-forwarder.js index c658c55a..1a2f3e1e 100644 --- a/ext/fg/js/frame-offset-forwarder.js +++ b/ext/fg/js/frame-offset-forwarder.js @@ -23,6 +23,10 @@ class FrameOffsetForwarder { constructor() { this._started = false; + this._cacheMaxSize = 1000; + this._frameCache = new Set(); + this._unreachableContentWindowCache = new Set(); + this._forwardFrameOffset = ( window !== window.parent ? this._forwardFrameOffsetParent.bind(this) : @@ -74,12 +78,12 @@ 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; + if (!this._unreachableContentWindowCache.has(e.source)) { + sourceFrame = this._findFrameWithContentWindow(e.source); } if (sourceFrame === null) { + // closed shadow root etc. + this._addToCache(this._unreachableContentWindowCache, e.source); this._forwardFrameOffsetOrigin(null, uniqueId); return; } @@ -91,6 +95,64 @@ class FrameOffsetForwarder { this._forwardFrameOffset(offset, uniqueId); } + _findFrameWithContentWindow(contentWindow) { + 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._addToCache(this._frameCache, 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; + } + + *_getFrameElementSources() { + 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/fg/js/frontend-initialize.js b/ext/fg/js/frontend-initialize.js index 2b942258..2df59e20 100644 --- a/ext/fg/js/frontend-initialize.js +++ b/ext/fg/js/frontend-initialize.js @@ -16,6 +16,7 @@ */ /* global + * DOM * FrameOffsetForwarder * Frontend * PopupProxy @@ -24,7 +25,7 @@ * apiOptionsGet */ -async function createIframePopupProxy(url, frameOffsetForwarder) { +async function createIframePopupProxy(url, frameOffsetForwarder, setDisabled) { const rootPopupInformationPromise = yomichan.getTemporaryListenerResult( chrome.runtime.onMessage, ({action, params}, {resolve}) => { @@ -38,7 +39,7 @@ async function createIframePopupProxy(url, frameOffsetForwarder) { const getFrameOffset = frameOffsetForwarder.getOffset.bind(frameOffsetForwarder); - const popup = new PopupProxy(popupId, 0, null, frameId, url, getFrameOffset); + const popup = new PopupProxy(popupId, 0, null, frameId, url, getFrameOffset, setDisabled); await popup.prepare(); return popup; @@ -78,6 +79,13 @@ async function main() { let frontendPreparePromise = null; let frameOffsetForwarder = null; + let iframePopupsInRootFrameAvailable = true; + + const disableIframePopupsInRootFrame = () => { + iframePopupsInRootFrameAvailable = false; + applyOptions(); + }; + const applyOptions = async () => { const optionsContext = {depth: isSearchPage ? 0 : depth, url}; const options = await apiOptionsGet(optionsContext); @@ -88,8 +96,8 @@ async function main() { } let popup; - if (isIframe && options.general.showIframePopupsInRootFrame) { - popup = popups.iframe || await createIframePopupProxy(url, frameOffsetForwarder); + if (isIframe && options.general.showIframePopupsInRootFrame && DOM.getFullscreenElement() === null && iframePopupsInRootFrameAvailable) { + popup = popups.iframe || await createIframePopupProxy(url, frameOffsetForwarder, disableIframePopupsInRootFrame); popups.iframe = popup; } else if (proxy) { popup = popups.proxy || await createPopupProxy(depth, id, parentFrameId, url); @@ -117,6 +125,7 @@ async function main() { }; yomichan.on('optionsUpdated', applyOptions); + window.addEventListener('fullscreenchange', applyOptions, false); await applyOptions(); } diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index 82ad9a8f..3af83db2 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -20,7 +20,7 @@ */ class PopupProxy { - constructor(id, depth, parentId, parentFrameId, url, getFrameOffset=null) { + constructor(id, depth, parentId, parentFrameId, url, getFrameOffset=null, setDisabled=null) { this._parentId = parentId; this._parentFrameId = parentFrameId; this._id = id; @@ -28,6 +28,7 @@ class PopupProxy { this._url = url; this._apiSender = new FrontendApiSender(); this._getFrameOffset = getFrameOffset; + this._setDisabled = setDisabled; this._frameOffset = null; this._frameOffsetPromise = null; @@ -142,6 +143,10 @@ class PopupProxy { try { const offset = await this._frameOffsetPromise; this._frameOffset = offset !== null ? offset : [0, 0]; + if (offset === null && this._setDisabled !== null) { + this._setDisabled(); + return; + } this._frameOffsetUpdatedAt = now; } catch (e) { logError(e); diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 99610e17..ae158263 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -16,6 +16,7 @@ */ /* global + * DOM * apiGetMessageToken * apiInjectStylesheet */ @@ -271,7 +272,7 @@ class Popup { } _onFullscreenChanged() { - const parent = (Popup._getFullscreenElement() || document.body || null); + const parent = (DOM.getFullscreenElement() || document.body || null); if (parent !== null && this._container.parentNode !== parent) { parent.appendChild(this._container); } @@ -365,16 +366,6 @@ class Popup { contentWindow.postMessage({action, params, token}, this._targetOrigin); } - static _getFullscreenElement() { - return ( - document.fullscreenElement || - document.msFullscreenElement || - document.mozFullScreenElement || - document.webkitFullscreenElement || - null - ); - } - static _getPositionForHorizontalText(elementRect, width, height, viewport, offsetScale, optionsGeneral) { const preferBelow = (optionsGeneral.popupHorizontalTextPosition === 'below'); const horizontalOffset = optionsGeneral.popupHorizontalOffset * offsetScale; 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" } } } diff --git a/ext/mixed/js/dom.js b/ext/mixed/js/dom.js index 03acbb80..31ba33d6 100644 --- a/ext/mixed/js/dom.js +++ b/ext/mixed/js/dom.js @@ -62,4 +62,14 @@ class DOM { default: return false; } } + + static getFullscreenElement() { + return ( + document.fullscreenElement || + document.msFullscreenElement || + document.mozFullScreenElement || + document.webkitFullscreenElement || + null + ); + } } |