diff options
| -rw-r--r-- | ext/bg/js/backend.js | 25 | ||||
| -rw-r--r-- | ext/bg/js/settings/popup-preview-frame.js | 26 | ||||
| -rw-r--r-- | ext/fg/js/popup.js | 144 | ||||
| -rw-r--r-- | ext/manifest.json | 1 | ||||
| -rw-r--r-- | ext/mixed/js/api.js | 4 | 
5 files changed, 130 insertions, 70 deletions
| diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index d1a34f82..458ea483 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -499,19 +499,30 @@ class Backend {          return Promise.resolve({frameId});      } -    _onApiInjectStylesheet({css}, sender) { +    _onApiInjectStylesheet({type, value}, sender) {          if (!sender.tab) {              return Promise.reject(new Error('Invalid tab'));          }          const tabId = sender.tab.id;          const frameId = sender.frameId; -        const details = { -            code: css, -            runAt: 'document_start', -            cssOrigin: 'user', -            allFrames: false -        }; +        const details = ( +            type === 'file' ? +            { +                file: value, +                runAt: 'document_start', +                cssOrigin: 'author', +                allFrames: false, +                matchAboutBlank: true +            } : +            { +                code: value, +                runAt: 'document_start', +                cssOrigin: 'user', +                allFrames: false, +                matchAboutBlank: true +            } +        );          if (typeof frameId === 'number') {              details.frameId = frameId;          } diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js index 8fd06222..e900d4e2 100644 --- a/ext/bg/js/settings/popup-preview-frame.js +++ b/ext/bg/js/settings/popup-preview-frame.js @@ -22,7 +22,8 @@ class SettingsPopupPreview {      constructor() {          this.frontend = null;          this.apiOptionsGetOld = apiOptionsGet; -        this.popupInjectOuterStylesheetOld = Popup.injectOuterStylesheet; +        this.popup = null; +        this.popupSetCustomOuterCssOld = null;          this.popupShown = false;          this.themeChangeTimeout = null;          this.textSource = null; @@ -50,19 +51,19 @@ class SettingsPopupPreview {          const popupHost = new PopupProxyHost();          await popupHost.prepare(); -        const popup = popupHost.getOrCreatePopup(); -        popup.setChildrenSupported(false); +        this.popup = popupHost.getOrCreatePopup(); +        this.popup.setChildrenSupported(false); -        this.frontend = new Frontend(popup); +        this.popupSetCustomOuterCssOld = this.popup.setCustomOuterCss; +        this.popup.setCustomOuterCss = (...args) => this.popupSetCustomOuterCss(...args); + +        this.frontend = new Frontend(this.popup);          this.frontend.setEnabled = function () {};          this.frontend.searchClear = function () {};          await this.frontend.prepare(); -        // Overwrite popup -        Popup.injectOuterStylesheet = (...args) => this.popupInjectOuterStylesheet(...args); -          // Update search          this.updateSearch();      } @@ -83,14 +84,13 @@ class SettingsPopupPreview {          return options;      } -    popupInjectOuterStylesheet(...args) { +    async popupSetCustomOuterCss(...args) {          // This simulates the stylesheet priorities when injecting using the web extension API. -        const result = this.popupInjectOuterStylesheetOld(...args); +        const result = await this.popupSetCustomOuterCssOld.call(this.popup, ...args); -        const outerStylesheet = Popup.outerStylesheet;          const node = document.querySelector('#client-css'); -        if (node !== null && outerStylesheet !== null) { -            node.parentNode.insertBefore(outerStylesheet, node); +        if (node !== null && result !== null) { +            node.parentNode.insertBefore(result, node);          }          return result; @@ -137,7 +137,7 @@ class SettingsPopupPreview {      setCustomOuterCss(css) {          if (this.frontend === null) { return; } -        this.frontend.popup.setCustomOuterCss(css, true); +        this.frontend.popup.setCustomOuterCss(css, false);      }      async updateSearch() { diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 45203c03..59c46ab8 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -31,7 +31,6 @@ class Popup {          this._visible = false;          this._visibleOverride = null;          this._options = null; -        this._stylesheetInjectedViaApi = false;          this._contentScale = 1.0;          this._containerSizeContentScale = null; @@ -158,21 +157,13 @@ class Popup {          this._container.dataset.yomichanSiteColor = this._getSiteColor();      } -    async setCustomOuterCss(css, injectDirectly) { -        // Cannot repeatedly inject stylesheets using web extension APIs since there is no way to remove them. -        if (this._stylesheetInjectedViaApi) { return; } - -        if (injectDirectly || Popup._isOnExtensionPage()) { -            Popup.injectOuterStylesheet(css); -        } else { -            if (!css) { return; } -            try { -                await apiInjectStylesheet(css); -                this._stylesheetInjectedViaApi = true; -            } catch (e) { -                // NOP -            } -        } +    async setCustomOuterCss(css, useWebExtensionApi) { +        return await Popup._injectStylesheet( +            'yomichan-popup-outer-user-stylesheet', +            'code', +            css, +            useWebExtensionApi +        );      }      setChildrenSupported(value) { @@ -187,26 +178,6 @@ class Popup {          return this._container.getBoundingClientRect();      } -    static injectOuterStylesheet(css) { -        if (Popup.outerStylesheet === null) { -            if (!css) { return; } -            Popup.outerStylesheet = document.createElement('style'); -            Popup.outerStylesheet.id = 'yomichan-popup-outer-stylesheet'; -        } - -        const outerStylesheet = Popup.outerStylesheet; -        if (css) { -            outerStylesheet.textContent = css; - -            const par = document.head; -            if (par && outerStylesheet.parentNode !== par) { -                par.appendChild(outerStylesheet); -            } -        } else { -            outerStylesheet.textContent = ''; -        } -    } -      // Private functions      _inject() { @@ -248,10 +219,24 @@ class Popup {              });              this._observeFullscreen(true);              this._onFullscreenChanged(); -            this.setCustomOuterCss(this._options.general.customPopupOuterCss, false); +            this._injectStyles();          });      } +    async _injectStyles() { +        try { +            await Popup._injectStylesheet('yomichan-popup-outer-stylesheet', 'file', '/fg/css/client.css', true); +        } catch (e) { +            // NOP +        } + +        try { +            await this.setCustomOuterCss(this._options.general.customPopupOuterCss, true); +        } catch (e) { +            // NOP +        } +    } +      _observeFullscreen(observe) {          if (!observe) {              this._fullscreenEventListeners.removeAllEventListeners(); @@ -526,15 +511,6 @@ class Popup {          ];      } -    static _isOnExtensionPage() { -        try { -            const url = chrome.runtime.getURL('/'); -            return window.location.href.substring(0, url.length) === url; -        } catch (e) { -            // NOP -        } -    } -      static _getViewport(useVisualViewport) {          const visualViewport = window.visualViewport;          if (visualViewport !== null && typeof visualViewport === 'object') { @@ -567,6 +543,80 @@ class Popup {              bottom: window.innerHeight          };      } + +    static _isOnExtensionPage() { +        try { +            const url = chrome.runtime.getURL('/'); +            return window.location.href.substring(0, url.length) === url; +        } catch (e) { +            // NOP +        } +    } + +    static async _injectStylesheet(id, type, value, useWebExtensionApi) { +        const injectedStylesheets = Popup._injectedStylesheets; + +        if (Popup._isOnExtensionPage()) { +            // Permissions error will occur if trying to use the WebExtension API to inject +            // into an extension page. +            useWebExtensionApi = false; +        } + +        let styleNode = injectedStylesheets.get(id); +        if (typeof styleNode !== 'undefined') { +            if (styleNode === null) { +                // Previously injected via WebExtension API +                throw new Error(`Stylesheet with id ${id} has already been injected using the WebExtension API`); +            } +        } else { +            styleNode = null; +        } + +        if (useWebExtensionApi) { +            // Inject via WebExtension API +            if (styleNode !== null && styleNode.parentNode !== null) { +                styleNode.parentNode.removeChild(styleNode); +            } + +            await apiInjectStylesheet(type, value); + +            injectedStylesheets.set(id, null); +            return null; +        } + +        // Create node in document +        const parentNode = document.head; +        if (parentNode === null) { +            throw new Error('No parent node'); +        } + +        // Create or reuse node +        const isFile = (type === 'file'); +        const tagName = isFile ? 'link' : 'style'; +        if (styleNode === null || styleNode.nodeName.toLowerCase() !== tagName) { +            if (styleNode !== null && styleNode.parentNode !== null) { +                styleNode.parentNode.removeChild(styleNode); +            } +            styleNode = document.createElement(tagName); +            styleNode.id = id; +        } + +        // Update node style +        if (isFile) { +            styleNode.rel = value; +        } else { +            styleNode.textContent = value; +        } + +        // Update parent +        if (styleNode.parentNode !== parentNode) { +            parentNode.appendChild(styleNode); +        } + +        // Add to map +        injectedStylesheets.set(id, styleNode); +        return styleNode; +    }  } -Popup.outerStylesheet = null; +Popup._injectedStylesheets = new Map(); diff --git a/ext/manifest.json b/ext/manifest.json index 68a8adb4..b86459f9 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -30,7 +30,6 @@              "fg/js/frontend.js",              "fg/js/frontend-initialize.js"          ], -        "css": ["fg/css/client.css"],          "match_about_blank": true,          "all_frames": true      }], diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js index 86bdc73c..14900ecf 100644 --- a/ext/mixed/js/api.js +++ b/ext/mixed/js/api.js @@ -89,8 +89,8 @@ function apiFrameInformationGet() {      return _apiInvoke('frameInformationGet');  } -function apiInjectStylesheet(css) { -    return _apiInvoke('injectStylesheet', {css}); +function apiInjectStylesheet(type, value) { +    return _apiInvoke('injectStylesheet', {type, value});  }  function apiGetEnvironmentInfo() { |