summaryrefslogtreecommitdiff
path: root/data/extensions/jsr@javascriptrestrictor/nscl/service/DocStartInjection.js
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/jsr@javascriptrestrictor/nscl/service/DocStartInjection.js')
-rw-r--r--data/extensions/jsr@javascriptrestrictor/nscl/service/DocStartInjection.js98
1 files changed, 82 insertions, 16 deletions
diff --git a/data/extensions/jsr@javascriptrestrictor/nscl/service/DocStartInjection.js b/data/extensions/jsr@javascriptrestrictor/nscl/service/DocStartInjection.js
index 1b78604..65571ab 100644
--- a/data/extensions/jsr@javascriptrestrictor/nscl/service/DocStartInjection.js
+++ b/data/extensions/jsr@javascriptrestrictor/nscl/service/DocStartInjection.js
@@ -1,7 +1,7 @@
/*
* NoScript Commons Library
* Reusable building blocks for cross-browser security/privacy WebExtensions.
- * Copyright (C) 2020-2023 Giorgio Maone <https://maone.net>
+ * Copyright (C) 2020-2024 Giorgio Maone <https://maone.net>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
@@ -25,7 +25,9 @@
var DocStartInjection = (() => {
const MSG_ID = "__DocStartInjection__";
- let repeating = !("contentScripts" in browser);
+ const repeating = !("contentScripts" in browser);
+ const mv3Callbacks = repeating && !browser.tabs.executeScript; // mv3 on Chrome
+
let scriptBuilders = new Set();
let getId = ({requestId, tabId, frameId, url}) => requestId || `${tabId}:${frameId}:${url}`;
let pending = new Map();
@@ -47,16 +49,32 @@ var DocStartInjection = (() => {
let {tabId, frameId, url} = request;
if (tabId < 0 || !/^(?:(?:https?|ftp|data|blob|file):|about:blank$)/.test(url)) return;
- await Promise.all([...scriptBuilders].map(async buildScript => {
+ await Promise.allSettled([...scriptBuilders].map(async buildScript => {
+ let script;
try {
- let script = await buildScript({tabId, frameId, url});
- if (script) scripts.add(`try {
+ script = await buildScript({tabId, frameId, url});
+ if (!script) return;
+ if (mv3Callbacks) {
+ if (typeof script !== "object") {
+ throw new Error('On MV3 only {data: jsonObject, callback: "globalFunctionName", assign: "globalScopeVarName"} injection can work!')
+ }
+ const {data, callback, assign} = script;
+ scripts.add({
+ data,
+ callback,
+ assign,
+ });
+ return;
+ }
+ // mv2
+ scripts.add(`try {
${typeof script === "function" ? `(${script})();` : script}
} catch (e) {
console.error("Error in DocStartInjection script", e);
- }`);
+ }`
+ );
} catch (e) {
- error("Error calling DocStartInjection scriptBuilder", buildScript, e);
+ error(`Error calling DocStartInjection scriptBuilder: buildScript ${buildScript} - script: ${script}`, e);
}
}));
@@ -68,9 +86,42 @@ var DocStartInjection = (() => {
let id = getId(request);
if (repeating) {
- let scriptsBlock = [...scripts].join("\n");
- let injectionId = `injection:${uuid()}:${await sha256(scriptsBlock)}`;
- let args = {
+ let injectionId = `injection:${uuid()}:${await sha256(Math.random().toString(16))}`;
+ let args = mv3Callbacks ?
+ // mv3 browser.scripting.executeScript()
+ {
+ func: (url, injectionId, scripts) => {
+ if (document.readyState === "complete" ||
+ window[injectionId] ||
+ document.URL !== url
+ ) return window[injectionId];
+ window[injectionId] = true;
+ for (s of scripts) {
+ const {callback, assign, data} = s;
+ try {
+ if (assign && !(assign in globalThis)) {
+ globalThis[assign] = data;
+ }
+ if (callback) {
+ let cb = globalThis[callback];
+ if (typeof cb == "function") {
+ cb.call(globalThis, data);
+ } else {
+ console.warn(`callback globalThis.${callback} is not a function (${cb}).`);
+ }
+ }
+ } catch (e) {
+ console.error(`Error in DocStartInjection script ${JSON.stringify(s)}`, e);
+ }
+ }
+ return document.readyState === "loading";
+ },
+ args: [url, injectionId, [...scripts]],
+ target: {tabId, frameIds: [frameId]},
+ injectImmediately: true,
+ } :
+ // mv2 browser.tabs.executeScript()
+ {
code: `(() => {
let injectionId = ${JSON.stringify(injectionId)};
if (document.readyState === "complete" ||
@@ -78,7 +129,7 @@ var DocStartInjection = (() => {
document.URL !== ${JSON.stringify(url)}
) return window[injectionId];
window[injectionId] = true;
- ${scriptsBlock}
+ ${[...scripts].join("\n")}
return document.readyState === "loading";
})();`,
runAt: "document_start",
@@ -117,11 +168,21 @@ var DocStartInjection = (() => {
}
async function run(request, repeat = false) {
- let id = getId(request);
- let args = pending.get(id);
+ const id = getId(request);
+ const args = pending.get(id);
if (!args) return;
let {url, tabId} = request;
- let attempts = 0, success = false;
+ let attempts = 0;
+ let success = false;
+ const execute = mv3Callbacks ?
+ async () => {
+ const ret = await browser.scripting.executeScript(args);
+ return ret[0].result;
+ }
+ : async() => {
+ const ret = await browser.tabs.executeScript(tabId, args);
+ return ret[0];
+ };
for (; pending.has(id);) {
attempts++;
try {
@@ -133,8 +194,8 @@ var DocStartInjection = (() => {
}
console.error(`DocStartInjection at ${url} ${attempts} failed attempts so far...`);
}
- let ret = await browser.tabs.executeScript(tabId, args);
- if (success = ret[0]) {
+ if (execute()) {
+ success = true;
break;
}
} catch (e) {
@@ -144,6 +205,10 @@ var DocStartInjection = (() => {
if (!/\baccess\b/.test(e.message)) {
console.error(e.message);
}
+ if (!browser.tabs.executeScript) {
+ console.error(`MV3 fatality, cannot script tab ${tabId}! ${JSON.stringify(args)}`);
+ break;
+ }
if (attempts % 1000 === 0) {
console.error(`DocStartInjection at ${url} ${attempts} failed attempts`, e);
}
@@ -209,6 +274,7 @@ var DocStartInjection = (() => {
}
return {
+ mv3Callbacks,
register(scriptBuilder) {
if (scriptBuilders.size === 0) listen(true);
scriptBuilders.add(scriptBuilder);