Skip to content

Commit 81bd93a

Browse files
committed
Simplified and apparently more reliable+flexible+efficient dynamic script injection method.
1 parent 673c881 commit 81bd93a

File tree

5 files changed

+65
-129
lines changed

5 files changed

+65
-129
lines changed

src/bg/RequestGuard.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ var RequestGuard = (() => {
422422
}
423423
}
424424

425-
debug(`CSP blocker:`, blocker);
425+
debug(`CSP blocker on %s:`, request.url, blocker);
426426
if (blocker) {
427427
if (header) {
428428
header.value = CSP.inject(header.value, blocker);

src/bg/RequestUtil.js

Lines changed: 46 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,130 +1,61 @@
11
'use strict';
22
{
3-
let runningScripts = new Map();
4-
3+
let pendingRequests = new Map();
4+
let cleanup = r => {
5+
pendingRequests.delete(r.requestId);
6+
};
7+
let filter = {
8+
urls: ["<all_urls>"],
9+
types: ["main_frame", "sub_frame", "object"]
10+
};
11+
browser.webRequest.onCompleted.addListener(cleanup, filter);
12+
browser.webRequest.onErrorOccurred.addListener(cleanup, filter);
513
var RequestUtil = {
614
async executeOnStart(request, details) {
715
let {requestId, tabId, frameId} = request;
8-
details = Object.assign({
9-
runAt: "document_start",
10-
frameId,
11-
}, details);
12-
browser.tabs.executeScript(tabId, details);
13-
return;
14-
let filter = browser.webRequest.filterResponseData(requestId);
15-
filter.onstart = event => {
16-
browser.tabs.executeScript(tabId, details);
17-
debug("Execute on start", details);
18-
filter.write(new Uint8Array());
19-
};
20-
filter.ondata = event => {
21-
filter.write(event.data);
22-
filter.disconnect();
23-
24-
}
25-
},
26-
async executeOnStartCS(request, details) {
27-
let {url, requestId, tabId, frameId} = request;
28-
29-
let urlObj = new URL(url);
30-
if (urlObj.hash || urlObj.port || urlObj.username) {
31-
urlObj.hash = urlObj.port = urlObj.username = "";
32-
url = urlObj.toString();
16+
let scripts = pendingRequests.get(requestId);
17+
let scriptKey = JSON.stringify(details);
18+
if (!scripts) {
19+
pendingRequests.set(requestId, scripts = new Map());
20+
scripts.set(scriptKey, details);
21+
} else {
22+
scripts.set(scriptKey, details);
23+
return;
3324
}
34-
let wr = browser.webRequest;
35-
let filter = {
36-
urls: [`${urlObj.origin}/*`],
37-
types: ["main_frame", "sub_frame", "object"]
38-
};
39-
let finalize;
40-
let cleanup = r => {
41-
if (cleanup && r.requestId === requestId) {
42-
wr.onCompleted.removeListener(cleanup);
43-
wr.onErrorOccurred.removeListener(cleanup);
44-
cleanup = null;
45-
if (finalize) {
46-
finalize();
47-
}
48-
}
49-
};
50-
wr.onCompleted.addListener(cleanup, filter);
51-
wr.onErrorOccurred.addListener(cleanup, filter);
52-
53-
details = Object.assign({
54-
runAt: "document_start",
55-
frameId,
56-
}, details);
57-
58-
if (browser.contentScripts) {
59-
let js = [{}];
60-
if (details.file) js[0].file = details.file;
61-
else if (details.code) js[0].code = details.code;
62-
let settings = {
63-
"runAt": details.runAt,
64-
js,
65-
matches: [url],
66-
allFrames: frameId !== 0,
67-
}
68-
// let's try to avoid duplicates
69-
let key = JSON.stringify(settings);
70-
if (runningScripts.has(key)) {
71-
let scriptRef = runningScripts.get(key);
72-
scriptRef.count++;
73-
return;
74-
}
75-
if (settings.allFrames) {
76-
// let's check whether the same script is registered for top frames:
77-
// if it is, let's unregister it first to avoid duplicates
78-
settings.allFrames = false;
79-
let topKey = JSON.stringify(settings);
80-
settings.allFrames = true;
81-
if (runningScripts.has(topKey)) {
82-
let topScript = runningScripts.get(topKey);
83-
try {
84-
topScript.unregister();
85-
} catch (e) {
86-
error(e);
87-
} finally {
88-
runningScripts.delete(topKey);
89-
}
90-
}
91-
}
9225

93-
let script = await browser.contentScripts.register(settings);
94-
debug("Content script %o registered.", settings);
95-
finalize = () => {
96-
debug("Finalizing content script %o...", settings);
26+
let filter = browser.webRequest.filterResponseData(requestId);
27+
let buffer = [];
28+
filter.onstart = async event => {
29+
filter.write(new Uint8Array());
30+
for (let details of scripts.values()) {
31+
details = Object.assign({
32+
runAt: "document_start",
33+
frameId,
34+
}, details);
9735
try {
98-
script.unregister();
99-
runningScripts.delete(key);
100-
debug("Content script %o unregistered!", settings);
101-
} finally {
102-
finalize = null;
36+
await browser.tabs.executeScript(tabId, details);
37+
debug("Execute on start OK", request.url, details);
38+
} catch (e) {
39+
error(e, "Execute on start failed", request.url, details);
10340
}
10441
}
105-
runningScripts.set(key, script);
106-
if (!cleanup) { // the request has already been interrupted
107-
finalize();
42+
if (buffer.length) {
43+
debug("Flushing %s buffer chunks", buffer.length);
44+
for (let chunk of buffer) {
45+
filter.write(chunk);
46+
}
47+
filter.disconnect();
48+
buffer = null;
10849
}
109-
110-
return;
111-
}
112-
113-
function listener(r) {
114-
if (r.requestId === requestId) {
115-
browser.tabs.executeScript(tabId, details);
116-
finalize();
117-
finalize = null;
50+
};
51+
filter.ondata = event => {
52+
if (buffer) {
53+
buffer.push(event.data);
54+
return;
11855
}
56+
filter.write(event.data);
57+
filter.disconnect();
11958
}
120-
finalize = () => {
121-
wr.onResponseStarted.removeListener(listener);
122-
}
123-
wr.onResponseStarted.addListener(listener, filter);
124-
debug("Executing %o", details);
125-
126-
},
127-
128-
59+
}
12960
}
13061
}

src/content/media.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
console.log("Media Hook", document.documentElement.innerHTML);
1+
debug("Media Hook (blocked %s)", !!window.mediaBlocker, document.URL, document.documentElement && document.documentElement.innerHTML);
22
try {
33
(() => {
44
let unpatched = new Map();
@@ -25,9 +25,6 @@ try {
2525
patch(window.MediaSource.prototype, "addSourceBuffer", function(mime, ...args) {
2626
let ms = this;
2727
let urls = urlMap.get(ms);
28-
let me = Array.from(document.querySelectorAll("video,audio"))
29-
.find(e => e.srcObject === ms || urls && urls.has(e.src));
30-
let exposedMime = `${mime} (MSE)`;
3128

3229
let request = {
3330
id: "noscript-media",
@@ -40,13 +37,21 @@ try {
4037
notifyPage();
4138

4239
if (window.mediaBlocker) {
43-
try {
44-
let ph = PlaceHolder.create("media", request);
45-
ph.replace(me);
46-
PlaceHolder.listen();
47-
} catch (e) {
48-
error(e);
49-
}
40+
(async () => {
41+
let me = Array.from(document.querySelectorAll("video,audio"))
42+
.find(e => e.srcObject === ms || urls && urls.has(e.src));
43+
44+
if (!me) return;
45+
let exposedMime = `${mime} (MSE)`;
46+
47+
try {
48+
let ph = PlaceHolder.create("media", request);
49+
ph.replace(me);
50+
PlaceHolder.listen();
51+
} catch (e) {
52+
error(e);
53+
}
54+
})();
5055
throw new Error(`${exposedMime} blocked by NoScript`);
5156
}
5257

src/content/webglHook.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
console.log("WebGL Hook", document.documentElement.innerHTML);
1+
console.log("WebGL Hook", document.URL, document.documentElement && document.documentElement.innerHTML);
22
try {
33
let proto = HTMLCanvasElement.prototype;
44
let getContext = proto.getContext;

src/lib/log.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
console.debug(`${PREFIX} ${msg}`, ...rest);
1010
}
1111
function error(e, msg, ...rest) {
12-
console.error(`${PREFIX} ${msg}`, e, e.message, e.stack);
12+
console.error(`${PREFIX} ${msg}`, ...rest, e, e.message, e.stack);
1313
}
1414
}

0 commit comments

Comments
 (0)