summaryrefslogtreecommitdiff
path: root/data/extensions/jsr@javascriptrestrictor/nscl/service
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/jsr@javascriptrestrictor/nscl/service')
-rw-r--r--data/extensions/jsr@javascriptrestrictor/nscl/service/DocStartInjection.js98
-rw-r--r--data/extensions/jsr@javascriptrestrictor/nscl/service/NavCache.js32
-rw-r--r--data/extensions/jsr@javascriptrestrictor/nscl/service/TabCache.js18
3 files changed, 108 insertions, 40 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);
diff --git a/data/extensions/jsr@javascriptrestrictor/nscl/service/NavCache.js b/data/extensions/jsr@javascriptrestrictor/nscl/service/NavCache.js
index 0e603fd..28bdb0d 100644
--- a/data/extensions/jsr@javascriptrestrictor/nscl/service/NavCache.js
+++ b/data/extensions/jsr@javascriptrestrictor/nscl/service/NavCache.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
*
@@ -45,26 +45,26 @@ var NavCache = (() => {
});
browser.tabs.onRemoved.addListener(tabId => {
- tabs.delete(tabId);
+ delete tabs[tabId];
});
- (async () => {
-
- async function populateFrames(tab) {
- let tabId = tab.id;
- let frames = await browser.webNavigation.getAllFrames({tabId});
- if (!frames) return; // invalid tab
- if (!tabs[tabId]) tabs[tabId] = {};
- let top = tabs[tabId];
- for ({frameId, url, parentFrameId} of frames) {
- tab[frameId] = {url, parentFrameId};
+ return {
+ wakening: (async () => {
+ async function populateFrames(tab) {
+ let tabId = tab.id;
+ let frames = await browser.webNavigation.getAllFrames({tabId});
+ if (!frames) return; // invalid tab
+ if (!tabs[tabId]) tabs[tabId] = {};
+ let top = tabs[tabId];
+ for ({frameId, url, parentFrameId} of frames) {
+ tab[frameId] = {url, parentFrameId};
+ }
}
- }
- await Promise.all((await browser.tabs.query({})).map(populateFrames));
- })();
+ await Promise.all((await browser.tabs.query({})).map(populateFrames));
+ return true;
+ })(),
- return {
getTab(tabId) {
return clone(tabs[tabId] || {});
},
diff --git a/data/extensions/jsr@javascriptrestrictor/nscl/service/TabCache.js b/data/extensions/jsr@javascriptrestrictor/nscl/service/TabCache.js
index bac095e..eb290f0 100644
--- a/data/extensions/jsr@javascriptrestrictor/nscl/service/TabCache.js
+++ b/data/extensions/jsr@javascriptrestrictor/nscl/service/TabCache.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
*
@@ -20,7 +20,7 @@
var TabCache = (() => {
- let cache = new Map();
+ const cache = new Map();
browser.tabs.onUpdated.addListener((tabId, changes, tab) => {
cache.set(tabId, tab);
@@ -30,15 +30,17 @@ var TabCache = (() => {
cache.delete(tabId);
});
- (async () => {
- for (let tab of await browser.tabs.query({})) {
- cache.set(tab.id, tab);
- }
- })();
-
return {
+ wakening: (async () => {
+ for (let tab of await browser.tabs.query({})) {
+ cache.set(tab.id, tab);
+ }
+ })(),
get(tabId) {
return cache.get(tabId);
+ },
+ async async(tabId) {
+ return cache.get(tabId) || await browser.tabs.get(tabId);
}
};
})();