diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2020-06-24 21:46:13 -0400 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-06-24 21:46:13 -0400 | 
| commit | 3e68af8666bdf9a6d8d605f7a3bb0432c8d6cb33 (patch) | |
| tree | 5674e8a865b9a55983dd2e9d54a263fb0cb99105 /ext/fg/js | |
| parent | 96932119f8627725774ffdc66a82326d7302db30 (diff) | |
Shadow DOM container for popup iframes (#623)
* Add support for injecting stylesheets into a custom parent node
* Add api.getStylesheetContent
* Add support for injecting a CSS file's content
* Add usePopupShadowDom option
* Use a per-parentNode cache
* Add support for using a shadow DOM wrapper around popup iframes
* Ignore the popup container instead of the frame
Diffstat (limited to 'ext/fg/js')
| -rw-r--r-- | ext/fg/js/frontend.js | 2 | ||||
| -rw-r--r-- | ext/fg/js/popup.js | 62 | 
2 files changed, 55 insertions, 9 deletions
| diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index ae0953f9..f6b0d236 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -345,7 +345,7 @@ class Frontend {      }      _ignoreElements() { -        return this._popup === null || this._popup.isProxy() ? [] : [this._popup.getFrame()]; +        return this._popup === null || this._popup.isProxy() ? [] : [this._popup.getContainer()];      }      _ignorePoint(x, y) { diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 3b14d3d0..5ee62c9b 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -47,6 +47,9 @@ class Popup {          this._frame.style.width = '0';          this._frame.style.height = '0'; +        this._container = this._frame; +        this._shadow = null; +          this._fullscreenEventListeners = new EventListenerCollection();      } @@ -180,7 +183,12 @@ class Popup {      }      async setCustomOuterCss(css, useWebExtensionApi) { -        return await dynamicLoader.loadStyle('yomichan-popup-outer-user-stylesheet', 'code', css, useWebExtensionApi); +        let parentNode = null; +        if (this._shadow !== null) { +            useWebExtensionApi = false; +            parentNode = this._shadow; +        } +        return await dynamicLoader.loadStyle('yomichan-popup-outer-user-stylesheet', 'code', css, useWebExtensionApi, parentNode);      }      setChildrenSupported(value) { @@ -195,6 +203,10 @@ class Popup {          return this._frame.getBoundingClientRect();      } +    getContainer() { +        return this._container; +    } +      // Private functions      _inject() { @@ -330,9 +342,9 @@ class Popup {              throw new Error('Options not initialized');          } -        const {useSecurePopupFrameUrl} = this._options.general; +        const {useSecurePopupFrameUrl, usePopupShadowDom} = this._options.general; -        this._injectStyles(); +        await this._setUpContainer(usePopupShadowDom);          const {secret, token} = await this._initializeFrame(this._frame, this._targetOrigin, this._frameId, (frame) => {              frame.removeAttribute('src'); @@ -382,9 +394,9 @@ class Popup {      }      _resetFrame() { -        const parent = this._frame.parentNode; +        const parent = this._container.parentNode;          if (parent !== null) { -            parent.removeChild(this._frame); +            parent.removeChild(this._container);          }          this._frame.removeAttribute('src');          this._frame.removeAttribute('srcdoc'); @@ -395,9 +407,31 @@ class Popup {          this._injectPromiseComplete = false;      } +    async _setUpContainer(usePopupShadowDom) { +        if (usePopupShadowDom && typeof this._frame.attachShadow === 'function') { +            const container = document.createElement('div'); +            container.style.setProperty('all', 'initial', 'important'); +            const shadow = container.attachShadow({mode: 'closed', delegatesFocus: true}); +            shadow.appendChild(this._frame); + +            this._container = container; +            this._shadow = shadow; +        } else { +            const frameParentNode = this._frame.parentNode; +            if (frameParentNode !== null) { +                frameParentNode.removeChild(this._frame); +            } + +            this._container = this._frame; +            this._shadow = null; +        } + +        await this._injectStyles(); +    } +      async _injectStyles() {          try { -            await dynamicLoader.loadStyle('yomichan-popup-outer-stylesheet', 'file', '/fg/css/client.css', true); +            await this._injectPopupOuterStylesheet();          } catch (e) {              // NOP          } @@ -409,6 +443,18 @@ class Popup {          }      } +    async _injectPopupOuterStylesheet() { +        let fileType = 'file'; +        let useWebExtensionApi = true; +        let parentNode = null; +        if (this._shadow !== null) { +            fileType = 'file-content'; +            useWebExtensionApi = false; +            parentNode = this._shadow; +        } +        await dynamicLoader.loadStyle('yomichan-popup-outer-stylesheet', fileType, '/fg/css/client.css', useWebExtensionApi, parentNode); +    } +      _observeFullscreen(observe) {          if (!observe) {              this._fullscreenEventListeners.removeAllEventListeners(); @@ -425,8 +471,8 @@ class Popup {      _onFullscreenChanged() {          const parent = this._getFrameParentElement(); -        if (parent !== null && this._frame.parentNode !== parent) { -            parent.appendChild(this._frame); +        if (parent !== null && this._container.parentNode !== parent) { +            parent.appendChild(this._container);          }      } |