From 6df32675b16b591313b40231583b4d06dda9e526 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Dec 2020 08:19:40 -0500 Subject: [PATCH] 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. --- src/js/start.js | 2 +- src/js/tab.js | 19 ++++++------ src/js/traffic.js | 79 +++++++++++++++++++++++++++++++++-------------- 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/src/js/start.js b/src/js/start.js index df3225d8e..b86e45c07 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -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 diff --git a/src/js/tab.js b/src/js/tab.js index 55b400dd5..cc09eb7bb 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -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() { diff --git a/src/js/traffic.js b/src/js/traffic.js index 17cdb615a..e0734b661 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -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 - // 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; }