diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2021-11-22 19:29:20 -0500 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-11-22 19:29:20 -0500 | 
| commit | cedf6b25c4327d33411877dbb412877dfa7753e9 (patch) | |
| tree | dd5c8bcfb162b8ea5c67c3ba6eeacb396a143edf | |
| parent | b0a0184334a375b33d9ee5b2f8f834dc5a7841b7 (diff) | |
ScriptManager (#2021)
* Create ScriptManager class
* Use ScriptManager in Backend
| -rw-r--r-- | .eslintrc.json | 1 | ||||
| -rw-r--r-- | ext/background.html | 1 | ||||
| -rw-r--r-- | ext/js/background/backend.js | 153 | ||||
| -rw-r--r-- | ext/js/background/script-manager.js | 160 | ||||
| -rw-r--r-- | ext/sw.js | 1 | 
5 files changed, 172 insertions, 144 deletions
| diff --git a/.eslintrc.json b/.eslintrc.json index 1608bc9f..bfc9c3d2 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -186,6 +186,7 @@                  "ext/js/background/environment.js",                  "ext/js/background/profile-conditions-util.js",                  "ext/js/background/request-builder.js", +                "ext/js/background/script-manager.js",                  "ext/js/comm/anki.js",                  "ext/js/comm/clipboard-monitor.js",                  "ext/js/comm/clipboard-reader.js", diff --git a/ext/background.html b/ext/background.html index 6259e519..44027a48 100644 --- a/ext/background.html +++ b/ext/background.html @@ -27,6 +27,7 @@  <script src="/js/background/environment.js"></script>  <script src="/js/background/profile-conditions-util.js"></script>  <script src="/js/background/request-builder.js"></script> +<script src="/js/background/script-manager.js"></script>  <script src="/js/comm/anki.js"></script>  <script src="/js/comm/clipboard-monitor.js"></script>  <script src="/js/comm/clipboard-reader.js"></script> diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index ca2f5f16..f48a87f8 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -31,6 +31,7 @@   * PermissionsUtil   * ProfileConditionsUtil   * RequestBuilder + * ScriptManager   * StringUtil   * Translator   * wanakana @@ -67,6 +68,7 @@ class Backend {              requestBuilder: this._requestBuilder          });          this._optionsUtil = new OptionsUtil(); +        this._scriptManager = new ScriptManager();          this._searchPopupTabId = null;          this._searchPopupTabCreatePromise = null; @@ -559,8 +561,10 @@ class Backend {          return Promise.resolve({tabId, frameId});      } -    _onApiInjectStylesheet({type, value}, sender) { -        return this._injectStylesheet(type, value, sender); +    async _onApiInjectStylesheet({type, value}, sender) { +        const {frameId, tab} = sender; +        if (!isObject(tab)) { throw new Error('Invalid tab'); } +        return await this._scriptManager.injectStylesheet(type, value, tab.id, frameId);      }      async _onApiGetStylesheetContent({url}) { @@ -746,7 +750,7 @@ class Backend {      _onApiDocumentStart(params, sender) {          const {tab, frameId, url} = sender;          if (typeof url !== 'string' || typeof tab !== 'object' || tab === null) { return; } -        this._updateTabAccessibility(url, tab, frameId); +        this._updateTabAccessibility(url, tab.id, frameId);      }      async _onApiGetTermFrequencies({termReadingList, dictionaries}) { @@ -2085,145 +2089,6 @@ class Backend {          });      } -    _injectStylesheet(type, value, target) { -        if (isObject(chrome.tabs) && typeof chrome.tabs.insertCSS === 'function') { -            return this._injectStylesheetMV2(type, value, target); -        } else if (isObject(chrome.scripting) && typeof chrome.scripting.insertCSS === 'function') { -            return this._injectStylesheetMV3(type, value, target); -        } else { -            return Promise.reject(new Error('insertCSS function not available')); -        } -    } - -    _injectStylesheetMV2(type, value, target) { -        return new Promise((resolve, reject) => { -            if (!target.tab) { -                reject(new Error('Invalid tab')); -                return; -            } - -            const tabId = target.tab.id; -            const frameId = target.frameId; -            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; -            } - -            chrome.tabs.insertCSS(tabId, details, () => { -                const e = chrome.runtime.lastError; -                if (e) { -                    reject(new Error(e.message)); -                } else { -                    resolve(); -                } -            }); -        }); -    } - -    _injectStylesheetMV3(type, value, target) { -        return new Promise((resolve, reject) => { -            if (!target.tab) { -                reject(new Error('Invalid tab')); -                return; -            } - -            const tabId = target.tab.id; -            const frameId = target.frameId; -            const details = ( -                type === 'file' ? -                {origin: chrome.scripting.StyleOrigin.AUTHOR, files: [value]} : -                {origin: chrome.scripting.StyleOrigin.USER,   css: value} -            ); -            details.target = { -                tabId, -                allFrames: false -            }; -            if (typeof frameId === 'number') { -                details.target.frameIds = [frameId]; -            } - -            chrome.scripting.insertCSS(details, () => { -                const e = chrome.runtime.lastError; -                if (e) { -                    reject(new Error(e.message)); -                } else { -                    resolve(); -                } -            }); -        }); -    } - -    _injectScript(file, tabId, frameId) { -        if (isObject(chrome.tabs) && typeof chrome.tabs.executeScript === 'function') { -            return this._injectScriptMV2(file, tabId, frameId); -        } else if (isObject(chrome.scripting) && typeof chrome.scripting.executeScript === 'function') { -            return this._injectScriptMV3(file, tabId, frameId); -        } else { -            return Promise.reject(new Error('executeScript function not available')); -        } -    } - -    _injectScriptMV2(file, tabId, frameId) { -        return new Promise((resolve, reject) => { -            const details = { -                allFrames: false, -                frameId, -                file, -                matchAboutBlank: true, -                runAt: 'document_start' -            }; -            const callback = (results) => { -                const e = chrome.runtime.lastError; -                if (e) { -                    reject(new Error(e.message)); -                } else { -                    const result = results[0]; -                    resolve({frameId, result}); -                } -            }; -            chrome.tabs.executeScript(tabId, details, callback); -        }); -    } - -    _injectScriptMV3(file, tabId, frameId) { -        return new Promise((resolve, reject) => { -            const details = { -                files: [file], -                target: { -                    allFrames: false, -                    frameIds: [frameId], -                    tabId -                } -            }; -            const callback = (results) => { -                const e = chrome.runtime.lastError; -                if (e) { -                    reject(new Error(e.message)); -                } else { -                    const {frameId: frameId2, result} = results[0]; -                    resolve({frameId: frameId2, result}); -                } -            }; -            chrome.scripting.executeScript(details, callback); -        }); -    } -      _getTabById(tabId) {          return new Promise((resolve, reject) => {              chrome.tabs.get( @@ -2275,7 +2140,7 @@ class Backend {          }      } -    async _updateTabAccessibility(url, tab, frameId) { +    async _updateTabAccessibility(url, tabId, frameId) {          let file = null;          switch (new URL(url).hostname) { @@ -2291,7 +2156,7 @@ class Backend {          if (file === null) { return; } -        return await this._injectScript(file, tab.id, frameId); +        await this._scriptManager.injectScript(file, tabId, frameId);      }      async _getNormalizedDictionaryDatabaseMedia(targets) { diff --git a/ext/js/background/script-manager.js b/ext/js/background/script-manager.js new file mode 100644 index 00000000..a5dbe0d2 --- /dev/null +++ b/ext/js/background/script-manager.js @@ -0,0 +1,160 @@ +/* + * 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/>. + */ + +/** + * This class is used to manage script injection into content tabs. + */ +class ScriptManager { +    /** +     * Injects a stylesheet into a specific tab and frame. +     * @param {string} type The type of content to inject; either 'file' or 'code'. +     * @param {string} content The content to inject. +     *   If type is 'file', this argument should be a path to a file. +     *   If type is 'code', this argument should be the CSS content. +     * @param {number} tabId The id of the tab to inject into. +     * @param {number} frameId The id of the frame to inject into. +     * @returns {Promise<void>} +     */ +    injectStylesheet(type, content, tabId, frameId) { +        if (isObject(chrome.tabs) && typeof chrome.tabs.insertCSS === 'function') { +            return this._injectStylesheetMV2(type, content, tabId, frameId); +        } else if (isObject(chrome.scripting) && typeof chrome.scripting.insertCSS === 'function') { +            return this._injectStylesheetMV3(type, content, tabId, frameId); +        } else { +            return Promise.reject(new Error('Stylesheet injection not supported')); +        } +    } +    /** +     * Injects a script into a specific tab and frame. +     * @param {string} file The path to a file to inject. +     * @param {number} tabId The id of the tab to inject into. +     * @param {number} frameId The id of the frame to inject into. +     * @returns {Promise<{frameId: number, result: object}>} The id of the frame and the result of the script injection. +     */ +    injectScript(file, tabId, frameId) { +        if (isObject(chrome.tabs) && typeof chrome.tabs.executeScript === 'function') { +            return this._injectScriptMV2(file, tabId, frameId); +        } else if (isObject(chrome.scripting) && typeof chrome.scripting.executeScript === 'function') { +            return this._injectScriptMV3(file, tabId, frameId); +        } else { +            return Promise.reject(new Error('Script injection not supported')); +        } +    } + +    // Private + +    _injectStylesheetMV2(type, content, tabId, frameId) { +        return new Promise((resolve, reject) => { +            const details = ( +                type === 'file' ? +                { +                    file: content, +                    runAt: 'document_start', +                    cssOrigin: 'author', +                    allFrames: false, +                    matchAboutBlank: true +                } : +                { +                    code: content, +                    runAt: 'document_start', +                    cssOrigin: 'user', +                    allFrames: false, +                    matchAboutBlank: true +                } +            ); +            if (typeof frameId === 'number') { +                details.frameId = frameId; +            } +            chrome.tabs.insertCSS(tabId, details, () => { +                const e = chrome.runtime.lastError; +                if (e) { +                    reject(new Error(e.message)); +                } else { +                    resolve(); +                } +            }); +        }); +    } + +    _injectStylesheetMV3(type, content, tabId, frameId) { +        return new Promise((resolve, reject) => { +            const details = ( +                type === 'file' ? +                {origin: chrome.scripting.StyleOrigin.AUTHOR, files: [content]} : +                {origin: chrome.scripting.StyleOrigin.USER,   css: content} +            ); +            details.target = { +                tabId, +                allFrames: false +            }; +            if (typeof frameId === 'number') { +                details.target.frameIds = [frameId]; +            } +            chrome.scripting.insertCSS(details, () => { +                const e = chrome.runtime.lastError; +                if (e) { +                    reject(new Error(e.message)); +                } else { +                    resolve(); +                } +            }); +        }); +    } + +    _injectScriptMV2(file, tabId, frameId) { +        return new Promise((resolve, reject) => { +            const details = { +                allFrames: false, +                frameId, +                file, +                matchAboutBlank: true, +                runAt: 'document_start' +            }; +            chrome.tabs.executeScript(tabId, details, (results) => { +                const e = chrome.runtime.lastError; +                if (e) { +                    reject(new Error(e.message)); +                } else { +                    const result = results[0]; +                    resolve({frameId, result}); +                } +            }); +        }); +    } + +    _injectScriptMV3(file, tabId, frameId) { +        return new Promise((resolve, reject) => { +            const details = { +                files: [file], +                target: { +                    allFrames: false, +                    frameIds: [frameId], +                    tabId +                } +            }; +            chrome.scripting.executeScript(details, (results) => { +                const e = chrome.runtime.lastError; +                if (e) { +                    reject(new Error(e.message)); +                } else { +                    const {frameId: frameId2, result} = results[0]; +                    resolve({frameId: frameId2, result}); +                } +            }); +        }); +    } +} @@ -24,6 +24,7 @@ self.importScripts(      '/js/background/environment.js',      '/js/background/profile-conditions-util.js',      '/js/background/request-builder.js', +    '/js/background/script-manager.js',      '/js/comm/anki.js',      '/js/comm/clipboard-monitor.js',      '/js/comm/clipboard-reader.js', |