Skip to content

Commit 5c15f68

Browse files
committed
add workaround for Firefox's inability to redirect xhr to data: URI
1 parent 9bc029b commit 5c15f68

File tree

1 file changed

+92
-17
lines changed

1 file changed

+92
-17
lines changed

platform/webext/vapi-webrequest.js

Lines changed: 92 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ vAPI.net.registerListeners = function() {
4343
// https://github.com/gorhill/uBlock/issues/2950
4444
// Firefox 55 does not normalize URLs to ASCII, uBO must do this itself.
4545
// https://bugzilla.mozilla.org/show_bug.cgi?id=945240
46-
var mustPunycode = false;
46+
let mustPunycode = false;
4747
(function() {
4848
if (
4949
typeof browser === 'object' &&
@@ -58,10 +58,10 @@ vAPI.net.registerListeners = function() {
5858
}
5959
})();
6060

61-
var wrApi = browser.webRequest;
61+
let wrApi = browser.webRequest;
6262

6363
// legacy Chromium understands only these network request types.
64-
var validTypes = new Set([
64+
let validTypes = new Set([
6565
'image',
6666
'main_frame',
6767
'object',
@@ -80,14 +80,14 @@ vAPI.net.registerListeners = function() {
8080
}
8181
}
8282

83-
var denormalizeTypes = function(aa) {
83+
let denormalizeTypes = function(aa) {
8484
if ( aa.length === 0 ) {
8585
return Array.from(validTypes);
8686
}
87-
var out = new Set(),
87+
let out = new Set(),
8888
i = aa.length;
8989
while ( i-- ) {
90-
var type = aa[i];
90+
let type = aa[i];
9191
if ( validTypes.has(type) ) {
9292
out.add(type);
9393
}
@@ -98,12 +98,12 @@ vAPI.net.registerListeners = function() {
9898
return Array.from(out);
9999
};
100100

101-
var punycode = self.punycode;
102-
var reAsciiHostname = /^https?:\/\/[0-9a-z_.:@-]+[/?#]/;
103-
var reNetworkURI = /^(?:ftps?|https?|wss?)/;
104-
var parsedURL = new URL('about:blank');
101+
let punycode = self.punycode;
102+
let reAsciiHostname = /^https?:\/\/[0-9a-z_.:@-]+[/?#]/;
103+
let reNetworkURI = /^(?:ftps?|https?|wss?)/;
104+
let parsedURL = new URL('about:blank');
105105

106-
var normalizeRequestDetails = function(details) {
106+
let normalizeRequestDetails = function(details) {
107107
if (
108108
details.tabId === vAPI.noTabId &&
109109
reNetworkURI.test(details.documentUrl)
@@ -119,7 +119,7 @@ vAPI.net.registerListeners = function() {
119119
);
120120
}
121121

122-
var type = details.type;
122+
let type = details.type;
123123

124124
// https://github.com/gorhill/uBlock/issues/1493
125125
// Chromium 49+/WebExtensions support a new request type: `ping`,
@@ -135,10 +135,85 @@ vAPI.net.registerListeners = function() {
135135
}
136136
};
137137

138-
var onBeforeRequestClient = this.onBeforeRequest.callback;
139-
var onBeforeRequest = function(details) {
138+
// This is to work around Firefox's inability to redirect xmlhttprequest
139+
// to data: URI.
140+
let pseudoRedirector = {
141+
filters: new Map(),
142+
reDataURI: /^data:\w+\/\w+;base64,/,
143+
dec: null,
144+
init: function() {
145+
this.dec = new Uint8Array(128);
146+
let s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
147+
for ( let i = 0, n = s.length; i < n; i++ ) {
148+
this.dec[s.charCodeAt(i)] = i;
149+
}
150+
},
151+
start: function(requestId, redirectUrl) {
152+
if ( this.dec === null ) { this.init(); }
153+
let match = this.reDataURI.exec(redirectUrl);
154+
if ( match === null ) { return redirectUrl; }
155+
let s = redirectUrl.slice(match[0].length).replace(/=*$/, '');
156+
let f = browser.webRequest.filterResponseData(requestId);
157+
f.onstop = this.done;
158+
f.onerror = this.disconnect;
159+
this.filters.set(f, s);
160+
},
161+
done: function() {
162+
let pr = pseudoRedirector;
163+
let bufIn = pr.filters.get(this);
164+
if ( bufIn === undefined ) { return pr.disconnect(this); }
165+
let dec = pr.dec;
166+
let sizeIn = bufIn.length;
167+
let iIn = 0;
168+
let sizeOut = sizeIn * 6 >>> 3;
169+
let bufOut = new Uint8Array(sizeOut);
170+
let iOut = 0;
171+
let n = sizeIn & ~3;
172+
while ( iIn < n ) {
173+
let b0 = dec[bufIn.charCodeAt(iIn++)];
174+
let b1 = dec[bufIn.charCodeAt(iIn++)];
175+
let b2 = dec[bufIn.charCodeAt(iIn++)];
176+
let b3 = dec[bufIn.charCodeAt(iIn++)];
177+
bufOut[iOut++] = (b0 << 2) & 0xFC | (b1 >>> 4);
178+
bufOut[iOut++] = (b1 << 4) & 0xF0 | (b2 >>> 2);
179+
bufOut[iOut++] = (b2 << 6) & 0xC0 | b3;
180+
}
181+
if ( n < sizeIn ) {
182+
let b0 = dec[bufIn.charCodeAt(iIn++)];
183+
let b1 = dec[bufIn.charCodeAt(iIn++)];
184+
if ( (sizeIn & 3) === 2 ) {
185+
let b2 = dec[bufIn.charCodeAt(iIn++)];
186+
bufOut[iOut++] = (b0 << 2) & 0xFC | (b1 >>> 4);
187+
bufOut[iOut++] = (b1 << 4) & 0xF0 | (b2 >>> 2);
188+
} else {
189+
bufOut[iOut++] = (b0 << 2) & 0xFC | (b1 >>> 4);
190+
}
191+
}
192+
this.write(bufOut);
193+
pr.disconnect(this);
194+
},
195+
disconnect: function(f) {
196+
let pr = pseudoRedirector;
197+
pr.filters.delete(f);
198+
f.disconnect();
199+
}
200+
};
201+
202+
let onBeforeRequestClient = this.onBeforeRequest.callback;
203+
let onBeforeRequest = function(details) {
140204
normalizeRequestDetails(details);
141-
return onBeforeRequestClient(details);
205+
let r = onBeforeRequestClient(details);
206+
if (
207+
r !== undefined &&
208+
r.redirectUrl !== undefined &&
209+
details.type === 'xmlhttprequest'
210+
) {
211+
r.redirectUrl = pseudoRedirector.start(
212+
details.requestId,
213+
r.redirectUrl
214+
);
215+
}
216+
return r;
142217
};
143218

144219
if ( onBeforeRequest ) {
@@ -175,10 +250,10 @@ vAPI.net.registerListeners = function() {
175250
);
176251
}
177252

178-
var onHeadersReceivedClient = this.onHeadersReceived.callback,
253+
let onHeadersReceivedClient = this.onHeadersReceived.callback,
179254
onHeadersReceivedClientTypes = this.onHeadersReceived.types.slice(0),
180255
onHeadersReceivedTypes = denormalizeTypes(onHeadersReceivedClientTypes);
181-
var onHeadersReceived = function(details) {
256+
let onHeadersReceived = function(details) {
182257
normalizeRequestDetails(details);
183258
if (
184259
onHeadersReceivedClientTypes.length !== 0 &&

0 commit comments

Comments
 (0)