1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-18 17:02:27 +02:00

Fix whitelist status evaluation of tabless network requests

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

The `behnind-the-scene` context was wrongly used to
evaluate the whitelist status of the context of tabless
network requests. The document origin must be used
instead when it's available.

Additionally, much code has been revisited for better
ES6 syntax compliance.
This commit is contained in:
Raymond Hill 2019-07-05 17:44:08 -04:00
parent 9693d07a6d
commit ae56c4dfe8
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
2 changed files with 652 additions and 673 deletions

View File

@ -30,48 +30,43 @@ To create a log of net requests
**/
/******************************************************************************/
/******************************************************************************/
{
µBlock.PageStore = (function() {
// start of private namespace
// >>>>>
/******************************************************************************/
const µb = µBlock;
/******************************************************************************/
/******************************************************************************/
// To mitigate memory churning
const netFilteringCacheJunkyard = [];
const netFilteringCacheJunkyardMax = 10;
/******************************************************************************/
const NetFilteringResultCache = function() {
const NetFilteringResultCache = class {
constructor() {
this.init();
};
}
NetFilteringResultCache.prototype = {
shelfLife: 15000,
init: function() {
init() {
this.blocked = new Map();
this.results = new Map();
this.hash = 0;
this.timer = undefined;
return this;
},
}
dispose: function() {
dispose() {
this.empty();
if ( netFilteringCacheJunkyard.length < netFilteringCacheJunkyardMax ) {
netFilteringCacheJunkyard.push(this);
}
return null;
},
}
rememberResult: function(fctxt, result) {
rememberResult(fctxt, result) {
if ( fctxt.tabId <= 0 ) { return; }
if ( this.results.size === 0 ) {
this.pruneAsync();
@ -86,9 +81,9 @@ NetFilteringResultCache.prototype = {
const now = Date.now();
this.blocked.set(key, now);
this.hash = now;
},
}
rememberBlock: function(fctxt) {
rememberBlock(fctxt) {
if ( fctxt.tabId <= 0 ) { return; }
if ( this.blocked.size === 0 ) {
this.pruneAsync();
@ -99,9 +94,9 @@ NetFilteringResultCache.prototype = {
now
);
this.hash = now;
},
}
empty: function() {
empty() {
this.blocked.clear();
this.results.clear();
this.hash = 0;
@ -109,9 +104,9 @@ NetFilteringResultCache.prototype = {
clearTimeout(this.timer);
this.timer = undefined;
}
},
}
prune: function() {
prune() {
const obsolete = Date.now() - this.shelfLife;
for ( const entry of this.blocked ) {
if ( entry[1] <= obsolete ) {
@ -127,9 +122,9 @@ NetFilteringResultCache.prototype = {
if ( this.blocked.size !== 0 || this.results.size !== 0 ) {
this.pruneAsync();
}
},
}
pruneAsync: function() {
pruneAsync() {
if ( this.timer !== undefined ) { return; }
this.timer = vAPI.setTimeout(
( ) => {
@ -138,17 +133,17 @@ NetFilteringResultCache.prototype = {
},
this.shelfLife
);
},
}
lookupResult: function(fctxt) {
lookupResult(fctxt) {
return this.results.get(
fctxt.getDocHostname() + ' ' +
fctxt.type + ' ' +
fctxt.url
);
},
}
lookupAllBlocked: function(hostname) {
lookupAllBlocked(hostname) {
const result = [];
for ( const entry of this.blocked ) {
const pos = entry[0].indexOf(' ');
@ -157,17 +152,18 @@ NetFilteringResultCache.prototype = {
}
}
return result;
},
};
}
NetFilteringResultCache.factory = function() {
static factory() {
const entry = netFilteringCacheJunkyard.pop();
return entry !== undefined
? entry.init()
: new NetFilteringResultCache();
}
};
/******************************************************************************/
NetFilteringResultCache.prototype.shelfLife = 15000;
/******************************************************************************/
// Frame stores are used solely to associate a URL with a frame id. The
@ -179,59 +175,53 @@ NetFilteringResultCache.factory = function() {
const frameStoreJunkyard = [];
const frameStoreJunkyardMax = 50;
/******************************************************************************/
const FrameStore = function(frameURL) {
const FrameStore = class {
constructor(frameURL) {
this.init(frameURL);
};
}
FrameStore.prototype = {
init: function(frameURL) {
init(frameURL) {
const µburi = µb.URI;
this.pageHostname = µburi.hostnameFromURI(frameURL);
this.pageDomain =
µburi.domainFromHostname(this.pageHostname) || this.pageHostname;
return this;
},
}
dispose: function() {
dispose() {
this.pageHostname = this.pageDomain = '';
if ( frameStoreJunkyard.length < frameStoreJunkyardMax ) {
frameStoreJunkyard.push(this);
}
return null;
},
};
}
FrameStore.factory = function(frameURL) {
static factory(frameURL) {
const entry = frameStoreJunkyard.pop();
if ( entry === undefined ) {
return new FrameStore(frameURL);
}
return entry.init(frameURL);
}
};
/******************************************************************************/
/******************************************************************************/
// To mitigate memory churning
const pageStoreJunkyard = [];
const pageStoreJunkyardMax = 10;
/******************************************************************************/
const PageStore = function(tabId, context) {
const PageStore = class {
constructor(tabId, context) {
this.extraData = new Map();
this.journal = [];
this.journalTimer = null;
this.journalLastCommitted = this.journalLastUncommitted = undefined;
this.journalLastUncommittedURL = undefined;
this.init(tabId, context);
};
}
/******************************************************************************/
PageStore.factory = function(tabId, context) {
static factory(tabId, context) {
let entry = pageStoreJunkyard.pop();
if ( entry === undefined ) {
entry = new PageStore(tabId, context);
@ -239,15 +229,13 @@ PageStore.factory = function(tabId, context) {
entry.init(tabId, context);
}
return entry;
};
/******************************************************************************/
}
// https://github.com/gorhill/uBlock/issues/3201
// The context is used to determine whether we report behavior change to the
// logger.
// The context is used to determine whether we report behavior change
// to the logger.
PageStore.prototype.init = function(tabId, context) {
init(tabId, context) {
const tabContext = µb.tabContextManager.mustLookup(tabId);
this.tabId = tabId;
@ -285,7 +273,7 @@ PageStore.prototype.init = function(tabId, context) {
// - If it has been initialized, we do not want to change the state
// of the current context.
const fctxt = µb.logger.enabled
? µBlock.filteringContext
? µb.filteringContext
.duplicate()
.fromTabId(tabId)
.setURL(tabContext.rawURL)
@ -311,11 +299,9 @@ PageStore.prototype.init = function(tabId, context) {
}
return this;
};
}
/******************************************************************************/
PageStore.prototype.reuse = function(context) {
reuse(context) {
// When force refreshing a page, the page store data needs to be reset.
// If the hostname changes, we can't merely just update the context.
@ -324,11 +310,12 @@ PageStore.prototype.reuse = function(context) {
context = '';
}
// If URL changes without a page reload (more and more common), then we
// need to keep all that we collected for reuse. In particular, not
// doing so was causing a problem in `videos.foxnews.com`: clicking a
// video thumbnail would not work, because the frame hierarchy structure
// was flushed from memory, while not really being flushed on the page.
// If URL changes without a page reload (more and more common), then
// we need to keep all that we collected for reuse. In particular,
// not doing so was causing a problem in `videos.foxnews.com`:
// clicking a video thumbnail would not work, because the frame
// hierarchy structure was flushed from memory, while not really being
// flushed on the page.
if ( context === 'tabUpdated' ) {
// As part of https://github.com/chrisaljoudi/uBlock/issues/405
// URL changed, force a re-evaluation of filtering switch
@ -345,13 +332,9 @@ PageStore.prototype.reuse = function(context) {
this.netFilteringCache = this.netFilteringCache.dispose();
this.init(this.tabId, context);
return this;
};
}
// https://www.youtube.com/watch?v=dltNSbOupgE
/******************************************************************************/
PageStore.prototype.dispose = function() {
dispose() {
this.tabHostname = '';
this.title = '';
this.rawURL = '';
@ -373,70 +356,63 @@ PageStore.prototype.dispose = function() {
pageStoreJunkyard.push(this);
}
return null;
};
}
/******************************************************************************/
PageStore.prototype.disposeFrameStores = function() {
disposeFrameStores() {
for ( const frameStore of this.frames.values() ) {
frameStore.dispose();
}
this.frames.clear();
};
}
PageStore.prototype.getFrame = function(frameId) {
getFrame(frameId) {
return this.frames.get(frameId) || null;
};
}
PageStore.prototype.setFrame = function(frameId, frameURL) {
setFrame(frameId, frameURL) {
const frameStore = this.frames.get(frameId);
if ( frameStore !== undefined ) {
frameStore.init(frameURL);
} else {
this.frames.set(frameId, FrameStore.factory(frameURL));
}
};
}
/******************************************************************************/
getNetFilteringSwitch() {
return µb.tabContextManager
.mustLookup(this.tabId)
.getNetFilteringSwitch();
}
PageStore.prototype.getNetFilteringSwitch = function() {
return µb.tabContextManager.mustLookup(this.tabId).getNetFilteringSwitch();
};
PageStore.prototype.getSpecificCosmeticFilteringSwitch = function() {
getSpecificCosmeticFilteringSwitch() {
return this.noCosmeticFiltering !== true;
};
}
PageStore.prototype.toggleNetFilteringSwitch = function(url, scope, state) {
toggleNetFilteringSwitch(url, scope, state) {
µb.toggleNetFilteringSwitch(url, scope, state);
this.netFilteringCache.empty();
};
}
/******************************************************************************/
PageStore.prototype.injectLargeMediaElementScriptlet = function() {
injectLargeMediaElementScriptlet() {
vAPI.tabs.injectScript(this.tabId, {
file: '/js/scriptlets/load-large-media-interactive.js',
allFrames: true,
runAt: 'document_idle',
});
µb.contextMenu.update(this.tabId);
};
}
PageStore.prototype.temporarilyAllowLargeMediaElements = function(state) {
temporarilyAllowLargeMediaElements(state) {
this.largeMediaCount = 0;
µb.contextMenu.update(this.tabId);
this.allowLargeMediaElementsUntil = state ? Date.now() + 86400000 : 0;
µb.scriptlets.injectDeep(this.tabId, 'load-large-media-all');
};
/******************************************************************************/
}
// https://github.com/gorhill/uBlock/issues/2053
// There is no way around using journaling to ensure we deal properly with
// potentially out of order navigation events vs. network request events.
PageStore.prototype.journalAddRequest = function(hostname, result) {
journalAddRequest(hostname, result) {
if ( hostname === '' ) { return; }
this.journal.push(
hostname,
@ -448,9 +424,9 @@ PageStore.prototype.journalAddRequest = function(hostname, result) {
µb.hiddenSettings.requestJournalProcessPeriod
);
}
};
}
PageStore.prototype.journalAddRootFrame = function(type, url) {
journalAddRootFrame(type, url) {
if ( type === 'committed' ) {
this.journalLastCommitted = this.journal.length;
if (
@ -472,9 +448,9 @@ PageStore.prototype.journalAddRootFrame = function(type, url) {
( ) => { this.journalProcess(true); },
µb.hiddenSettings.requestJournalProcessPeriod
);
};
}
PageStore.prototype.journalProcess = function(fromTimer) {
journalProcess(fromTimer) {
if ( !fromTimer ) {
clearTimeout(this.journalTimer);
}
@ -507,31 +483,34 @@ PageStore.prototype.journalProcess = function(fromTimer) {
µb.updateToolbarIcon(this.tabId, 0x02);
}
// Everything before pivot does not originate from current page -- we still
// need to bump global blocked/allowed counts.
// Everything before pivot does not originate from current page -- we
// still need to bump global blocked/allowed counts.
for ( let i = 0; i < pivot; i += 2 ) {
aggregateCounts += journal[i+1];
}
if ( aggregateCounts !== 0 ) {
µb.localSettings.blockedRequestCount += aggregateCounts & 0xFFFF;
µb.localSettings.allowedRequestCount += aggregateCounts >>> 16 & 0xFFFF;
µb.localSettings.blockedRequestCount +=
aggregateCounts & 0xFFFF;
µb.localSettings.allowedRequestCount +=
aggregateCounts >>> 16 & 0xFFFF;
µb.localSettingsLastModified = now;
}
journal.length = 0;
};
}
/******************************************************************************/
PageStore.prototype.filterRequest = function(fctxt) {
filterRequest(fctxt) {
fctxt.filter = undefined;
if ( this.getNetFilteringSwitch() === false ) {
if ( this.getNetFilteringSwitch(fctxt) === false ) {
return 0;
}
const requestType = fctxt.type;
if ( requestType === 'csp_report' && this.filterCSPReport(fctxt) === 1 ) {
if (
requestType === 'csp_report' &&
this.filterCSPReport(fctxt) === 1
) {
return 1;
}
@ -588,27 +567,17 @@ PageStore.prototype.filterRequest = function(fctxt) {
if ( cacheableResult ) {
this.netFilteringCache.rememberResult(fctxt, result);
} else if ( result === 1 && this.collapsibleResources.has(requestType) ) {
} else if (
result === 1 &&
this.collapsibleResources.has(requestType)
) {
this.netFilteringCache.rememberBlock(fctxt, true);
}
return result;
};
}
PageStore.prototype.cacheableResults = new Set([
'sub_frame',
]);
PageStore.prototype.collapsibleResources = new Set([
'image',
'media',
'object',
'sub_frame',
]);
/******************************************************************************/
PageStore.prototype.filterCSPReport = function(fctxt) {
filterCSPReport(fctxt) {
if (
µb.sessionSwitches.evaluateZ(
'no-csp-reports',
@ -621,11 +590,9 @@ PageStore.prototype.filterCSPReport = function(fctxt) {
return 1;
}
return 0;
};
}
/******************************************************************************/
PageStore.prototype.filterFont = function(fctxt) {
filterFont(fctxt) {
if ( fctxt.type === 'font' ) {
this.remoteFontCount += 1;
}
@ -641,14 +608,12 @@ PageStore.prototype.filterFont = function(fctxt) {
return 1;
}
return 0;
};
}
/******************************************************************************/
PageStore.prototype.filterScripting = function(fctxt, netFiltering) {
filterScripting(fctxt, netFiltering) {
fctxt.filter = undefined;
if ( netFiltering === undefined ) {
netFiltering = this.getNetFilteringSwitch();
netFiltering = this.getNetFilteringSwitch(fctxt);
}
if (
netFiltering === false ||
@ -663,13 +628,10 @@ PageStore.prototype.filterScripting = function(fctxt, netFiltering) {
fctxt.filter = µb.sessionSwitches.toLogData();
}
return 1;
};
/******************************************************************************/
}
// The caller is responsible to check whether filtering is enabled or not.
PageStore.prototype.filterLargeMediaElement = function(fctxt, size) {
filterLargeMediaElement(fctxt, size) {
fctxt.filter = undefined;
if ( Date.now() < this.allowLargeMediaElementsUntil ) {
@ -700,16 +662,12 @@ PageStore.prototype.filterLargeMediaElement = function(fctxt, size) {
}
return 1;
};
}
// https://www.youtube.com/watch?v=drW8p_dTLD4
/******************************************************************************/
PageStore.prototype.getBlockedResources = function(request, response) {
getBlockedResources(request, response) {
const normalURL = µb.normalizePageURL(this.tabId, request.frameURL);
const resources = request.resources;
const fctxt = µBlock.filteringContext;
const fctxt = µb.filteringContext;
fctxt.fromTabId(this.tabId)
.setDocOriginFromURL(normalURL);
// Force some resources to go through the filtering engine in order to
@ -728,14 +686,25 @@ PageStore.prototype.getBlockedResources = function(request, response) {
response.hash = this.netFilteringCache.hash;
response.blockedResources =
this.netFilteringCache.lookupAllBlocked(fctxt.getDocHostname());
}
};
PageStore.prototype.cacheableResults = new Set([
'sub_frame',
]);
PageStore.prototype.collapsibleResources = new Set([
'image',
'media',
'object',
'sub_frame',
]);
µb.PageStore = PageStore;
/******************************************************************************/
return {
factory: PageStore.factory
};
// <<<<<
// end of private namespace
})();
/******************************************************************************/
}

View File

@ -19,21 +19,12 @@
Home: https://github.com/gorhill/uBlock
*/
// https://github.com/gorhill/uBlock/issues/2720
/******************************************************************************/
/******************************************************************************/
(function() {
'use strict';
/******************************************************************************/
var µb = µBlock;
/******************************************************************************/
// https://github.com/gorhill/httpswitchboard/issues/303
// Some kind of trick going on here:
// Any scheme other than 'http' and 'https' is remapped into a fake
// URL which trick the rest of µBlock into being able to process an
// otherwise unmanageable scheme. µBlock needs web page to have a proper
@ -42,10 +33,7 @@ var µb = µBlock;
// hostname. This way, for a specific scheme you can create scope with
// rules which will apply only to that scheme.
/******************************************************************************/
/******************************************************************************/
µb.normalizePageURL = function(tabId, pageURL) {
µBlock.normalizePageURL = function(tabId, pageURL) {
if ( tabId < 0 ) {
return 'http://behind-the-scene/';
}
@ -63,7 +51,7 @@ var µb = µBlock;
fakeHostname = uri.path + '.' + fakeHostname;
}
return 'http://' + fakeHostname + '/';
return `http://${fakeHostname}/`;
};
/******************************************************************************/
@ -127,48 +115,54 @@ housekeep itself.
*/
µb.tabContextManager = (function() {
var tabContexts = new Map();
µBlock.tabContextManager = (( ) => {
const µb = µBlock;
const tabContexts = new Map();
// https://github.com/chrisaljoudi/uBlock/issues/1001
// This is to be used as last-resort fallback in case a tab is found to not
// be bound while network requests are fired for the tab.
var mostRecentRootDocURL = '';
var mostRecentRootDocURLTimestamp = 0;
let mostRecentRootDocURL = '';
let mostRecentRootDocURLTimestamp = 0;
var popupCandidates = new Map();
const popupCandidates = new Map();
var PopupCandidate = function(targetTabId, openerTabId) {
const PopupCandidate = class {
constructor(targetTabId, openerTabId) {
this.targetTabId = targetTabId;
this.opener = {
tabId: openerTabId,
popunder: false,
trustedURL: openerTabId === µb.mouseEventRegister.tabId ?
µb.mouseEventRegister.url :
''
trustedURL: openerTabId === µb.mouseEventRegister.tabId
? µb.mouseEventRegister.url
: ''
};
this.selfDestructionTimer = null;
this.launchSelfDestruction();
};
}
PopupCandidate.prototype.destroy = function() {
destroy() {
if ( this.selfDestructionTimer !== null ) {
clearTimeout(this.selfDestructionTimer);
}
popupCandidates.delete(this.targetTabId);
};
}
PopupCandidate.prototype.launchSelfDestruction = function() {
launchSelfDestruction() {
if ( this.selfDestructionTimer !== null ) {
clearTimeout(this.selfDestructionTimer);
}
this.selfDestructionTimer = vAPI.setTimeout(this.destroy.bind(this), 10000);
this.selfDestructionTimer = vAPI.setTimeout(
( ) => this.destroy(),
10000
);
}
};
var popupCandidateTest = function(targetTabId) {
for ( var entry of popupCandidates ) {
var tabId = entry[0];
var candidate = entry[1];
const popupCandidateTest = function(targetTabId) {
for ( const entry of popupCandidates ) {
const tabId = entry[0];
const candidate = entry[1];
if (
targetTabId !== tabId &&
targetTabId !== candidate.opener.tabId
@ -190,7 +184,7 @@ housekeep itself.
};
vAPI.tabs.onPopupCreated = function(targetTabId, openerTabId) {
var popup = popupCandidates.get(targetTabId);
const popup = popupCandidates.get(targetTabId);
if ( popup === undefined ) {
popupCandidates.set(
targetTabId,
@ -200,17 +194,17 @@ housekeep itself.
popupCandidateTest(targetTabId);
};
var gcPeriod = 10 * 60 * 1000;
const gcPeriod = 10 * 60 * 1000;
// A pushed entry is removed from the stack unless it is committed with
// a set time.
var StackEntry = function(url, commit) {
const StackEntry = function(url, commit) {
this.url = url;
this.committed = commit;
this.tstamp = Date.now();
};
var TabContext = function(tabId) {
const TabContext = function(tabId) {
this.tabId = tabId;
this.stack = [];
this.rawURL =
@ -238,16 +232,14 @@ housekeep itself.
TabContext.prototype.onTab = function(tab) {
if ( tab ) {
this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
this.gcTimer = vAPI.setTimeout(( ) => this.onGC(), gcPeriod);
} else {
this.destroy();
}
};
TabContext.prototype.onGC = function() {
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
return;
}
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; }
// https://github.com/gorhill/uBlock/issues/1713
// For unknown reasons, Firefox's setTimeout() will sometimes
// causes the callback function to be called immediately, bypassing
@ -258,7 +250,7 @@ housekeep itself.
}
this.onGCBarrier = true;
this.gcTimer = null;
vAPI.tabs.get(this.tabId, this.onTab.bind(this));
vAPI.tabs.get(this.tabId, tab => { this.onTab(tab); });
this.onGCBarrier = false;
};
@ -271,11 +263,9 @@ housekeep itself.
}
this.commitTimer = null;
// Remove uncommitted entries at the top of the stack.
var i = this.stack.length;
let i = this.stack.length;
while ( i-- ) {
if ( this.stack[i].committed ) {
break;
}
if ( this.stack[i].committed ) { break; }
}
// https://github.com/gorhill/uBlock/issues/300
// If no committed entry was found, fall back on the bottom-most one
@ -295,10 +285,8 @@ housekeep itself.
// contexts, as the behind-the-scene context is permanent -- so we do not
// want to flush it.
TabContext.prototype.autodestroy = function() {
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
return;
}
this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; }
this.gcTimer = vAPI.setTimeout(( ) => this.onGC(), gcPeriod);
};
// Update just force all properties to be updated to match the most recent
@ -313,7 +301,7 @@ housekeep itself.
this.rootDomain = '';
return;
}
var stackEntry = this.stack[this.stack.length - 1];
const stackEntry = this.stack[this.stack.length - 1];
this.rawURL = stackEntry.url;
this.normalURL = µb.normalizePageURL(this.tabId, this.rawURL);
this.origin = µb.URI.originFromURI(this.normalURL);
@ -328,7 +316,7 @@ housekeep itself.
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
return;
}
var count = this.stack.length;
const count = this.stack.length;
if ( count !== 0 && this.stack[count - 1].url === url ) {
return;
}
@ -338,7 +326,7 @@ housekeep itself.
if ( this.commitTimer !== null ) {
clearTimeout(this.commitTimer);
}
this.commitTimer = vAPI.setTimeout(this.onCommit.bind(this), 500);
this.commitTimer = vAPI.setTimeout(( ) => this.onCommit(), 500);
};
// This tells that the url is definitely the one to be associated with the
@ -347,10 +335,8 @@ housekeep itself.
TabContext.prototype.commit = function(url) {
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; }
if ( this.stack.length !== 0 ) {
var top = this.stack[this.stack.length - 1];
if ( top.url === url && top.committed ) {
return false;
}
const top = this.stack[this.stack.length - 1];
if ( top.url === url && top.committed ) { return false; }
}
this.stack = [new StackEntry(url, true)];
this.update();
@ -364,7 +350,11 @@ housekeep itself.
// https://github.com/chrisaljoudi/uBlock/issues/1078
// Use both the raw and normalized URLs.
this.netFiltering = µb.getNetFilteringSwitch(this.normalURL);
if ( this.netFiltering && this.rawURL !== this.normalURL && this.rawURL !== '' ) {
if (
this.netFiltering &&
this.rawURL !== this.normalURL &&
this.rawURL !== ''
) {
this.netFiltering = µb.getNetFilteringSwitch(this.rawURL);
}
this.netFilteringReadTime = Date.now();
@ -373,8 +363,8 @@ housekeep itself.
// These are to be used for the API of the tab context manager.
var push = function(tabId, url) {
var entry = tabContexts.get(tabId);
const push = function(tabId, url) {
let entry = tabContexts.get(tabId);
if ( entry === undefined ) {
entry = new TabContext(tabId);
entry.autodestroy();
@ -386,14 +376,14 @@ housekeep itself.
};
// Find a tab context for a specific tab.
var lookup = function(tabId) {
const lookup = function(tabId) {
return tabContexts.get(tabId) || null;
};
// Find a tab context for a specific tab. If none is found, attempt to
// fix this. When all fail, the behind-the-scene context is returned.
var mustLookup = function(tabId) {
var entry = tabContexts.get(tabId);
const mustLookup = function(tabId) {
const entry = tabContexts.get(tabId);
if ( entry !== undefined ) {
return entry;
}
@ -401,7 +391,10 @@ housekeep itself.
// Google Hangout popup opens without a root frame. So for now we will
// just discard that best-guess root frame if it is too far in the
// future, at which point it ceases to be a "best guess".
if ( mostRecentRootDocURL !== '' && mostRecentRootDocURLTimestamp + 500 < Date.now() ) {
if (
mostRecentRootDocURL !== '' &&
mostRecentRootDocURLTimestamp + 500 < Date.now()
) {
mostRecentRootDocURL = '';
}
// https://github.com/chrisaljoudi/uBlock/issues/1001
@ -423,8 +416,8 @@ housekeep itself.
// https://github.com/gorhill/uBlock/issues/1735
// Filter for popups if actually committing.
var commit = function(tabId, url) {
var entry = tabContexts.get(tabId);
const commit = function(tabId, url) {
let entry = tabContexts.get(tabId);
if ( entry === undefined ) {
entry = push(tabId, url);
} else if ( entry.commit(url) ) {
@ -433,12 +426,12 @@ housekeep itself.
return entry;
};
var exists = function(tabId) {
const exists = function(tabId) {
return tabContexts.get(tabId) !== undefined;
};
// Behind-the-scene tab context
(function() {
{
const entry = new TabContext(vAPI.noTabId);
entry.stack.push(new StackEntry('', true));
entry.rawURL = '';
@ -446,15 +439,16 @@ housekeep itself.
entry.origin = µb.URI.originFromURI(entry.normalURL);
entry.rootHostname = µb.URI.hostnameFromURI(entry.origin);
entry.rootDomain = µb.URI.domainFromHostname(entry.rootHostname);
})();
}
// Context object, typically to be used to feed filtering engines.
var contextJunkyard = [];
var Context = function(tabId) {
const contextJunkyard = [];
const Context = class {
constructor(tabId) {
this.init(tabId);
};
Context.prototype.init = function(tabId) {
var tabContext = lookup(tabId);
}
init(tabId) {
const tabContext = lookup(tabId);
this.rootHostname = tabContext.rootHostname;
this.rootDomain = tabContext.rootDomain;
this.pageHostname =
@ -464,12 +458,13 @@ housekeep itself.
this.requestHostname =
this.requestDomain = '';
return this;
};
Context.prototype.dispose = function() {
}
dispose() {
contextJunkyard.push(this);
}
};
var createContext = function(tabId) {
const createContext = function(tabId) {
if ( contextJunkyard.length ) {
return contextJunkyard.pop().init(tabId);
}
@ -493,6 +488,7 @@ housekeep itself.
// content has changed.
vAPI.tabs.onNavigation = function(details) {
const µb = µBlock;
if ( details.frameId === 0 ) {
µb.tabContextManager.commit(details.tabId, details.url);
let pageStore = µb.bindTabToPageStats(details.tabId, 'tabCommitted');
@ -517,16 +513,16 @@ vAPI.tabs.onNavigation = function(details) {
vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) {
if ( !tab.url || tab.url === '' ) { return; }
if ( !changeInfo.url ) { return; }
µb.tabContextManager.commit(tabId, changeInfo.url);
µb.bindTabToPageStats(tabId, 'tabUpdated');
µBlock.tabContextManager.commit(tabId, changeInfo.url);
µBlock.bindTabToPageStats(tabId, 'tabUpdated');
};
/******************************************************************************/
vAPI.tabs.onClosed = function(tabId) {
if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
µb.unbindTabFromPageStats(tabId);
µb.contextMenu.update();
µBlock.unbindTabFromPageStats(tabId);
µBlock.contextMenu.update();
};
/******************************************************************************/
@ -553,7 +549,8 @@ vAPI.tabs.onClosed = function(tabId) {
// c: close opener
// d: close target
vAPI.tabs.onPopupUpdated = (function() {
vAPI.tabs.onPopupUpdated = (( ) => {
const µb = µBlock;
// The same context object will be reused everytime. This also allows to
// remember whether a popup or popunder was matched.
const fctxt = µBlock.filteringContext.setFilter(undefined);
@ -839,17 +836,17 @@ vAPI.tabs.registerListeners();
// Create an entry for the tab if it doesn't exist.
µb.bindTabToPageStats = function(tabId, context) {
µBlock.bindTabToPageStats = function(tabId, context) {
this.updateToolbarIcon(tabId, 0x03);
// Do not create a page store for URLs which are of no interests
if ( µb.tabContextManager.exists(tabId) === false ) {
if ( this.tabContextManager.exists(tabId) === false ) {
this.unbindTabFromPageStats(tabId);
return null;
}
// Reuse page store if one exists: this allows to guess if a tab is a popup
var pageStore = this.pageStores.get(tabId);
let pageStore = this.pageStores.get(tabId);
// Tab is not bound
if ( pageStore === undefined ) {
@ -885,45 +882,59 @@ vAPI.tabs.registerListeners();
/******************************************************************************/
µb.unbindTabFromPageStats = function(tabId) {
µBlock.unbindTabFromPageStats = function(tabId) {
//console.debug('µBlock> unbindTabFromPageStats(%d)', tabId);
var pageStore = this.pageStores.get(tabId);
if ( pageStore !== undefined ) {
const pageStore = this.pageStores.get(tabId);
if ( pageStore === undefined ) { return; }
pageStore.dispose();
this.pageStores.delete(tabId);
this.pageStoresToken = Date.now();
}
};
/******************************************************************************/
µb.pageStoreFromTabId = function(tabId) {
µBlock.pageStoreFromTabId = function(tabId) {
return this.pageStores.get(tabId) || null;
};
µb.mustPageStoreFromTabId = function(tabId) {
µBlock.mustPageStoreFromTabId = function(tabId) {
return this.pageStores.get(tabId) || this.pageStores.get(vAPI.noTabId);
};
/******************************************************************************/
// Permanent page store for behind-the-scene requests. Must never be removed.
//
// https://github.com/uBlockOrigin/uBlock-issues/issues/651
// The whitelist status of the tabless page store will be determined by
// the document context (if present) of the network request.
(function() {
const pageStore = µb.PageStore.factory(vAPI.noTabId);
µb.pageStores.set(pageStore.tabId, pageStore);
{
const NoPageStore = class extends µBlock.PageStore {
getNetFilteringSwitch(fctxt) {
if ( fctxt.docId === 0 ) {
const docOrigin = fctxt.getDocOrigin();
if ( docOrigin ) {
return µBlock.getNetFilteringSwitch(docOrigin);
}
}
return super.getNetFilteringSwitch();
}
};
const pageStore = new NoPageStore(vAPI.noTabId);
µBlock.pageStores.set(pageStore.tabId, pageStore);
pageStore.title = vAPI.i18n('logBehindTheScene');
})();
}
/******************************************************************************/
// Update visual of extension icon.
µb.updateToolbarIcon = (function() {
let tabIdToDetails = new Map();
µBlock.updateToolbarIcon = (( ) => {
const tabIdToDetails = new Map();
let updateBadge = function(tabId) {
let parts = tabIdToDetails.get(tabId);
const updateBadge = function(tabId) {
const parts = tabIdToDetails.get(tabId);
tabIdToDetails.delete(tabId);
let state = 0;
@ -963,24 +974,24 @@ vAPI.tabs.registerListeners();
/******************************************************************************/
µb.updateTitle = (function() {
var tabIdToTimer = new Map();
var delay = 499;
µBlock.updateTitle = (( ) => {
const tabIdToTimer = new Map();
const delay = 499;
var tryAgain = function(entry) {
const tryAgain = function(entry) {
if ( entry.count === 1 ) { return false; }
entry.count -= 1;
tabIdToTimer.set(
entry.tabId,
vAPI.setTimeout(updateTitle.bind(null, entry), delay)
vAPI.setTimeout(( ) => { updateTitle(entry); }, delay)
);
return true;
};
var onTabReady = function(entry, tab) {
const onTabReady = function(entry, tab) {
if ( !tab ) { return; }
var µb = µBlock;
var pageStore = µb.pageStoreFromTabId(entry.tabId);
const µb = µBlock;
const pageStore = µb.pageStoreFromTabId(entry.tabId);
if ( pageStore === null ) { return; }
// Firefox needs this: if you detach a tab, the new tab won't have
// its rawURL set. Concretely, this causes the logger to report an
@ -991,21 +1002,21 @@ vAPI.tabs.registerListeners();
if ( !tab.title && tryAgain(entry) ) { return; }
// https://github.com/gorhill/uMatrix/issues/225
// Sometimes title changes while page is loading.
var settled = tab.title && tab.title === pageStore.title;
const settled = tab.title && tab.title === pageStore.title;
pageStore.title = tab.title || tab.url || '';
if ( !settled ) {
tryAgain(entry);
}
};
var updateTitle = function(entry) {
const updateTitle = function(entry) {
tabIdToTimer.delete(entry.tabId);
vAPI.tabs.get(entry.tabId, onTabReady.bind(null, entry));
};
return function(tabId) {
if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
var timer = tabIdToTimer.get(tabId);
const timer = tabIdToTimer.get(tabId);
if ( timer !== undefined ) {
clearTimeout(timer);
}
@ -1021,29 +1032,31 @@ vAPI.tabs.registerListeners();
/******************************************************************************/
// Stale page store entries janitor
// https://github.com/chrisaljoudi/uBlock/issues/455
// Stale page store entries janitor
var pageStoreJanitorPeriod = 15 * 60 * 1000;
var pageStoreJanitorSampleAt = 0;
var pageStoreJanitorSampleSize = 10;
{
const pageStoreJanitorPeriod = 15 * 60 * 1000;
let pageStoreJanitorSampleAt = 0;
let pageStoreJanitorSampleSize = 10;
var pageStoreJanitor = function() {
var vapiTabs = vAPI.tabs;
var tabIds = Array.from(µb.pageStores.keys()).sort();
var checkTab = function(tabId) {
vapiTabs.get(tabId, function(tab) {
if ( !tab ) {
µb.unbindTabFromPageStats(tabId);
}
const pageStoreJanitor = function() {
const tabIds = Array.from(µBlock.pageStores.keys()).sort();
const checkTab = tabId => {
vAPI.tabs.get(tabId, tab => {
if ( tab ) { return; }
µBlock.unbindTabFromPageStats(tabId);
});
};
if ( pageStoreJanitorSampleAt >= tabIds.length ) {
pageStoreJanitorSampleAt = 0;
}
var n = Math.min(pageStoreJanitorSampleAt + pageStoreJanitorSampleSize, tabIds.length);
for ( var i = pageStoreJanitorSampleAt; i < n; i++ ) {
var tabId = tabIds[i];
const n = Math.min(
pageStoreJanitorSampleAt + pageStoreJanitorSampleSize,
tabIds.length
);
for ( let i = pageStoreJanitorSampleAt; i < n; i++ ) {
const tabId = tabIds[i];
if ( vAPI.isBehindTheSceneTabId(tabId) ) { continue; }
checkTab(tabId);
}
@ -1053,9 +1066,6 @@ var pageStoreJanitor = function() {
};
vAPI.setTimeout(pageStoreJanitor, pageStoreJanitorPeriod);
/******************************************************************************/
})();
}
/******************************************************************************/