Add approximate reporting of tabless network requests

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/1204

Not much can be done beside reporting to tabless network
requests to all tabs for which the context is a match.

A short term local cache is used to avoid having to iterate
through all existing tabs for each tabless network request
just to find and report to the matching ones -- users
reporting having a lot of opened tabs at once is not so
uncommon.
This commit is contained in:
Raymond Hill 2020-12-12 08:19:40 -05:00
parent cc86e373ec
commit 6df32675b1
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
3 changed files with 65 additions and 35 deletions

View file

@ -69,7 +69,7 @@ const initializeTabs = async function() {
if ( tab.discarded === true ) { continue; }
const { id, url } = tab;
µb.tabContextManager.commit(id, url);
µb.bindTabToPageStats(id);
µb.bindTabToPageStore(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

View file

@ -371,10 +371,10 @@
// It is a popup, block and remove the tab.
if ( popupType === 'popup' ) {
µb.unbindTabFromPageStats(targetTabId);
µb.unbindTabFromPageStore(targetTabId);
vAPI.tabs.remove(targetTabId, false);
} else {
µb.unbindTabFromPageStats(openerTabId);
µb.unbindTabFromPageStore(openerTabId);
vAPI.tabs.remove(openerTabId, true);
}
@ -850,7 +850,7 @@ vAPI.Tabs = class extends vAPI.Tabs {
onClosed(tabId) {
super.onClosed(tabId);
if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
µBlock.unbindTabFromPageStats(tabId);
µBlock.unbindTabFromPageStore(tabId);
µBlock.contextMenu.update();
}
@ -874,7 +874,7 @@ vAPI.Tabs = class extends vAPI.Tabs {
const µb = µBlock;
if ( details.frameId === 0 ) {
µb.tabContextManager.commit(details.tabId, details.url);
let pageStore = µb.bindTabToPageStats(details.tabId, 'tabCommitted');
let pageStore = µb.bindTabToPageStore(details.tabId, 'tabCommitted');
if ( pageStore ) {
pageStore.journalAddRootFrame('committed', details.url);
}
@ -896,7 +896,7 @@ vAPI.Tabs = class extends vAPI.Tabs {
if ( !tab.url || tab.url === '' ) { return; }
if ( !changeInfo.url ) { return; }
µBlock.tabContextManager.commit(tabId, changeInfo.url);
µBlock.bindTabToPageStats(tabId, 'tabUpdated');
µBlock.bindTabToPageStore(tabId, 'tabUpdated');
}
};
@ -907,12 +907,12 @@ vAPI.tabs = new vAPI.Tabs();
// Create an entry for the tab if it doesn't exist.
µBlock.bindTabToPageStats = function(tabId, context) {
µBlock.bindTabToPageStore = function(tabId, context) {
this.updateToolbarIcon(tabId, 0b111);
// Do not create a page store for URLs which are of no interests
if ( this.tabContextManager.exists(tabId) === false ) {
this.unbindTabFromPageStats(tabId);
this.unbindTabFromPageStore(tabId);
return null;
}
@ -954,8 +954,7 @@ vAPI.tabs = new vAPI.Tabs();
/******************************************************************************/
µBlock.unbindTabFromPageStats = function(tabId) {
//console.debug('µBlock> unbindTabFromPageStats(%d)', tabId);
µBlock.unbindTabFromPageStore = function(tabId) {
const pageStore = this.pageStores.get(tabId);
if ( pageStore === undefined ) { return; }
pageStore.dispose();
@ -1143,7 +1142,7 @@ vAPI.tabs = new vAPI.Tabs();
const checkTab = async tabId => {
const tab = await vAPI.tabs.get(tabId);
if ( tab instanceof Object && tab.discarded !== true ) { return; }
µBlock.unbindTabFromPageStats(tabId);
µBlock.unbindTabFromPageStore(tabId);
};
const pageStoreJanitor = function() {

View file

@ -205,7 +205,7 @@ const onBeforeRootFrameRequest = function(fctxt) {
fctxt.type = 'main_frame';
}
const pageStore = µb.bindTabToPageStats(fctxt.tabId, 'beforeRequest');
const pageStore = µb.bindTabToPageStore(fctxt.tabId, 'beforeRequest');
if ( pageStore !== null ) {
pageStore.journalAddRootFrame('uncommitted', requestURL);
pageStore.journalAddRequest(requestHostname, result);
@ -294,35 +294,15 @@ const toBlockDocResult = function(url, hostname, logData) {
// Intercept and filter behind-the-scene requests.
// https://github.com/gorhill/uBlock/issues/870
// Finally, Chromium 49+ gained the ability to report network request of type
// `beacon`, so now we can block them according to the state of the
// "Disable hyperlink auditing/beacon" setting.
const onBeforeBehindTheSceneRequest = function(fctxt) {
const µb = µBlock;
const pageStore = µb.pageStoreFromTabId(fctxt.tabId);
if ( pageStore === null ) { return; }
// https://bugs.chromium.org/p/chromium/issues/detail?id=637577#c15
// Do not filter behind-the-scene network request of type `beacon`: there
// is no point. In any case, this will become a non-issue once
// <https://bugs.chromium.org/p/chromium/issues/detail?id=522129> is
// fixed.
// Blocking behind-the-scene requests can break a lot of stuff: prevent
// browser updates, prevent extension updates, prevent extensions from
// working properly, etc.
// So we filter if and only if the "advanced user" mode is selected.
// https://github.com/gorhill/uBlock/issues/3150
// Ability to globally block CSP reports MUST also apply to
// behind-the-scene network requests.
// 2018-03-30:
// Filter all behind-the-scene network requests like any other network
// requests. Hopefully this will not break stuff as it used to be the
// case.
let result = 0;
// https://github.com/uBlockOrigin/uBlock-issues/issues/339
@ -341,8 +321,8 @@ const onBeforeBehindTheSceneRequest = function(fctxt) {
// The "any-tab" scope is not whitelist-able, and in such case we must
// use the origin URL as the scope. Most such requests aren't going to
// be blocked, so we further test for whitelisting and modify the
// result only when the request is being blocked.
// be blocked, so we test for whitelisting and modify the result only
// when the request is being blocked.
if (
result === 1 &&
µb.getNetFilteringSwitch(fctxt.tabOrigin) === false
@ -352,6 +332,9 @@ const onBeforeBehindTheSceneRequest = function(fctxt) {
}
}
// https://github.com/uBlockOrigin/uBlock-issues/issues/1204
onBeforeBehindTheSceneRequest.journalAddRequest(fctxt, result);
if ( µb.logger.enabled ) {
fctxt.setRealm('network').toLogger();
}
@ -369,6 +352,54 @@ const onBeforeBehindTheSceneRequest = function(fctxt) {
}
};
// https://github.com/uBlockOrigin/uBlock-issues/issues/1204
// Report the tabless network requests to all page stores matching the
// document origin. This is an approximation, there is unfortunately no
// way to know for sure which exact page triggered a tabless network
// request.
onBeforeBehindTheSceneRequest.journalAddRequest = (( ) => {
let hostname = '';
let pageStores = new Set();
let pageStoresToken = 0;
let gcTimer;
const reset = function() {
hostname = '';
pageStores = new Set();
pageStoresToken = 0;
};
const gc = ( ) => {
gcTimer = undefined;
if ( pageStoresToken !== µBlock.pageStoresToken ) { return reset(); }
gcTimer = vAPI.setTimeout(gc, 30011);
};
return function(fctxt, result) {
const { docHostname } = fctxt;
if (
docHostname !== hostname ||
pageStoresToken !== µBlock.pageStoresToken
) {
hostname = docHostname;
pageStores = new Set();
for ( const pageStore of µBlock.pageStores.values() ) {
if ( pageStore.tabHostname !== docHostname ) { continue; }
pageStores.add(pageStore);
}
pageStoresToken = µBlock.pageStoresToken;
if ( gcTimer !== undefined ) {
clearTimeout(gcTimer);
}
gcTimer = vAPI.setTimeout(gc, 30011);
}
for ( const pageStore of pageStores ) {
pageStore.journalAddRequest(fctxt.hostname, result);
}
};
})();
/******************************************************************************/
// To handle:
@ -394,7 +425,7 @@ const onHeadersReceived = function(details) {
let pageStore = µb.pageStoreFromTabId(fctxt.tabId);
if ( pageStore === null ) {
if ( isRootDoc === false ) { return; }
pageStore = µb.bindTabToPageStats(fctxt.tabId, 'beforeRequest');
pageStore = µb.bindTabToPageStore(fctxt.tabId, 'beforeRequest');
}
if ( pageStore.getNetFilteringSwitch(fctxt) === false ) { return; }