ScriptManager (#2021)
* Create ScriptManager class * Use ScriptManager in Backend
This commit is contained in:
parent
b0a0184334
commit
cedf6b25c4
@ -186,6 +186,7 @@
|
|||||||
"ext/js/background/environment.js",
|
"ext/js/background/environment.js",
|
||||||
"ext/js/background/profile-conditions-util.js",
|
"ext/js/background/profile-conditions-util.js",
|
||||||
"ext/js/background/request-builder.js",
|
"ext/js/background/request-builder.js",
|
||||||
|
"ext/js/background/script-manager.js",
|
||||||
"ext/js/comm/anki.js",
|
"ext/js/comm/anki.js",
|
||||||
"ext/js/comm/clipboard-monitor.js",
|
"ext/js/comm/clipboard-monitor.js",
|
||||||
"ext/js/comm/clipboard-reader.js",
|
"ext/js/comm/clipboard-reader.js",
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
<script src="/js/background/environment.js"></script>
|
<script src="/js/background/environment.js"></script>
|
||||||
<script src="/js/background/profile-conditions-util.js"></script>
|
<script src="/js/background/profile-conditions-util.js"></script>
|
||||||
<script src="/js/background/request-builder.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/anki.js"></script>
|
||||||
<script src="/js/comm/clipboard-monitor.js"></script>
|
<script src="/js/comm/clipboard-monitor.js"></script>
|
||||||
<script src="/js/comm/clipboard-reader.js"></script>
|
<script src="/js/comm/clipboard-reader.js"></script>
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
* PermissionsUtil
|
* PermissionsUtil
|
||||||
* ProfileConditionsUtil
|
* ProfileConditionsUtil
|
||||||
* RequestBuilder
|
* RequestBuilder
|
||||||
|
* ScriptManager
|
||||||
* StringUtil
|
* StringUtil
|
||||||
* Translator
|
* Translator
|
||||||
* wanakana
|
* wanakana
|
||||||
@ -67,6 +68,7 @@ class Backend {
|
|||||||
requestBuilder: this._requestBuilder
|
requestBuilder: this._requestBuilder
|
||||||
});
|
});
|
||||||
this._optionsUtil = new OptionsUtil();
|
this._optionsUtil = new OptionsUtil();
|
||||||
|
this._scriptManager = new ScriptManager();
|
||||||
|
|
||||||
this._searchPopupTabId = null;
|
this._searchPopupTabId = null;
|
||||||
this._searchPopupTabCreatePromise = null;
|
this._searchPopupTabCreatePromise = null;
|
||||||
@ -559,8 +561,10 @@ class Backend {
|
|||||||
return Promise.resolve({tabId, frameId});
|
return Promise.resolve({tabId, frameId});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onApiInjectStylesheet({type, value}, sender) {
|
async _onApiInjectStylesheet({type, value}, sender) {
|
||||||
return this._injectStylesheet(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}) {
|
async _onApiGetStylesheetContent({url}) {
|
||||||
@ -746,7 +750,7 @@ class Backend {
|
|||||||
_onApiDocumentStart(params, sender) {
|
_onApiDocumentStart(params, sender) {
|
||||||
const {tab, frameId, url} = sender;
|
const {tab, frameId, url} = sender;
|
||||||
if (typeof url !== 'string' || typeof tab !== 'object' || tab === null) { return; }
|
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}) {
|
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) {
|
_getTabById(tabId) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
chrome.tabs.get(
|
chrome.tabs.get(
|
||||||
@ -2275,7 +2140,7 @@ class Backend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _updateTabAccessibility(url, tab, frameId) {
|
async _updateTabAccessibility(url, tabId, frameId) {
|
||||||
let file = null;
|
let file = null;
|
||||||
|
|
||||||
switch (new URL(url).hostname) {
|
switch (new URL(url).hostname) {
|
||||||
@ -2291,7 +2156,7 @@ class Backend {
|
|||||||
|
|
||||||
if (file === null) { return; }
|
if (file === null) { return; }
|
||||||
|
|
||||||
return await this._injectScript(file, tab.id, frameId);
|
await this._scriptManager.injectScript(file, tabId, frameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getNormalizedDictionaryDatabaseMedia(targets) {
|
async _getNormalizedDictionaryDatabaseMedia(targets) {
|
||||||
|
160
ext/js/background/script-manager.js
Normal file
160
ext/js/background/script-manager.js
Normal file
@ -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/environment.js',
|
||||||
'/js/background/profile-conditions-util.js',
|
'/js/background/profile-conditions-util.js',
|
||||||
'/js/background/request-builder.js',
|
'/js/background/request-builder.js',
|
||||||
|
'/js/background/script-manager.js',
|
||||||
'/js/comm/anki.js',
|
'/js/comm/anki.js',
|
||||||
'/js/comm/clipboard-monitor.js',
|
'/js/comm/clipboard-monitor.js',
|
||||||
'/js/comm/clipboard-reader.js',
|
'/js/comm/clipboard-reader.js',
|
||||||
|
Loading…
Reference in New Issue
Block a user