mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-10 01:02:08 +01:00
refactor some webRequest-related code (now that firefox legacy is out of the way)
This commit is contained in:
parent
5e08d083e0
commit
9039874fc9
12 changed files with 341 additions and 580 deletions
|
@ -620,7 +620,7 @@ vAPI.tabs.injectScript = function(tabId, details, callback) {
|
|||
// https://code.google.com/p/chromium/issues/detail?id=410868#c8
|
||||
void chrome.runtime.lastError;
|
||||
if ( typeof callback === 'function' ) {
|
||||
callback();
|
||||
callback.apply(null, arguments);
|
||||
}
|
||||
};
|
||||
if ( tabId ) {
|
||||
|
@ -1038,6 +1038,61 @@ vAPI.messaging.broadcast = function(message) {
|
|||
);
|
||||
})();
|
||||
|
||||
vAPI.net = {
|
||||
listenerMap: new WeakMap(),
|
||||
// legacy Chromium understands only these network request types.
|
||||
validTypes: (function() {
|
||||
let types = new Set([
|
||||
'main_frame',
|
||||
'sub_frame',
|
||||
'stylesheet',
|
||||
'script',
|
||||
'image',
|
||||
'object',
|
||||
'xmlhttprequest',
|
||||
'other'
|
||||
]);
|
||||
let wrrt = browser.webRequest.ResourceType;
|
||||
if ( wrrt instanceof Object ) {
|
||||
for ( let typeKey in wrrt ) {
|
||||
if ( wrrt.hasOwnProperty(typeKey) ) {
|
||||
types.add(wrrt[typeKey]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return types;
|
||||
})(),
|
||||
denormalizeFilters: null,
|
||||
normalizeDetails: null,
|
||||
addListener: function(which, clientListener, filters, options) {
|
||||
if ( typeof this.denormalizeFilters === 'function' ) {
|
||||
filters = this.denormalizeFilters(filters);
|
||||
}
|
||||
let actualListener;
|
||||
if ( typeof this.normalizeDetails === 'function' ) {
|
||||
actualListener = function(details) {
|
||||
vAPI.net.normalizeDetails(details);
|
||||
return clientListener(details);
|
||||
};
|
||||
this.listenerMap.set(clientListener, actualListener);
|
||||
}
|
||||
browser.webRequest[which].addListener(
|
||||
actualListener || clientListener,
|
||||
filters,
|
||||
options
|
||||
);
|
||||
},
|
||||
removeListener: function(which, clientListener) {
|
||||
let actualListener = this.listenerMap.get(clientListener);
|
||||
if ( actualListener !== undefined ) {
|
||||
this.listenerMap.delete(clientListener);
|
||||
}
|
||||
browser.webRequest[which].removeListener(
|
||||
actualListener || clientListener
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -1097,62 +1152,6 @@ vAPI.commands = chrome.commands;
|
|||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// This is called only once, when everything has been loaded in memory after
|
||||
// the extension was launched. It can be used to inject content scripts
|
||||
// in already opened web pages, to remove whatever nuisance could make it to
|
||||
// the web pages before uBlock was ready.
|
||||
|
||||
vAPI.onLoadAllCompleted = function() {
|
||||
// http://code.google.com/p/chromium/issues/detail?id=410868#c11
|
||||
// Need to be sure to access `vAPI.lastError()` to prevent
|
||||
// spurious warnings in the console.
|
||||
var onScriptInjected = function() {
|
||||
vAPI.lastError();
|
||||
};
|
||||
var scriptStart = function(tabId) {
|
||||
var manifest = chrome.runtime.getManifest();
|
||||
if ( manifest instanceof Object === false ) { return; }
|
||||
for ( var contentScript of manifest.content_scripts ) {
|
||||
for ( var file of contentScript.js ) {
|
||||
vAPI.tabs.injectScript(tabId, {
|
||||
file: file,
|
||||
allFrames: contentScript.all_frames,
|
||||
runAt: contentScript.run_at
|
||||
}, onScriptInjected);
|
||||
}
|
||||
}
|
||||
};
|
||||
var bindToTabs = function(tabs) {
|
||||
var µb = µBlock;
|
||||
var i = tabs.length, tab;
|
||||
while ( i-- ) {
|
||||
tab = tabs[i];
|
||||
µb.tabContextManager.commit(tab.id, tab.url);
|
||||
µb.bindTabToPageStats(tab.id);
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/129
|
||||
if ( /^https?:\/\//.test(tab.url) ) {
|
||||
scriptStart(tab.id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
chrome.tabs.query({ url: '<all_urls>' }, bindToTabs);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.punycodeHostname = function(hostname) {
|
||||
return hostname;
|
||||
};
|
||||
|
||||
vAPI.punycodeURL = function(url) {
|
||||
return url;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/531
|
||||
// Storage area dedicated to admin settings. Read-only.
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2017-2018 Raymond Hill
|
||||
Copyright (C) 2017-present Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -25,63 +25,24 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.net = {
|
||||
onBeforeRequest: {},
|
||||
onBeforeMaybeSpuriousCSPReport: {},
|
||||
onHeadersReceived: {},
|
||||
nativeCSPReportFiltering: false
|
||||
};
|
||||
|
||||
vAPI.net.registerListeners = function() {
|
||||
|
||||
var µb = µBlock,
|
||||
µburi = µb.URI,
|
||||
wrApi = chrome.webRequest;
|
||||
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=410382
|
||||
// Between Chromium 38-48, plug-ins' network requests were reported as
|
||||
// type "other" instead of "object".
|
||||
var is_v38_48 = /\bChrom[a-z]+\/(?:3[89]|4[0-8])\.[\d.]+\b/.test(navigator.userAgent);
|
||||
|
||||
// legacy Chromium understands only these network request types.
|
||||
var validTypes = {
|
||||
main_frame: true,
|
||||
sub_frame: true,
|
||||
stylesheet: true,
|
||||
script: true,
|
||||
image: true,
|
||||
object: true,
|
||||
xmlhttprequest: true,
|
||||
other: true
|
||||
};
|
||||
// modern Chromium/WebExtensions: more types available.
|
||||
if ( wrApi.ResourceType ) {
|
||||
(function() {
|
||||
for ( var typeKey in wrApi.ResourceType ) {
|
||||
if ( wrApi.ResourceType.hasOwnProperty(typeKey) ) {
|
||||
validTypes[wrApi.ResourceType[typeKey]] = true;
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
var extToTypeMap = new Map([
|
||||
(function() {
|
||||
let extToTypeMap = new Map([
|
||||
['eot','font'],['otf','font'],['svg','font'],['ttf','font'],['woff','font'],['woff2','font'],
|
||||
['mp3','media'],['mp4','media'],['webm','media'],
|
||||
['gif','image'],['ico','image'],['jpeg','image'],['jpg','image'],['png','image'],['webp','image']
|
||||
]);
|
||||
|
||||
var denormalizeTypes = function(aa) {
|
||||
let denormalizeTypes = function(aa) {
|
||||
if ( aa.length === 0 ) {
|
||||
return Object.keys(validTypes);
|
||||
return Array.from(vAPI.net.validTypes);
|
||||
}
|
||||
var out = [];
|
||||
var i = aa.length,
|
||||
let out = [];
|
||||
let i = aa.length,
|
||||
type,
|
||||
needOther = true;
|
||||
while ( i-- ) {
|
||||
type = aa[i];
|
||||
if ( validTypes[type] ) {
|
||||
if ( vAPI.net.validTypes.has(type) ) {
|
||||
out.push(type);
|
||||
}
|
||||
if ( type === 'other' ) {
|
||||
|
@ -94,7 +55,7 @@ vAPI.net.registerListeners = function() {
|
|||
return out;
|
||||
};
|
||||
|
||||
var headerValue = function(headers, name) {
|
||||
let headerValue = function(headers, name) {
|
||||
var i = headers.length;
|
||||
while ( i-- ) {
|
||||
if ( headers[i].name.toLowerCase() === name ) {
|
||||
|
@ -104,7 +65,9 @@ vAPI.net.registerListeners = function() {
|
|||
return '';
|
||||
};
|
||||
|
||||
var normalizeRequestDetails = function(details) {
|
||||
let parsedURL = new URL('https://www.example.org/');
|
||||
|
||||
vAPI.net.normalizeDetails = function(details) {
|
||||
// Chromium 63+ supports the `initiator` property, which contains
|
||||
// the URL of the origin from which the network request was made.
|
||||
if (
|
||||
|
@ -116,7 +79,7 @@ vAPI.net.registerListeners = function() {
|
|||
details.documentUrl = details.initiator;
|
||||
}
|
||||
|
||||
var type = details.type;
|
||||
let type = details.type;
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/1493
|
||||
// Chromium 49+/WebExtensions support a new request type: `ping`,
|
||||
|
@ -137,7 +100,8 @@ vAPI.net.registerListeners = function() {
|
|||
}
|
||||
|
||||
// Try to map known "extension" part of URL to request type.
|
||||
var path = µburi.pathFromURI(details.url),
|
||||
parsedURL.href = details.url;
|
||||
let path = parsedURL.pathname,
|
||||
pos = path.indexOf('.', path.length - 6);
|
||||
if ( pos !== -1 && (type = extToTypeMap.get(path.slice(pos + 1))) ) {
|
||||
details.type = type;
|
||||
|
@ -160,58 +124,16 @@ vAPI.net.registerListeners = function() {
|
|||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/862
|
||||
// If no transposition possible, transpose to `object` as per
|
||||
// Chromium bug 410382
|
||||
// https://code.google.com/p/chromium/issues/detail?id=410382
|
||||
if ( is_v38_48 ) {
|
||||
details.type = 'object';
|
||||
vAPI.net.denormalizeFilters = function(filters) {
|
||||
let urls = filters.urls || [ '<all_urls>' ];
|
||||
let types = filters.types;
|
||||
if ( Array.isArray(types) ) {
|
||||
types = denormalizeTypes(types);
|
||||
}
|
||||
};
|
||||
|
||||
var onBeforeRequestClient = this.onBeforeRequest.callback;
|
||||
var onBeforeRequest = function(details) {
|
||||
normalizeRequestDetails(details);
|
||||
return onBeforeRequestClient(details);
|
||||
};
|
||||
|
||||
// This is needed for Chromium 49-55.
|
||||
var onBeforeSendHeaders = validTypes.csp_report
|
||||
// modern Chromium/WebExtensions: type 'csp_report' is supported
|
||||
? null
|
||||
// legacy Chromium
|
||||
: function(details) {
|
||||
if ( details.type !== 'ping' || details.method !== 'POST' ) { return; }
|
||||
var type = headerValue(details.requestHeaders, 'content-type');
|
||||
if ( type === '' ) { return; }
|
||||
if ( type.endsWith('/csp-report') ) {
|
||||
details.type = 'csp_report';
|
||||
return onBeforeRequestClient(details);
|
||||
}
|
||||
};
|
||||
|
||||
var onHeadersReceivedClient = this.onHeadersReceived.callback,
|
||||
onHeadersReceivedClientTypes = this.onHeadersReceived.types.slice(0),
|
||||
onHeadersReceivedTypes = denormalizeTypes(onHeadersReceivedClientTypes);
|
||||
var onHeadersReceived = function(details) {
|
||||
normalizeRequestDetails(details);
|
||||
if (
|
||||
onHeadersReceivedClientTypes.length !== 0 &&
|
||||
onHeadersReceivedClientTypes.indexOf(details.type) === -1
|
||||
) {
|
||||
return;
|
||||
}
|
||||
return onHeadersReceivedClient(details);
|
||||
};
|
||||
|
||||
var urls, types;
|
||||
|
||||
if ( onBeforeRequest ) {
|
||||
urls = this.onBeforeRequest.urls || ['<all_urls>'];
|
||||
types = this.onBeforeRequest.types || undefined;
|
||||
if (
|
||||
(validTypes.websocket) &&
|
||||
(vAPI.net.validTypes.has('websocket')) &&
|
||||
(types === undefined || types.indexOf('websocket') !== -1) &&
|
||||
(urls.indexOf('<all_urls>') === -1)
|
||||
) {
|
||||
|
@ -222,51 +144,8 @@ vAPI.net.registerListeners = function() {
|
|||
urls.push('wss://*/*');
|
||||
}
|
||||
}
|
||||
wrApi.onBeforeRequest.addListener(
|
||||
onBeforeRequest,
|
||||
{ urls: urls, types: types },
|
||||
this.onBeforeRequest.extra
|
||||
);
|
||||
}
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/3140
|
||||
this.nativeCSPReportFiltering = validTypes.csp_report;
|
||||
if (
|
||||
this.nativeCSPReportFiltering &&
|
||||
typeof this.onBeforeMaybeSpuriousCSPReport.callback === 'function'
|
||||
) {
|
||||
wrApi.onBeforeRequest.addListener(
|
||||
this.onBeforeMaybeSpuriousCSPReport.callback,
|
||||
{
|
||||
urls: [ 'http://*/*', 'https://*/*' ],
|
||||
types: [ 'csp_report' ]
|
||||
},
|
||||
[ 'blocking', 'requestBody' ]
|
||||
);
|
||||
}
|
||||
|
||||
// Chromium 48 and lower does not support `ping` type.
|
||||
// Chromium 56 and higher does support `csp_report` stype.
|
||||
if ( onBeforeSendHeaders ) {
|
||||
wrApi.onBeforeSendHeaders.addListener(
|
||||
onBeforeSendHeaders,
|
||||
{
|
||||
'urls': [ '<all_urls>' ],
|
||||
'types': [ 'ping' ]
|
||||
},
|
||||
[ 'blocking', 'requestHeaders' ]
|
||||
);
|
||||
}
|
||||
|
||||
if ( onHeadersReceived ) {
|
||||
urls = this.onHeadersReceived.urls || ['<all_urls>'];
|
||||
types = onHeadersReceivedTypes;
|
||||
wrApi.onHeadersReceived.addListener(
|
||||
onHeadersReceived,
|
||||
{ urls: urls, types: types },
|
||||
this.onHeadersReceived.extra
|
||||
);
|
||||
}
|
||||
};
|
||||
return { types, urls };
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2017-2018 The uBlock Origin authors
|
||||
Copyright (C) 2017-present The uBlock Origin authors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -27,6 +27,14 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
if ( self.browser instanceof Object ) {
|
||||
self.chrome = self.browser;
|
||||
} else {
|
||||
self.browser = self.chrome;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1408996#c9
|
||||
var vAPI = window.vAPI; // jshint ignore:line
|
||||
|
||||
|
|
|
@ -25,20 +25,7 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.net = {
|
||||
onBeforeRequest: {},
|
||||
onBeforeMaybeSpuriousCSPReport: {},
|
||||
onHeadersReceived: {},
|
||||
nativeCSPReportFiltering: true,
|
||||
webRequest: browser.webRequest,
|
||||
canFilterResponseBody:
|
||||
typeof browser.webRequest === 'object' &&
|
||||
typeof browser.webRequest.filterResponseData === 'function'
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.net.registerListeners = function() {
|
||||
(function() {
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/2950
|
||||
// Firefox 56 does not normalize URLs to ASCII, uBO must do this itself.
|
||||
|
@ -56,40 +43,18 @@ vAPI.net.registerListeners = function() {
|
|||
mustPunycode = evalMustPunycode();
|
||||
}, { once: true });
|
||||
|
||||
let wrApi = browser.webRequest;
|
||||
|
||||
// legacy Chromium understands only these network request types.
|
||||
let validTypes = new Set([
|
||||
'image',
|
||||
'main_frame',
|
||||
'object',
|
||||
'other',
|
||||
'script',
|
||||
'stylesheet',
|
||||
'sub_frame',
|
||||
'xmlhttprequest',
|
||||
]);
|
||||
// modern Chromium/WebExtensions: more types available.
|
||||
if ( wrApi.ResourceType ) {
|
||||
for ( let typeKey in wrApi.ResourceType ) {
|
||||
if ( wrApi.ResourceType.hasOwnProperty(typeKey) ) {
|
||||
validTypes.add(wrApi.ResourceType[typeKey]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let denormalizeTypes = function(aa) {
|
||||
if ( aa.length === 0 ) {
|
||||
return Array.from(validTypes);
|
||||
return Array.from(vAPI.net.validTypes);
|
||||
}
|
||||
let out = new Set(),
|
||||
i = aa.length;
|
||||
while ( i-- ) {
|
||||
let type = aa[i];
|
||||
if ( validTypes.has(type) ) {
|
||||
if ( vAPI.net.validTypes.has(type) ) {
|
||||
out.add(type);
|
||||
}
|
||||
if ( type === 'image' && validTypes.has('imageset') ) {
|
||||
if ( type === 'image' && vAPI.net.validTypes.has('imageset') ) {
|
||||
out.add('imageset');
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +65,7 @@ vAPI.net.registerListeners = function() {
|
|||
let reAsciiHostname = /^https?:\/\/[0-9a-z_.:@-]+[/?#]/;
|
||||
let parsedURL = new URL('about:blank');
|
||||
|
||||
let normalizeRequestDetails = function(details) {
|
||||
vAPI.net.normalizeDetails = function(details) {
|
||||
if (
|
||||
details.tabId === vAPI.noTabId &&
|
||||
typeof details.documentUrl === 'string'
|
||||
|
@ -132,79 +97,14 @@ vAPI.net.registerListeners = function() {
|
|||
}
|
||||
};
|
||||
|
||||
// This is to work around Firefox's inability to redirect xmlhttprequest
|
||||
// requests to data: URIs.
|
||||
let pseudoRedirector = {
|
||||
filters: new Map(),
|
||||
reDataURI: /^data:\w+\/\w+;base64,/,
|
||||
dec: null,
|
||||
init: function() {
|
||||
this.dec = new Uint8Array(128);
|
||||
let s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
for ( let i = 0, n = s.length; i < n; i++ ) {
|
||||
this.dec[s.charCodeAt(i)] = i;
|
||||
}
|
||||
return this.dec;
|
||||
},
|
||||
start: function(requestId, redirectUrl) {
|
||||
let match = this.reDataURI.exec(redirectUrl);
|
||||
if ( match === null ) { return redirectUrl; }
|
||||
let s = redirectUrl.slice(match[0].length).replace(/=*$/, '');
|
||||
let f = browser.webRequest.filterResponseData(requestId);
|
||||
f.onstop = this.done;
|
||||
f.onerror = this.disconnect;
|
||||
this.filters.set(f, s);
|
||||
},
|
||||
done: function() {
|
||||
let pr = pseudoRedirector;
|
||||
let bufIn = pr.filters.get(this);
|
||||
if ( bufIn === undefined ) { return pr.disconnect(this); }
|
||||
let dec = pr.dec || pr.init();
|
||||
let sizeIn = bufIn.length;
|
||||
let iIn = 0;
|
||||
let sizeOut = sizeIn * 6 >>> 3;
|
||||
let bufOut = new Uint8Array(sizeOut);
|
||||
let iOut = 0;
|
||||
let n = sizeIn & ~3;
|
||||
while ( iIn < n ) {
|
||||
let b0 = dec[bufIn.charCodeAt(iIn++)];
|
||||
let b1 = dec[bufIn.charCodeAt(iIn++)];
|
||||
let b2 = dec[bufIn.charCodeAt(iIn++)];
|
||||
let b3 = dec[bufIn.charCodeAt(iIn++)];
|
||||
bufOut[iOut++] = (b0 << 2) & 0xFC | (b1 >>> 4);
|
||||
bufOut[iOut++] = (b1 << 4) & 0xF0 | (b2 >>> 2);
|
||||
bufOut[iOut++] = (b2 << 6) & 0xC0 | b3;
|
||||
}
|
||||
if ( iIn !== sizeIn ) {
|
||||
let b0 = dec[bufIn.charCodeAt(iIn++)];
|
||||
let b1 = dec[bufIn.charCodeAt(iIn++)];
|
||||
bufOut[iOut++] = (b0 << 2) & 0xFC | (b1 >>> 4);
|
||||
if ( iIn !== sizeIn ) {
|
||||
let b2 = dec[bufIn.charCodeAt(iIn++)];
|
||||
bufOut[iOut++] = (b1 << 4) & 0xF0 | (b2 >>> 2);
|
||||
}
|
||||
}
|
||||
this.write(bufOut);
|
||||
pr.disconnect(this);
|
||||
},
|
||||
disconnect: function(f) {
|
||||
let pr = pseudoRedirector;
|
||||
pr.filters.delete(f);
|
||||
f.disconnect();
|
||||
vAPI.net.denormalizeFilters = function(filters) {
|
||||
let urls = filters.urls || [ '<all_urls>' ];
|
||||
let types = filters.types;
|
||||
if ( Array.isArray(types) ) {
|
||||
types = denormalizeTypes(types);
|
||||
}
|
||||
};
|
||||
|
||||
let onBeforeRequestClient = this.onBeforeRequest.callback;
|
||||
let onBeforeRequest = function(details) {
|
||||
normalizeRequestDetails(details);
|
||||
return onBeforeRequestClient(details);
|
||||
};
|
||||
|
||||
if ( onBeforeRequest ) {
|
||||
let urls = this.onBeforeRequest.urls || ['<all_urls>'];
|
||||
let types = this.onBeforeRequest.types || undefined;
|
||||
if (
|
||||
(validTypes.has('websocket')) &&
|
||||
(vAPI.net.validTypes.has('websocket')) &&
|
||||
(types === undefined || types.indexOf('websocket') !== -1) &&
|
||||
(urls.indexOf('<all_urls>') === -1)
|
||||
) {
|
||||
|
@ -215,48 +115,8 @@ vAPI.net.registerListeners = function() {
|
|||
urls.push('wss://*/*');
|
||||
}
|
||||
}
|
||||
wrApi.onBeforeRequest.addListener(
|
||||
onBeforeRequest,
|
||||
{ urls: urls, types: types },
|
||||
this.onBeforeRequest.extra
|
||||
);
|
||||
}
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/3140
|
||||
if ( typeof this.onBeforeMaybeSpuriousCSPReport.callback === 'function' ) {
|
||||
wrApi.onBeforeRequest.addListener(
|
||||
this.onBeforeMaybeSpuriousCSPReport.callback,
|
||||
{
|
||||
urls: [ 'http://*/*', 'https://*/*' ],
|
||||
types: [ 'csp_report' ]
|
||||
},
|
||||
[ 'blocking', 'requestBody' ]
|
||||
);
|
||||
}
|
||||
|
||||
let onHeadersReceivedClient = this.onHeadersReceived.callback,
|
||||
onHeadersReceivedClientTypes = this.onHeadersReceived.types.slice(0),
|
||||
onHeadersReceivedTypes = denormalizeTypes(onHeadersReceivedClientTypes);
|
||||
let onHeadersReceived = function(details) {
|
||||
normalizeRequestDetails(details);
|
||||
if (
|
||||
onHeadersReceivedClientTypes.length !== 0 &&
|
||||
onHeadersReceivedClientTypes.indexOf(details.type) === -1
|
||||
) {
|
||||
return;
|
||||
}
|
||||
return onHeadersReceivedClient(details);
|
||||
return { types, urls };
|
||||
};
|
||||
|
||||
if ( onHeadersReceived ) {
|
||||
let urls = this.onHeadersReceived.urls || ['<all_urls>'];
|
||||
let types = onHeadersReceivedTypes;
|
||||
wrApi.onHeadersReceived.addListener(
|
||||
onHeadersReceived,
|
||||
{ urls: urls, types: types },
|
||||
this.onHeadersReceived.extra
|
||||
);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
<script src="js/vapi-background.js"></script>
|
||||
<script src="js/vapi-webrequest.js"></script><!-- Forks can pick the webext, chromium, or their own implementation -->
|
||||
<script src="js/background.js"></script>
|
||||
<script src="js/traffic.js"></script>
|
||||
<script src="js/hntrie.js"></script>
|
||||
<script src="js/utils.js"></script>
|
||||
<script src="js/uritools.js"></script>
|
||||
|
@ -34,7 +35,6 @@
|
|||
<script src="js/logger.js"></script>
|
||||
<script src="js/pagestore.js"></script>
|
||||
<script src="js/tab.js"></script>
|
||||
<script src="js/traffic.js"></script>
|
||||
<script src="js/text-encode.js"></script>
|
||||
<script src="js/contextmenu.js"></script>
|
||||
<script src="js/reverselookup.js"></script>
|
||||
|
|
|
@ -63,17 +63,10 @@ var µBlock = (function() { // jshint ignore:line
|
|||
'vivaldi-scheme',
|
||||
'wyciwyg-scheme', // Firefox's "What-You-Cache-Is-What-You-Get"
|
||||
];
|
||||
// https://github.com/gorhill/uBlock/issues/3693#issuecomment-379782428
|
||||
if ( vAPI.webextFlavor.soup.has('webext') === false ) {
|
||||
whitelistDefault.push('behind-the-scene');
|
||||
}
|
||||
|
||||
return {
|
||||
firstInstall: false,
|
||||
|
||||
onBeforeStartQueue: [],
|
||||
onStartCompletedQueue: [],
|
||||
|
||||
userSettings: {
|
||||
advancedUserEnabled: false,
|
||||
alwaysDetachLogger: true,
|
||||
|
@ -98,13 +91,13 @@ var µBlock = (function() { // jshint ignore:line
|
|||
|
||||
hiddenSettingsDefault: hiddenSettingsDefault,
|
||||
hiddenSettings: (function() {
|
||||
var out = Object.assign({}, hiddenSettingsDefault),
|
||||
let out = Object.assign({}, hiddenSettingsDefault),
|
||||
json = vAPI.localStorage.getItem('immediateHiddenSettings');
|
||||
if ( typeof json === 'string' ) {
|
||||
try {
|
||||
var o = JSON.parse(json);
|
||||
let o = JSON.parse(json);
|
||||
if ( o instanceof Object ) {
|
||||
for ( var k in o ) {
|
||||
for ( let k in o ) {
|
||||
if ( out.hasOwnProperty(k) ) {
|
||||
out[k] = o[k];
|
||||
}
|
||||
|
@ -122,7 +115,7 @@ var µBlock = (function() { // jshint ignore:line
|
|||
// Features detection.
|
||||
privacySettingsSupported: vAPI.browserSettings instanceof Object,
|
||||
cloudStorageSupported: vAPI.cloud instanceof Object,
|
||||
canFilterResponseBody: vAPI.net.canFilterResponseBody === true,
|
||||
canFilterResponseData: typeof browser.webRequest.filterResponseData === 'function',
|
||||
canInjectScriptletsNow: vAPI.webextFlavor.soup.has('chromium'),
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/180
|
||||
|
|
|
@ -254,7 +254,7 @@
|
|||
|
||||
api.fromCompiledContent = function(reader) {
|
||||
// Don't bother loading filters if stream filtering is not supported.
|
||||
if ( µb.canFilterResponseBody === false ) { return; }
|
||||
if ( µb.canFilterResponseData === false ) { return; }
|
||||
|
||||
// 1002 = html filtering
|
||||
reader.select(1002);
|
||||
|
|
|
@ -683,22 +683,6 @@ PageStore.prototype.filterCSPReport = function(context) {
|
|||
}
|
||||
return 1;
|
||||
}
|
||||
// https://github.com/gorhill/uBlock/issues/3140
|
||||
// Special handling of CSP reports if and only if these can't be filtered
|
||||
// natively.
|
||||
if (
|
||||
vAPI.net.nativeCSPReportFiltering !== true &&
|
||||
this.internalRedirectionCount !== 0
|
||||
) {
|
||||
if ( µb.logger.isEnabled() ) {
|
||||
this.logData = {
|
||||
result: 1,
|
||||
source: 'global',
|
||||
raw: 'no-spurious-csp-report'
|
||||
};
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
|
33
src/js/scriptlets/should-inject-contentscript.js
Normal file
33
src/js/scriptlets/should-inject-contentscript.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2018-present Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// If content scripts are already injected, we need to respond with `false`,
|
||||
// to "should inject content scripts?"
|
||||
|
||||
(function() {
|
||||
try {
|
||||
return vAPI.uBO !== true;
|
||||
} catch(ex) {
|
||||
}
|
||||
return true;
|
||||
})();
|
|
@ -48,25 +48,14 @@ vAPI.app.onShutdown = function() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
var processCallbackQueue = function(queue, callback) {
|
||||
var processOne = function() {
|
||||
var fn = queue.pop();
|
||||
if ( fn ) {
|
||||
fn(processOne);
|
||||
} else if ( typeof callback === 'function' ) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
processOne();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Final initialization steps after all needed assets are in memory.
|
||||
// - Initialize internal state with maybe already existing tabs.
|
||||
// - Schedule next update operation.
|
||||
|
||||
var onAllReady = function() {
|
||||
µb.webRequest.start();
|
||||
initializeTabs();
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/184
|
||||
// Check for updates not too far in the future.
|
||||
µb.assets.addObserver(µb.assetObserver.bind(µb));
|
||||
|
@ -84,8 +73,55 @@ var onAllReady = function() {
|
|||
|
||||
µb.contextMenu.update(null);
|
||||
µb.firstInstall = false;
|
||||
};
|
||||
|
||||
processCallbackQueue(µb.onStartCompletedQueue);
|
||||
/******************************************************************************/
|
||||
|
||||
// This is called only once, when everything has been loaded in memory after
|
||||
// the extension was launched. It can be used to inject content scripts
|
||||
// in already opened web pages, to remove whatever nuisance could make it to
|
||||
// the web pages before uBlock was ready.
|
||||
|
||||
let initializeTabs = function() {
|
||||
let handleScriptResponse = function(tabId, results) {
|
||||
if (
|
||||
Array.isArray(results) === false ||
|
||||
results.length === 0 ||
|
||||
results[0] !== true
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Inject dclarative content scripts programmatically.
|
||||
let manifest = chrome.runtime.getManifest();
|
||||
if ( manifest instanceof Object === false ) { return; }
|
||||
for ( let contentScript of manifest.content_scripts ) {
|
||||
for ( let file of contentScript.js ) {
|
||||
vAPI.tabs.injectScript(tabId, {
|
||||
file: file,
|
||||
allFrames: contentScript.all_frames,
|
||||
runAt: contentScript.run_at
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
let bindToTabs = function(tabs) {
|
||||
for ( let tab of tabs ) {
|
||||
µb.tabContextManager.commit(tab.id, tab.url);
|
||||
µb.bindTabToPageStats(tab.id);
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/129
|
||||
// Find out whether content scripts need to be injected
|
||||
// programmatically. This may be necessary for web pages which
|
||||
// were loaded before uBO launched.
|
||||
if ( /^https?:\/\//.test(tab.url) === false ) { continue; }
|
||||
vAPI.tabs.injectScript(
|
||||
tab.id,
|
||||
{ file: 'js/scriptlets/should-inject-contentscript.js' },
|
||||
handleScriptResponse.bind(null, tab.id)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
browser.tabs.query({ url: '<all_urls>' }, bindToTabs);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -329,10 +365,8 @@ var onAdminSettingsRestored = function() {
|
|||
/******************************************************************************/
|
||||
|
||||
return function() {
|
||||
processCallbackQueue(µb.onBeforeStartQueue, function() {
|
||||
// https://github.com/gorhill/uBlock/issues/531
|
||||
µb.restoreAdminSettings(onAdminSettingsRestored);
|
||||
});
|
||||
// https://github.com/gorhill/uBlock/issues/531
|
||||
µb.restoreAdminSettings(onAdminSettingsRestored);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
µBlock.textEncode = (function() {
|
||||
|
||||
if ( µBlock.canFilterResponseBody !== true ) { return; }
|
||||
if ( µBlock.canFilterResponseData !== true ) { return; }
|
||||
|
||||
// charset aliases extracted from:
|
||||
// https://github.com/inexorabletash/text-encoding/blob/b4e5bc26e26e51f56e3daa9f13138c79f49d3c34/lib/encoding.js#L342
|
||||
|
|
|
@ -29,10 +29,6 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
var exports = {};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Platform-specific behavior.
|
||||
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/42
|
||||
|
@ -58,55 +54,9 @@ window.addEventListener('webextFlavor', function() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/2067
|
||||
// Experimental: Block everything until uBO is fully ready.
|
||||
// TODO: re-work vAPI code to match more closely how listeners are
|
||||
// registered with the webRequest API. This will simplify implementing
|
||||
// the feature here: we could have a temporary onBeforeRequest listener
|
||||
// which blocks everything until all is ready.
|
||||
// This would allow to avoid the permanent special test at the top of
|
||||
// the main onBeforeRequest just to implement this.
|
||||
// https://github.com/gorhill/uBlock/issues/3130
|
||||
// Don't block root frame.
|
||||
|
||||
var onBeforeReady = null;
|
||||
|
||||
µBlock.onStartCompletedQueue.push(function(callback) {
|
||||
vAPI.onLoadAllCompleted();
|
||||
callback();
|
||||
});
|
||||
|
||||
if ( µBlock.hiddenSettings.suspendTabsUntilReady ) {
|
||||
onBeforeReady = (function() {
|
||||
var suspendedTabs = new Set();
|
||||
µBlock.onStartCompletedQueue.push(function(callback) {
|
||||
onBeforeReady = null;
|
||||
for ( var tabId of suspendedTabs ) {
|
||||
vAPI.tabs.reload(tabId);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
return function(details) {
|
||||
if (
|
||||
details.type !== 'main_frame' &&
|
||||
vAPI.isBehindTheSceneTabId(details.tabId) === false
|
||||
) {
|
||||
suspendedTabs.add(details.tabId);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
})();
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Intercept and filter web requests.
|
||||
|
||||
var onBeforeRequest = function(details) {
|
||||
if ( onBeforeReady !== null && onBeforeReady(details) ) {
|
||||
return { cancel: true };
|
||||
}
|
||||
|
||||
// Special handling for root document.
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/1001
|
||||
// This must be executed regardless of whether the request is
|
||||
|
@ -444,7 +394,7 @@ var onBeforeBehindTheSceneRequest = function(details) {
|
|||
|
||||
// Blocked?
|
||||
if ( result === 1 ) {
|
||||
return { 'cancel': true };
|
||||
return { cancel: true };
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -452,91 +402,93 @@ var onBeforeBehindTheSceneRequest = function(details) {
|
|||
|
||||
// https://github.com/gorhill/uBlock/issues/3140
|
||||
|
||||
var onBeforeMaybeSpuriousCSPReport = function(details) {
|
||||
var tabId = details.tabId;
|
||||
let onBeforeMaybeSpuriousCSPReport = (function() {
|
||||
let textDecoder;
|
||||
|
||||
// Ignore behind-the-scene requests.
|
||||
if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
|
||||
return function(details) {
|
||||
let tabId = details.tabId;
|
||||
|
||||
// Lookup the page store associated with this tab id.
|
||||
var µb = µBlock,
|
||||
pageStore = µb.pageStoreFromTabId(tabId);
|
||||
if ( pageStore === null ) { return; }
|
||||
// Ignore behind-the-scene requests.
|
||||
if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
|
||||
|
||||
// If uBO is disabled for the page, it can't possibly causes CSP reports
|
||||
// to be triggered.
|
||||
if ( pageStore.getNetFilteringSwitch() === false ) { return; }
|
||||
// Lookup the page store associated with this tab id.
|
||||
let µb = µBlock,
|
||||
pageStore = µb.pageStoreFromTabId(tabId);
|
||||
if ( pageStore === null ) { return; }
|
||||
|
||||
// A resource was redirected to a neutered one?
|
||||
// TODO: mind injected scripts/styles as well.
|
||||
if ( pageStore.internalRedirectionCount === 0 ) { return; }
|
||||
// If uBO is disabled for the page, it can't possibly causes CSP
|
||||
// reports to be triggered.
|
||||
if ( pageStore.getNetFilteringSwitch() === false ) { return; }
|
||||
|
||||
var textDecoder = onBeforeMaybeSpuriousCSPReport.textDecoder;
|
||||
if (
|
||||
textDecoder === undefined &&
|
||||
typeof self.TextDecoder === 'function'
|
||||
) {
|
||||
textDecoder =
|
||||
onBeforeMaybeSpuriousCSPReport.textDecoder = new TextDecoder();
|
||||
}
|
||||
// A resource was redirected to a neutered one?
|
||||
// TODO: mind injected scripts/styles as well.
|
||||
if ( pageStore.internalRedirectionCount === 0 ) { return; }
|
||||
|
||||
// Find out whether the CSP report is a potentially spurious CSP report.
|
||||
// If from this point on we are unable to parse the CSP report data, the
|
||||
// safest assumption to protect users is to assume the CSP report is
|
||||
// spurious.
|
||||
if (
|
||||
textDecoder !== undefined &&
|
||||
details.method === 'POST'
|
||||
) {
|
||||
var raw = details.requestBody && details.requestBody.raw;
|
||||
if (
|
||||
Array.isArray(raw) &&
|
||||
raw.length !== 0 &&
|
||||
raw[0] instanceof Object &&
|
||||
raw[0].bytes instanceof ArrayBuffer
|
||||
textDecoder === undefined &&
|
||||
typeof self.TextDecoder === 'function'
|
||||
) {
|
||||
var data;
|
||||
try {
|
||||
data = JSON.parse(textDecoder.decode(raw[0].bytes));
|
||||
} catch (ex) {
|
||||
}
|
||||
if ( data instanceof Object ) {
|
||||
var report = data['csp-report'];
|
||||
if ( report instanceof Object ) {
|
||||
var blocked = report['blocked-uri'] || report['blockedURI'],
|
||||
validBlocked = typeof blocked === 'string',
|
||||
source = report['source-file'] || report['sourceFile'],
|
||||
validSource = typeof source === 'string';
|
||||
if (
|
||||
(validBlocked || validSource) &&
|
||||
(!validBlocked || !blocked.startsWith('data')) &&
|
||||
(!validSource || !source.startsWith('data'))
|
||||
) {
|
||||
return;
|
||||
textDecoder = new TextDecoder();
|
||||
}
|
||||
|
||||
// Find out whether the CSP report is a potentially spurious CSP report.
|
||||
// If from this point on we are unable to parse the CSP report data,
|
||||
// the safest assumption to protect users is to assume the CSP report
|
||||
// is spurious.
|
||||
if (
|
||||
textDecoder !== undefined &&
|
||||
details.method === 'POST'
|
||||
) {
|
||||
let raw = details.requestBody && details.requestBody.raw;
|
||||
if (
|
||||
Array.isArray(raw) &&
|
||||
raw.length !== 0 &&
|
||||
raw[0] instanceof Object &&
|
||||
raw[0].bytes instanceof ArrayBuffer
|
||||
) {
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(textDecoder.decode(raw[0].bytes));
|
||||
} catch (ex) {
|
||||
}
|
||||
if ( data instanceof Object ) {
|
||||
let report = data['csp-report'];
|
||||
if ( report instanceof Object ) {
|
||||
let blocked =
|
||||
report['blocked-uri'] || report['blockedURI'],
|
||||
validBlocked = typeof blocked === 'string',
|
||||
source =
|
||||
report['source-file'] || report['sourceFile'],
|
||||
validSource = typeof source === 'string';
|
||||
if (
|
||||
(validBlocked || validSource) &&
|
||||
(!validBlocked || !blocked.startsWith('data')) &&
|
||||
(!validSource || !source.startsWith('data'))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Potentially spurious CSP report.
|
||||
if ( µb.logger.isEnabled() ) {
|
||||
var hostname = µb.URI.hostnameFromURI(details.url);
|
||||
µb.logger.writeOne(
|
||||
tabId,
|
||||
'net',
|
||||
{ result: 1, source: 'global', raw: 'no-spurious-csp-report' },
|
||||
'csp_report',
|
||||
details.url,
|
||||
hostname,
|
||||
hostname
|
||||
);
|
||||
}
|
||||
// Potentially spurious CSP report.
|
||||
if ( µb.logger.isEnabled() ) {
|
||||
let hostname = µb.URI.hostnameFromURI(details.url);
|
||||
µb.logger.writeOne(
|
||||
tabId,
|
||||
'net',
|
||||
{ result: 1, source: 'global', raw: 'no-spurious-csp-report' },
|
||||
'csp_report',
|
||||
details.url,
|
||||
hostname,
|
||||
hostname
|
||||
);
|
||||
}
|
||||
|
||||
return { cancel: true };
|
||||
};
|
||||
|
||||
onBeforeMaybeSpuriousCSPReport.textDecoder = undefined;
|
||||
return { cancel: true };
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -592,7 +544,7 @@ var onHeadersReceived = function(details) {
|
|||
|
||||
// At this point we have a HTML document.
|
||||
|
||||
let filteredHTML = µb.canFilterResponseBody &&
|
||||
let filteredHTML = µb.canFilterResponseData &&
|
||||
filterDocument(pageStore, details) === true;
|
||||
|
||||
let modifiedHeaders = injectCSP(pageStore, details) === true;
|
||||
|
@ -880,7 +832,7 @@ var filterDocument = (function() {
|
|||
if ( headerValueFromName('content-disposition', headers) ) { return; }
|
||||
|
||||
var stream = request.stream =
|
||||
vAPI.net.webRequest.filterResponseData(details.requestId);
|
||||
browser.webRequest.filterResponseData(details.requestId);
|
||||
stream.ondata = onStreamData;
|
||||
stream.onstop = onStreamStop;
|
||||
stream.onerror = onStreamError;
|
||||
|
@ -1122,77 +1074,96 @@ var headerValueFromName = function(headerName, headers) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.net.onBeforeRequest = {
|
||||
urls: [
|
||||
'http://*/*',
|
||||
'https://*/*'
|
||||
],
|
||||
extra: [ 'blocking' ],
|
||||
callback: onBeforeRequest
|
||||
};
|
||||
|
||||
vAPI.net.onBeforeMaybeSpuriousCSPReport = {
|
||||
callback: onBeforeMaybeSpuriousCSPReport
|
||||
};
|
||||
|
||||
vAPI.net.onHeadersReceived = {
|
||||
urls: [
|
||||
'http://*/*',
|
||||
'https://*/*'
|
||||
],
|
||||
types: [
|
||||
'main_frame',
|
||||
'sub_frame',
|
||||
'image',
|
||||
'media'
|
||||
],
|
||||
extra: [ 'blocking', 'responseHeaders' ],
|
||||
callback: onHeadersReceived
|
||||
};
|
||||
|
||||
vAPI.net.registerListeners();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var isTemporarilyWhitelisted = function(result, hostname) {
|
||||
var obsolete, pos;
|
||||
|
||||
for (;;) {
|
||||
obsolete = documentWhitelists[hostname];
|
||||
let obsolete = documentWhitelists.get(hostname);
|
||||
if ( obsolete !== undefined ) {
|
||||
if ( obsolete > Date.now() ) {
|
||||
if ( result === 0 ) {
|
||||
return 2;
|
||||
}
|
||||
if ( result === 0 ) { return 2; }
|
||||
} else {
|
||||
delete documentWhitelists[hostname];
|
||||
documentWhitelists.delete(hostname);
|
||||
}
|
||||
}
|
||||
pos = hostname.indexOf('.');
|
||||
let pos = hostname.indexOf('.');
|
||||
if ( pos === -1 ) { break; }
|
||||
hostname = hostname.slice(pos + 1);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
var documentWhitelists = Object.create(null);
|
||||
let documentWhitelists = new Map();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
exports.temporarilyWhitelistDocument = function(hostname) {
|
||||
if ( typeof hostname !== 'string' || hostname === '' ) {
|
||||
return;
|
||||
return {
|
||||
start: (function() {
|
||||
let suspendedTabs = new Set();
|
||||
let onBeforeReady = function(details) {
|
||||
if ( details.type !== 'main_frame' && details.tabId > 0 ) {
|
||||
suspendedTabs.add(details.tabId);
|
||||
console.info('uBO suspend tab %d, block %s', details.tabId, details.url);
|
||||
return { cancel: true };
|
||||
}
|
||||
};
|
||||
// https://github.com/gorhill/uBlock/issues/2067
|
||||
// Experimental: Block everything until uBO is fully ready.
|
||||
// https://github.com/gorhill/uBlock/issues/3130
|
||||
// Don't block root frame.
|
||||
if ( µBlock.hiddenSettings.suspendTabsUntilReady ) {
|
||||
vAPI.net.addListener(
|
||||
'onBeforeRequest',
|
||||
onBeforeReady,
|
||||
{ urls: [ 'http://*/*', 'https://*/*' ] },
|
||||
[ 'blocking' ]
|
||||
);
|
||||
}
|
||||
return function() {
|
||||
vAPI.net.removeListener('onBeforeRequest', onBeforeReady);
|
||||
vAPI.net.addListener(
|
||||
'onBeforeRequest',
|
||||
onBeforeRequest,
|
||||
{ urls: [ 'http://*/*', 'https://*/*' ] },
|
||||
[ 'blocking' ]
|
||||
);
|
||||
vAPI.net.addListener(
|
||||
'onHeadersReceived',
|
||||
onHeadersReceived,
|
||||
{
|
||||
types: [ 'main_frame', 'sub_frame', 'image', 'media' ],
|
||||
urls: [ 'http://*/*', 'https://*/*' ],
|
||||
},
|
||||
[ 'blocking', 'responseHeaders' ]
|
||||
);
|
||||
if ( vAPI.net.validTypes.has('csp_report') ) {
|
||||
vAPI.net.addListener(
|
||||
'onBeforeRequest',
|
||||
onBeforeMaybeSpuriousCSPReport,
|
||||
{
|
||||
types: [ 'csp_report' ],
|
||||
urls: [ 'http://*/*', 'https://*/*' ]
|
||||
},
|
||||
[ 'blocking', 'requestBody' ]
|
||||
);
|
||||
}
|
||||
// https://github.com/gorhill/uBlock/issues/2067
|
||||
// Force-reload tabs for which network requests were blocked
|
||||
// during launch. This can happen only if tabs were "suspended".
|
||||
for ( let tabId of suspendedTabs ) {
|
||||
console.info('uBO suspend tab %d, force reload', tabId);
|
||||
vAPI.tabs.reload(tabId);
|
||||
}
|
||||
suspendedTabs.clear();
|
||||
};
|
||||
})(),
|
||||
|
||||
temporarilyWhitelistDocument: function(hostname) {
|
||||
if ( typeof hostname !== 'string' || hostname === '' ) { return; }
|
||||
documentWhitelists.set(hostname, Date.now() + 60 * 1000);
|
||||
}
|
||||
|
||||
documentWhitelists[hostname] = Date.now() + 60 * 1000;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
return exports;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
Loading…
Reference in a new issue