Simplified and apparently more reliable+flexible+efficient dynamic script injection method.
This commit is contained in:
parent
673c881559
commit
81bd93a72d
|
@ -422,7 +422,7 @@ var RequestGuard = (() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug(`CSP blocker:`, blocker);
|
debug(`CSP blocker on %s:`, request.url, blocker);
|
||||||
if (blocker) {
|
if (blocker) {
|
||||||
if (header) {
|
if (header) {
|
||||||
header.value = CSP.inject(header.value, blocker);
|
header.value = CSP.inject(header.value, blocker);
|
||||||
|
|
|
@ -1,130 +1,61 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
{
|
{
|
||||||
let runningScripts = new Map();
|
let pendingRequests = new Map();
|
||||||
|
let cleanup = r => {
|
||||||
|
pendingRequests.delete(r.requestId);
|
||||||
|
};
|
||||||
|
let filter = {
|
||||||
|
urls: ["<all_urls>"],
|
||||||
|
types: ["main_frame", "sub_frame", "object"]
|
||||||
|
};
|
||||||
|
browser.webRequest.onCompleted.addListener(cleanup, filter);
|
||||||
|
browser.webRequest.onErrorOccurred.addListener(cleanup, filter);
|
||||||
var RequestUtil = {
|
var RequestUtil = {
|
||||||
async executeOnStart(request, details) {
|
async executeOnStart(request, details) {
|
||||||
let {requestId, tabId, frameId} = request;
|
let {requestId, tabId, frameId} = request;
|
||||||
details = Object.assign({
|
let scripts = pendingRequests.get(requestId);
|
||||||
runAt: "document_start",
|
let scriptKey = JSON.stringify(details);
|
||||||
frameId,
|
if (!scripts) {
|
||||||
}, details);
|
pendingRequests.set(requestId, scripts = new Map());
|
||||||
browser.tabs.executeScript(tabId, details);
|
scripts.set(scriptKey, details);
|
||||||
return;
|
} else {
|
||||||
let filter = browser.webRequest.filterResponseData(requestId);
|
scripts.set(scriptKey, details);
|
||||||
filter.onstart = event => {
|
|
||||||
browser.tabs.executeScript(tabId, details);
|
|
||||||
debug("Execute on start", details);
|
|
||||||
filter.write(new Uint8Array());
|
|
||||||
};
|
|
||||||
filter.ondata = event => {
|
|
||||||
filter.write(event.data);
|
|
||||||
filter.disconnect();
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async executeOnStartCS(request, details) {
|
|
||||||
let {url, requestId, tabId, frameId} = request;
|
|
||||||
|
|
||||||
let urlObj = new URL(url);
|
|
||||||
if (urlObj.hash || urlObj.port || urlObj.username) {
|
|
||||||
urlObj.hash = urlObj.port = urlObj.username = "";
|
|
||||||
url = urlObj.toString();
|
|
||||||
}
|
|
||||||
let wr = browser.webRequest;
|
|
||||||
let filter = {
|
|
||||||
urls: [`${urlObj.origin}/*`],
|
|
||||||
types: ["main_frame", "sub_frame", "object"]
|
|
||||||
};
|
|
||||||
let finalize;
|
|
||||||
let cleanup = r => {
|
|
||||||
if (cleanup && r.requestId === requestId) {
|
|
||||||
wr.onCompleted.removeListener(cleanup);
|
|
||||||
wr.onErrorOccurred.removeListener(cleanup);
|
|
||||||
cleanup = null;
|
|
||||||
if (finalize) {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
wr.onCompleted.addListener(cleanup, filter);
|
|
||||||
wr.onErrorOccurred.addListener(cleanup, filter);
|
|
||||||
|
|
||||||
details = Object.assign({
|
|
||||||
runAt: "document_start",
|
|
||||||
frameId,
|
|
||||||
}, details);
|
|
||||||
|
|
||||||
if (browser.contentScripts) {
|
|
||||||
let js = [{}];
|
|
||||||
if (details.file) js[0].file = details.file;
|
|
||||||
else if (details.code) js[0].code = details.code;
|
|
||||||
let settings = {
|
|
||||||
"runAt": details.runAt,
|
|
||||||
js,
|
|
||||||
matches: [url],
|
|
||||||
allFrames: frameId !== 0,
|
|
||||||
}
|
|
||||||
// let's try to avoid duplicates
|
|
||||||
let key = JSON.stringify(settings);
|
|
||||||
if (runningScripts.has(key)) {
|
|
||||||
let scriptRef = runningScripts.get(key);
|
|
||||||
scriptRef.count++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (settings.allFrames) {
|
|
||||||
// let's check whether the same script is registered for top frames:
|
|
||||||
// if it is, let's unregister it first to avoid duplicates
|
|
||||||
settings.allFrames = false;
|
|
||||||
let topKey = JSON.stringify(settings);
|
|
||||||
settings.allFrames = true;
|
|
||||||
if (runningScripts.has(topKey)) {
|
|
||||||
let topScript = runningScripts.get(topKey);
|
|
||||||
try {
|
|
||||||
topScript.unregister();
|
|
||||||
} catch (e) {
|
|
||||||
error(e);
|
|
||||||
} finally {
|
|
||||||
runningScripts.delete(topKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let script = await browser.contentScripts.register(settings);
|
|
||||||
debug("Content script %o registered.", settings);
|
|
||||||
finalize = () => {
|
|
||||||
debug("Finalizing content script %o...", settings);
|
|
||||||
try {
|
|
||||||
script.unregister();
|
|
||||||
runningScripts.delete(key);
|
|
||||||
debug("Content script %o unregistered!", settings);
|
|
||||||
} finally {
|
|
||||||
finalize = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runningScripts.set(key, script);
|
|
||||||
if (!cleanup) { // the request has already been interrupted
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function listener(r) {
|
let filter = browser.webRequest.filterResponseData(requestId);
|
||||||
if (r.requestId === requestId) {
|
let buffer = [];
|
||||||
browser.tabs.executeScript(tabId, details);
|
filter.onstart = async event => {
|
||||||
finalize();
|
filter.write(new Uint8Array());
|
||||||
finalize = null;
|
for (let details of scripts.values()) {
|
||||||
|
details = Object.assign({
|
||||||
|
runAt: "document_start",
|
||||||
|
frameId,
|
||||||
|
}, details);
|
||||||
|
try {
|
||||||
|
await browser.tabs.executeScript(tabId, details);
|
||||||
|
debug("Execute on start OK", request.url, details);
|
||||||
|
} catch (e) {
|
||||||
|
error(e, "Execute on start failed", request.url, details);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (buffer.length) {
|
||||||
|
debug("Flushing %s buffer chunks", buffer.length);
|
||||||
|
for (let chunk of buffer) {
|
||||||
|
filter.write(chunk);
|
||||||
|
}
|
||||||
|
filter.disconnect();
|
||||||
|
buffer = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
filter.ondata = event => {
|
||||||
|
if (buffer) {
|
||||||
|
buffer.push(event.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
filter.write(event.data);
|
||||||
|
filter.disconnect();
|
||||||
}
|
}
|
||||||
finalize = () => {
|
}
|
||||||
wr.onResponseStarted.removeListener(listener);
|
|
||||||
}
|
|
||||||
wr.onResponseStarted.addListener(listener, filter);
|
|
||||||
debug("Executing %o", details);
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
console.log("Media Hook", document.documentElement.innerHTML);
|
debug("Media Hook (blocked %s)", !!window.mediaBlocker, document.URL, document.documentElement && document.documentElement.innerHTML);
|
||||||
try {
|
try {
|
||||||
(() => {
|
(() => {
|
||||||
let unpatched = new Map();
|
let unpatched = new Map();
|
||||||
|
@ -25,9 +25,6 @@ try {
|
||||||
patch(window.MediaSource.prototype, "addSourceBuffer", function(mime, ...args) {
|
patch(window.MediaSource.prototype, "addSourceBuffer", function(mime, ...args) {
|
||||||
let ms = this;
|
let ms = this;
|
||||||
let urls = urlMap.get(ms);
|
let urls = urlMap.get(ms);
|
||||||
let me = Array.from(document.querySelectorAll("video,audio"))
|
|
||||||
.find(e => e.srcObject === ms || urls && urls.has(e.src));
|
|
||||||
let exposedMime = `${mime} (MSE)`;
|
|
||||||
|
|
||||||
let request = {
|
let request = {
|
||||||
id: "noscript-media",
|
id: "noscript-media",
|
||||||
|
@ -40,13 +37,21 @@ try {
|
||||||
notifyPage();
|
notifyPage();
|
||||||
|
|
||||||
if (window.mediaBlocker) {
|
if (window.mediaBlocker) {
|
||||||
try {
|
(async () => {
|
||||||
let ph = PlaceHolder.create("media", request);
|
let me = Array.from(document.querySelectorAll("video,audio"))
|
||||||
ph.replace(me);
|
.find(e => e.srcObject === ms || urls && urls.has(e.src));
|
||||||
PlaceHolder.listen();
|
|
||||||
} catch (e) {
|
if (!me) return;
|
||||||
error(e);
|
let exposedMime = `${mime} (MSE)`;
|
||||||
}
|
|
||||||
|
try {
|
||||||
|
let ph = PlaceHolder.create("media", request);
|
||||||
|
ph.replace(me);
|
||||||
|
PlaceHolder.listen();
|
||||||
|
} catch (e) {
|
||||||
|
error(e);
|
||||||
|
}
|
||||||
|
})();
|
||||||
throw new Error(`${exposedMime} blocked by NoScript`);
|
throw new Error(`${exposedMime} blocked by NoScript`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
console.log("WebGL Hook", document.documentElement.innerHTML);
|
console.log("WebGL Hook", document.URL, document.documentElement && document.documentElement.innerHTML);
|
||||||
try {
|
try {
|
||||||
let proto = HTMLCanvasElement.prototype;
|
let proto = HTMLCanvasElement.prototype;
|
||||||
let getContext = proto.getContext;
|
let getContext = proto.getContext;
|
||||||
|
|
|
@ -9,6 +9,6 @@
|
||||||
console.debug(`${PREFIX} ${msg}`, ...rest);
|
console.debug(`${PREFIX} ${msg}`, ...rest);
|
||||||
}
|
}
|
||||||
function error(e, msg, ...rest) {
|
function error(e, msg, ...rest) {
|
||||||
console.error(`${PREFIX} ${msg}`, e, e.message, e.stack);
|
console.error(`${PREFIX} ${msg}`, ...rest, e, e.message, e.stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue