From d454b52a18127f782fafdf71534ea2c41f20ef44 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Tue, 23 Nov 2021 22:08:30 -0500 Subject: [PATCH] Google Docs accessibility refactor (#2023) * Skip urlRegex if it's used as a filter * Add getRequiredContentScriptRegistrationPermissions function * Add a reentrant check to google-docs.js * Remove script node * Move forceGoogleDocsHtmlRendering check into google-docs.js * Replace document-start.js usage with google-docs.js * Remove documentStart handling * Add missing parameter descriptions --- .eslintrc.json | 4 ++-- dev/data/manifest-variants.json | 2 +- ext/js/accessibility/google-docs.js | 32 +++++++++++++++++++++++++- ext/js/background/backend.js | 26 --------------------- ext/js/background/script-manager.js | 35 ++++++++++++++++++++++++----- ext/js/document-start.js | 18 --------------- ext/manifest.json | 2 +- 7 files changed, 65 insertions(+), 54 deletions(-) delete mode 100644 ext/js/document-start.js diff --git a/.eslintrc.json b/.eslintrc.json index bfc9c3d2..a1e47ef1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -120,7 +120,7 @@ "files": ["ext/**/*.js"], "excludedFiles": [ "ext/js/core.js", - "ext/js/document-start.js", + "ext/js/accessibility/google-docs.js", "ext/js/**/sandbox/**/*.js" ], "globals": { @@ -147,7 +147,7 @@ "files": ["ext/**/*.js"], "excludedFiles": [ "ext/js/core.js", - "ext/js/document-start.js", + "ext/js/accessibility/google-docs.js", "ext/js/yomichan.js", "ext/js/**/sandbox/**/*.js" ], diff --git a/dev/data/manifest-variants.json b/dev/data/manifest-variants.json index 004cec01..957fa159 100644 --- a/dev/data/manifest-variants.json +++ b/dev/data/manifest-variants.json @@ -73,7 +73,7 @@ "match_about_blank": true, "all_frames": true, "js": [ - "js/document-start.js" + "js/accessibility/google-docs.js" ] } ], diff --git a/ext/js/accessibility/google-docs.js b/ext/js/accessibility/google-docs.js index f9ba0f7f..e45743ab 100644 --- a/ext/js/accessibility/google-docs.js +++ b/ext/js/accessibility/google-docs.js @@ -15,7 +15,36 @@ * along with this program. If not, see . */ -(() => { +(async () => { + // Reentrant check + if (self.googleDocsAccessibilitySetup) { return; } + self.googleDocsAccessibilitySetup = true; + + const invokeApi = (action, params) => { + return new Promise((resolve, reject) => { + chrome.runtime.sendMessage({action, params}, (response) => { + void chrome.runtime.lastError; + if (typeof response !== 'object' || response === null) { + reject(new Error('Unexpected response')); + } else if (typeof response.error !== 'undefined') { + reject(new Error('Invalid response')); + } else { + resolve(response.result); + } + }); + }); + }; + + const optionsContext = {depth: 0, url: location.href}; + let options; + try { + options = await invokeApi('optionsGet', {optionsContext}); + } catch (e) { + return; + } + + if (!options.accessibility.forceGoogleDocsHtmlRendering) { return; } + let parent = document.head; if (parent === null) { parent = document.documentElement; @@ -24,4 +53,5 @@ const script = document.createElement('script'); script.textContent = 'window._docs_force_html_by_ext = true;'; parent.appendChild(script); + parent.removeChild(script); })(); diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index db43ec57..4e86a23c 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -127,7 +127,6 @@ class Backend { ['triggerDatabaseUpdated', {async: false, contentScript: true, handler: this._onApiTriggerDatabaseUpdated.bind(this)}], ['testMecab', {async: true, contentScript: true, handler: this._onApiTestMecab.bind(this)}], ['textHasJapaneseCharacters', {async: false, contentScript: true, handler: this._onApiTextHasJapaneseCharacters.bind(this)}], - ['documentStart', {async: false, contentScript: true, handler: this._onApiDocumentStart.bind(this)}], ['getTermFrequencies', {async: true, contentScript: true, handler: this._onApiGetTermFrequencies.bind(this)}] ]); this._messageHandlersWithProgress = new Map([ @@ -747,12 +746,6 @@ class Backend { return this._japaneseUtil.isStringPartiallyJapanese(text); } - _onApiDocumentStart(params, sender) { - const {tab, frameId, url} = sender; - if (typeof url !== 'string' || typeof tab !== 'object' || tab === null) { return; } - this._updateTabAccessibility(url, tab.id, frameId); - } - async _onApiGetTermFrequencies({termReadingList, dictionaries}) { return await this._translator.getTermFrequencies(termReadingList, dictionaries); } @@ -2140,25 +2133,6 @@ class Backend { } } - async _updateTabAccessibility(url, tabId, frameId) { - let file = null; - - switch (new URL(url).hostname) { - case 'docs.google.com': - { - const optionsContext = {depth: 0, url}; - const options = this._getProfileOptions(optionsContext); - if (!options.accessibility.forceGoogleDocsHtmlRendering) { return; } - file = 'js/accessibility/google-docs.js'; - } - break; - } - - if (file === null) { return; } - - await this._scriptManager.injectScript(file, tabId, frameId, false, true, 'document_start'); - } - async _getNormalizedDictionaryDatabaseMedia(targets) { const results = await this._dictionaryDatabase.getMedia(targets); for (const item of results) { diff --git a/ext/js/background/script-manager.js b/ext/js/background/script-manager.js index c6bdc0bb..ba4f8671 100644 --- a/ext/js/background/script-manager.js +++ b/ext/js/background/script-manager.js @@ -106,9 +106,9 @@ class ScriptManager { * @param {string} id A unique identifier for the registration. * @param {object} details The script registration details. * @param {boolean} [details.allFrames] Same as `all_frames` in the `content_scripts` manifest key. - * @param {string[]} [details.css] + * @param {string[]} [details.css] List of CSS paths. * @param {string[]} [details.excludeMatches] Same as `exclude_matches` in the `content_scripts` manifest key. - * @param {string[]} [details.js] + * @param {string[]} [details.js] List of script paths. * @param {boolean} [details.matchAboutBlank] Same as `match_about_blank` in the `content_scripts` manifest key. * @param {string[]} details.matches Same as `matches` in the `content_scripts` manifest key. * @param {string} [details.urlMatches] Regex match pattern to use as a fallback @@ -181,6 +181,31 @@ class ScriptManager { return true; } + /** + * Gets the optional permissions required to register a content script. + * @returns {string[]} An array of the required permissions, which may be empty. + */ + getRequiredContentScriptRegistrationPermissions() { + if ( + // Firefox + ( + typeof browser === 'object' && browser !== null && + isObject(browser.contentScripts) && + typeof browser.contentScripts.register === 'function' + ) || + // Chrome + ( + isObject(chrome.scripting) && + typeof chrome.scripting.registerContentScripts === 'function' + ) + ) { + return []; + } + + // Fallback + return ['webNavigation']; + } + // Private _injectStylesheetMV2(type, content, tabId, frameId, allFrames, matchAboutBlank, runAt) { @@ -333,8 +358,7 @@ class ScriptManager { _registerContentScriptFallback(id, details) { const {allFrames, css, js, matchAboutBlank, runAt, urlMatches} = details; - const urlRegex = new RegExp(urlMatches); - const details2 = {allFrames, css, js, matchAboutBlank, runAt, urlRegex}; + const details2 = {allFrames, css, js, matchAboutBlank, runAt, urlRegex: null}; let unregister; const webNavigationEvent = this._getWebNavigationEvent(runAt); if (isObject(webNavigationEvent)) { @@ -356,6 +380,7 @@ class ScriptManager { chrome.tabs.onUpdated.addListener(onTabUpdated, extraParameters); } catch (e) { // Chrome + details2.urlRegex = new RegExp(urlMatches); chrome.tabs.onUpdated.addListener(onTabUpdated); } unregister = () => chrome.tabs.onUpdated.removeListener(onTabUpdated); @@ -378,7 +403,7 @@ class ScriptManager { async _injectContentScript(isWebNavigation, details, status, url, tabId, frameId) { const {urlRegex} = details; - if (typeof urlRegex !== 'undefined' && !urlRegex.test(url)) { return; } + if (urlRegex !== null && !urlRegex.test(url)) { return; } let {allFrames, css, js, matchAboutBlank, runAt} = details; diff --git a/ext/js/document-start.js b/ext/js/document-start.js deleted file mode 100644 index de1c2403..00000000 --- a/ext/js/document-start.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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 . - */ - -chrome.runtime.sendMessage({action: 'documentStart'}, () => void chrome.runtime.lastError); diff --git a/ext/manifest.json b/ext/manifest.json index cd75e216..422d296e 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -72,7 +72,7 @@ "match_about_blank": true, "all_frames": true, "js": [ - "js/document-start.js" + "js/accessibility/google-docs.js" ] } ],