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/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",
|
||||
|
@ -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>
|
||||
|
@ -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) {
|
||||
|
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});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user