|
1 | 1 | 'use strict'; |
2 | 2 | { |
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); |
5 | 13 | var RequestUtil = { |
6 | 14 | async executeOnStart(request, details) { |
7 | 15 | 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; |
33 | 24 | } |
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 | | - } |
92 | 25 |
|
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); |
97 | 35 | 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); |
103 | 40 | } |
104 | 41 | } |
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; |
108 | 49 | } |
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; |
118 | 55 | } |
| 56 | + filter.write(event.data); |
| 57 | + filter.disconnect(); |
119 | 58 | } |
120 | | - finalize = () => { |
121 | | - wr.onResponseStarted.removeListener(listener); |
122 | | - } |
123 | | - wr.onResponseStarted.addListener(listener, filter); |
124 | | - debug("Executing %o", details); |
125 | | - |
126 | | - }, |
127 | | - |
128 | | - |
| 59 | + } |
129 | 60 | } |
130 | 61 | } |
0 commit comments