diff --git a/.eslintrc.json b/.eslintrc.json index 4ec8f0f6..48746bdb 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -120,6 +120,7 @@ "files": ["ext/**/*.js"], "excludedFiles": [ "ext/js/core.js", + "ext/js/document-start.js", "ext/js/**/sandbox/**/*.js" ], "globals": { @@ -146,6 +147,7 @@ "files": ["ext/**/*.js"], "excludedFiles": [ "ext/js/core.js", + "ext/js/document-start.js", "ext/js/yomichan.js", "ext/js/**/sandbox/**/*.js" ], diff --git a/dev/data/manifest-variants.json b/dev/data/manifest-variants.json index 52581350..0de348b9 100644 --- a/dev/data/manifest-variants.json +++ b/dev/data/manifest-variants.json @@ -33,11 +33,14 @@ }, "content_scripts": [ { + "run_at": "document_idle", "matches": [ "http://*/*", "https://*/*", "file://*/*" ], + "match_about_blank": true, + "all_frames": true, "js": [ "js/core.js", "js/yomichan.js", @@ -59,9 +62,20 @@ "js/language/text-scanner.js", "js/script/dynamic-loader.js", "js/app/content-script-main.js" + ] + }, + { + "run_at": "document_start", + "matches": [ + "http://*/*", + "https://*/*", + "file://*/*" ], "match_about_blank": true, - "all_frames": true + "all_frames": true, + "js": [ + "js/document-start.js" + ] } ], "minimum_chrome_version": "57.0.0.0", diff --git a/ext/data/schemas/options-schema.json b/ext/data/schemas/options-schema.json index 1565cfe6..d1fb28ad 100644 --- a/ext/data/schemas/options-schema.json +++ b/ext/data/schemas/options-schema.json @@ -72,7 +72,8 @@ "anki", "sentenceParsing", "inputs", - "clipboard" + "clipboard", + "accessibility" ], "properties": { "general": { @@ -1109,6 +1110,18 @@ "minimum": 0 } } + }, + "accessibility": { + "type": "object", + "required": [ + "forceGoogleDocsHtmlRendering" + ], + "properties": { + "forceGoogleDocsHtmlRendering": { + "type": "boolean", + "default": false + } + } } } } diff --git a/ext/js/accessibility/google-docs.js b/ext/js/accessibility/google-docs.js new file mode 100644 index 00000000..f9ba0f7f --- /dev/null +++ b/ext/js/accessibility/google-docs.js @@ -0,0 +1,27 @@ +/* + * 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 . + */ + +(() => { + let parent = document.head; + if (parent === null) { + parent = document.documentElement; + if (parent === null) { return; } + } + const script = document.createElement('script'); + script.textContent = 'window._docs_force_html_by_ext = true;'; + parent.appendChild(script); +})(); diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index 623f3612..b4b9bc27 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -124,7 +124,8 @@ class Backend { ['isTabSearchPopup', {async: true, contentScript: true, handler: this._onApiIsTabSearchPopup.bind(this)}], ['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)}] + ['textHasJapaneseCharacters', {async: false, contentScript: true, handler: this._onApiTextHasJapaneseCharacters.bind(this)}], + ['documentStart', {async: false, contentScript: true, handler: this._onDocumentStart.bind(this)}] ]); this._messageHandlersWithProgress = new Map([ ]); @@ -745,6 +746,12 @@ class Backend { return this._japaneseUtil.isStringPartiallyJapanese(text); } + _onDocumentStart(params, sender) { + const {tab, frameId, url} = sender; + if (typeof url !== 'string' || typeof tab !== 'object' || tab === null) { return; } + this._updateTabAccessibility(url, tab, frameId); + } + // Command handlers async _onCommandOpenSearchPage(params) { @@ -2207,4 +2214,31 @@ class Backend { // NOP } } + + _updateTabAccessibility(url, tab, 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; } + + const details = { + allFrames: false, + frameId, + file, + matchAboutBlank: true, + runAt: 'document_start' + }; + const callback = () => this._checkLastError(chrome.runtime.lastError); + chrome.tabs.executeScript(tab.id, details, callback); + } } diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index 42d8a93a..30ffadb1 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -462,7 +462,8 @@ class OptionsUtil { {async: true, update: this._updateVersion10.bind(this)}, {async: false, update: this._updateVersion11.bind(this)}, {async: true, update: this._updateVersion12.bind(this)}, - {async: true, update: this._updateVersion13.bind(this)} + {async: true, update: this._updateVersion13.bind(this)}, + {async: false, update: this._updateVersion14.bind(this)} ]; if (typeof targetVersion === 'number' && targetVersion < result.length) { result.splice(targetVersion); @@ -864,4 +865,15 @@ class OptionsUtil { } return options; } + + _updateVersion14(options) { + // Version 14 changes: + // Added accessibility options. + for (const profile of options.profiles) { + profile.options.accessibility = { + forceGoogleDocsHtmlRendering: false + }; + } + return options; + } } diff --git a/ext/js/document-start.js b/ext/js/document-start.js new file mode 100644 index 00000000..de1c2403 --- /dev/null +++ b/ext/js/document-start.js @@ -0,0 +1,18 @@ +/* + * 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 dd0f31c9..35f7b18e 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -32,11 +32,14 @@ }, "content_scripts": [ { + "run_at": "document_idle", "matches": [ "http://*/*", "https://*/*", "file://*/*" ], + "match_about_blank": true, + "all_frames": true, "js": [ "js/core.js", "js/yomichan.js", @@ -58,9 +61,20 @@ "js/language/text-scanner.js", "js/script/dynamic-loader.js", "js/app/content-script-main.js" + ] + }, + { + "run_at": "document_start", + "matches": [ + "http://*/*", + "https://*/*", + "file://*/*" ], "match_about_blank": true, - "all_frames": true + "all_frames": true, + "js": [ + "js/document-start.js" + ] } ], "minimum_chrome_version": "57.0.0.0", diff --git a/ext/settings.html b/ext/settings.html index cd9231c1..fbf041ad 100644 --- a/ext/settings.html +++ b/ext/settings.html @@ -37,6 +37,7 @@ Clipboard Shortcuts Backup + Accessibility Security + + +
+
+
+
+
+ Force HTML-based rendering for Google Docs + (?) +
+
+
+ +
+
+ +
+
+
diff --git a/test/test-options-util.js b/test/test-options-util.js index 255b6a27..0feeb21c 100644 --- a/test/test-options-util.js +++ b/test/test-options-util.js @@ -503,6 +503,9 @@ function createProfileOptionsUpdatedTestData1() { enableSearchPageMonitor: false, autoSearchContent: true, maximumSearchLength: 1000 + }, + accessibility: { + forceGoogleDocsHtmlRendering: false } }; } @@ -590,7 +593,7 @@ function createOptionsUpdatedTestData1() { } ], profileCurrent: 0, - version: 13, + version: 14, global: { database: { prefixWildcardsSupported: false