This commit is contained in:
gorhill 2016-10-08 10:15:31 -04:00
parent 41733339d3
commit 3ff3ae7d70
7 changed files with 135 additions and 91 deletions

View file

@ -89,8 +89,8 @@ return {
blockedRequestCount: 0,
allowedRequestCount: 0
},
localSettingsModifyTime: 0,
localSettingsSaveTime: 0,
localSettingsLastModified: 0,
localSettingsLastSaved: 0,
// read-only
systemSettings: {

View file

@ -217,9 +217,6 @@ Matrix.prototype.hasSameRules = function(other, srcHostname, desHostnames) {
// Specific destinations
for ( var desHostname in desHostnames ) {
if ( desHostnames.hasOwnProperty(desHostname) === false ) {
continue;
}
ruleKey = '* ' + desHostname;
if ( (thisRules[ruleKey] || 0) !== (otherRules[ruleKey] || 0) ) {
return false;

View file

@ -199,22 +199,27 @@ var µb = µBlock;
/******************************************************************************/
var getHostnameDict = function(hostnameToCountMap) {
var r = {}, de;
var domainFromHostname = µb.URI.domainFromHostname;
var domain, counts, blockCount, allowCount;
for ( var hostname in hostnameToCountMap ) {
if ( hostnameToCountMap.hasOwnProperty(hostname) === false ) {
continue;
var r = Object.create(null),
domainEntry,
domainFromHostname = µb.URI.domainFromHostname,
domain, blockCount, allowCount,
iter = hostnameToCountMap.entries(),
entry, hostname, counts;
for (;;) {
entry = iter.next();
if ( entry.done ) {
break;
}
if ( r.hasOwnProperty(hostname) ) {
hostname = entry.value[0];
if ( r[hostname] !== undefined ) {
continue;
}
domain = domainFromHostname(hostname) || hostname;
counts = hostnameToCountMap[domain] || 0;
counts = hostnameToCountMap.get(domain) || 0;
blockCount = counts & 0xFFFF;
allowCount = counts >>> 16 & 0xFFFF;
if ( r.hasOwnProperty(domain) === false ) {
de = r[domain] = {
if ( r[domain] === undefined ) {
domainEntry = r[domain] = {
domain: domain,
blockCount: blockCount,
allowCount: allowCount,
@ -222,13 +227,13 @@ var getHostnameDict = function(hostnameToCountMap) {
totalAllowCount: allowCount
};
} else {
de = r[domain];
domainEntry = r[domain];
}
counts = hostnameToCountMap[hostname] || 0;
counts = entry.value[1];
blockCount = counts & 0xFFFF;
allowCount = counts >>> 16 & 0xFFFF;
de.totalBlockCount += blockCount;
de.totalAllowCount += allowCount;
domainEntry.totalBlockCount += blockCount;
domainEntry.totalAllowCount += allowCount;
if ( hostname === domain ) {
continue;
}
@ -268,10 +273,8 @@ var getFirewallRules = function(srcHostname, desHostnames) {
r['. * 3p-frame'] = df.evaluateCellZY(srcHostname, '*', '3p-frame').toFilterString();
for ( var desHostname in desHostnames ) {
if ( desHostnames.hasOwnProperty(desHostname) ) {
r['/ ' + desHostname + ' *'] = df.evaluateCellZY('*', desHostname, '*').toFilterString();
r['. ' + desHostname + ' *'] = df.evaluateCellZY(srcHostname, desHostname, '*').toFilterString();
}
r['/ ' + desHostname + ' *'] = df.evaluateCellZY('*', desHostname, '*').toFilterString();
r['. ' + desHostname + ' *'] = df.evaluateCellZY(srcHostname, desHostname, '*').toFilterString();
}
return r;
};

View file

@ -266,6 +266,10 @@ var pageStoreJunkyardMax = 10;
var PageStore = function(tabId) {
this.init(tabId);
this.journal = [];
this.journalTimer = null;
this.journalLastCommitted = this.journalLastUncommitted = undefined;
this.journalLastUncommittedURL = undefined;
};
/******************************************************************************/
@ -298,7 +302,7 @@ PageStore.prototype.init = function(tabId) {
this.tabHostname = tabContext.rootHostname;
this.title = tabContext.rawURL;
this.rawURL = tabContext.rawURL;
this.hostnameToCountMap = {};
this.hostnameToCountMap = new Map();
this.contentLastModified = 0;
this.frames = Object.create(null);
this.perLoadBlockedRequestCount = 0;
@ -388,10 +392,6 @@ PageStore.prototype.reuse = function(context) {
/******************************************************************************/
PageStore.prototype.dispose = function() {
// rhill 2013-11-07: Even though at init time these are reset, I still
// need to release the memory taken by these, which can amount to
// sizeable enough chunks (especially requests, through the request URL
// used as a key).
this.tabHostname = '';
this.title = '';
this.rawURL = '';
@ -403,6 +403,12 @@ PageStore.prototype.dispose = function() {
}
this.disposeFrameStores();
this.netFilteringCache = this.netFilteringCache.dispose();
if ( this.journalTimer !== null ) {
clearTimeout(this.journalTimer);
this.journalTimer = null;
}
this.journal = [];
this.journalLastUncommittedURL = undefined;
if ( pageStoreJunkyard.length < pageStoreJunkyardMax ) {
pageStoreJunkyard.push(this);
}
@ -519,6 +525,92 @@ PageStore.prototype.temporarilyAllowLargeMediaElements = function() {
/******************************************************************************/
// 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) {
if ( hostname === '' ) { return; }
this.journal.push(
hostname,
result.charCodeAt(1) === 0x62 /* 'b' */ ? 0x00000001 : 0x00010000
);
if ( this.journalTimer === null ) {
this.journalTimer = vAPI.setTimeout(this.journalProcess.bind(this, true), 1000);
}
};
PageStore.prototype.journalAddRootFrame = function(type, url) {
if ( type === 'committed' ) {
this.journalLastCommitted = this.journal.length;
if (
this.journalLastUncommitted !== undefined &&
this.journalLastUncommitted < this.journalLastCommitted &&
this.journalLastUncommittedURL === url
) {
this.journalLastCommitted = this.journalLastUncommitted;
this.journalLastUncommitted = undefined;
}
} else if ( type === 'uncommitted' ) {
this.journalLastUncommitted = this.journal.length;
this.journalLastUncommittedURL = url;
}
if ( this.journalTimer !== null ) {
clearTimeout(this.journalTimer);
}
this.journalTimer = vAPI.setTimeout(this.journalProcess.bind(this, true), 1000);
};
PageStore.prototype.journalProcess = function(fromTimer) {
if ( !fromTimer ) {
clearTimeout(this.journalTimer);
}
this.journalTimer = null;
var journal = this.journal,
i, n = journal.length,
hostname, count, hostnameCounts,
aggregateCounts = 0,
now = Date.now(),
pivot = this.journalLastCommitted || 0;
// Everything after pivot originates from current page.
for ( i = pivot; i < n; i += 2 ) {
hostname = journal[i];
hostnameCounts = this.hostnameToCountMap.get(hostname);
if ( hostnameCounts === undefined ) {
hostnameCounts = 0;
this.contentLastModified = now;
}
count = journal[i+1];
this.hostnameToCountMap.set(hostname, hostnameCounts + count);
aggregateCounts += count;
}
this.perLoadBlockedRequestCount += aggregateCounts & 0xFFFF;
this.perLoadAllowedRequestCount += aggregateCounts >>> 16 & 0xFFFF;
this.journalLastCommitted = undefined;
// https://github.com/chrisaljoudi/uBlock/issues/905#issuecomment-76543649
// No point updating the badge if it's not being displayed.
if ( (aggregateCounts & 0xFFFF) && µb.userSettings.showIconBadge ) {
µb.updateBadgeAsync(this.tabId);
}
// Everything before pivot does not originate from current page -- we still
// need to bump global blocked/allowed counts.
for ( 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.localSettingsLastModified = now;
}
journal.length = 0;
};
/******************************************************************************/
PageStore.prototype.filterRequest = function(context) {
var requestType = context.requestType;
@ -625,34 +717,6 @@ PageStore.prototype.filterRequestNoCache = function(context) {
return result;
};
/******************************************************************************/
PageStore.prototype.logRequest = function(context, result) {
var requestHostname = context.requestHostname;
// rhill 20150206:
// be prepared to handle invalid requestHostname, I've seen this
// happen: http://./
if ( requestHostname === '' ) {
requestHostname = context.rootHostname;
}
var now = Date.now();
if ( this.hostnameToCountMap.hasOwnProperty(requestHostname) === false ) {
this.hostnameToCountMap[requestHostname] = 0;
this.contentLastModified = now;
}
var c = result.charAt(1);
if ( c === 'b' ) {
this.hostnameToCountMap[requestHostname] += 0x00000001;
this.perLoadBlockedRequestCount++;
µb.localSettings.blockedRequestCount++;
} else /* if ( c === '' || c === 'a' || c === 'n' ) */ {
this.hostnameToCountMap[requestHostname] += 0x00010000;
this.perLoadAllowedRequestCount++;
µb.localSettings.allowedRequestCount++;
}
µb.localSettingsModifyTime = now;
};
// https://www.youtube.com/watch?v=drW8p_dTLD4
/******************************************************************************/

View file

@ -50,13 +50,13 @@
var saveAfter = 4 * 60 * 1000;
var save = function() {
this.localSettingsSaveTime = Date.now();
this.localSettingsLastSaved = Date.now();
vAPI.storage.set(this.localSettings);
};
var onTimeout = function() {
var µb = µBlock;
if ( µb.localSettingsModifyTime > µb.localSettingsSaveTime ) {
if ( µb.localSettingsLastModified > µb.localSettingsLastSaved ) {
save.call(µb);
}
vAPI.setTimeout(onTimeout, saveAfter);

View file

@ -465,23 +465,10 @@ vAPI.tabs.onNavigation = function(details) {
if ( details.frameId !== 0 ) {
return;
}
var tabContext = µb.tabContextManager.commit(details.tabId, details.url);
var pageStore = µb.bindTabToPageStats(details.tabId, 'tabChanged');
// https://github.com/chrisaljoudi/uBlock/issues/630
// The hostname of the bound document must always be present in the
// mini-matrix. That's the best place I could find for the fix, all other
// options had bad side-effects or complications.
// TODO: Eventually, we will have to use an API to check whether a scheme
// is supported as I suspect we are going to start to see `ws`, `wss`
// as well soon.
if (
pageStore &&
tabContext.rawURL.startsWith('http') &&
pageStore.hostnameToCountMap.hasOwnProperty(tabContext.rootHostname) === false
) {
pageStore.hostnameToCountMap[tabContext.rootHostname] = 0x00010000;
µb.tabContextManager.commit(details.tabId, details.url);
var pageStore = µb.bindTabToPageStats(details.tabId, 'tabCommitted');
if ( pageStore ) {
pageStore.journalAddRootFrame('committed', details.url);
}
};
@ -778,7 +765,7 @@ vAPI.tabs.onPopupUpdated = (function() {
// filtering pane.
var pageStore = µb.pageStoreFromTabId(openerTabId);
if ( pageStore ) {
pageStore.logRequest(context, result);
pageStore.journalAddRequest(context.requestHostname, result);
pageStore.popupBlockedCount += 1;
}
@ -827,13 +814,13 @@ vAPI.tabs.registerListeners();
}
// https://github.com/chrisaljoudi/uBlock/issues/516
// Never rebind behind-the-scene scope
// Never rebind behind-the-scene scope.
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
return pageStore;
}
// https://github.com/gorhill/uBlock/issues/516
// If context if 'beforeRequest', do not rebind, wait for confirmation.
// https://github.com/chrisaljoudi/uBlock/issues/516
// If context is 'beforeRequest', do not rebind, wait for confirmation.
if ( context === 'beforeRequest' ) {
return pageStore;
}

View file

@ -89,9 +89,7 @@ var onBeforeRequest = function(details) {
var result = pageStore.filterRequest(requestContext);
// Possible outcomes: blocked, allowed-passthru, allowed-mirror
pageStore.logRequest(requestContext, result);
pageStore.journalAddRequest(requestContext.requestHostname, result);
if ( µb.logger.isEnabled() ) {
µb.logger.writeOne(
@ -117,12 +115,6 @@ var onBeforeRequest = function(details) {
// Blocked
// https://github.com/chrisaljoudi/uBlock/issues/905#issuecomment-76543649
// No point updating the badge if it's not being displayed.
if ( µb.userSettings.showIconBadge ) {
µb.updateBadgeAsync(tabId);
}
// https://github.com/gorhill/uBlock/issues/949
// Redirect blocked request?
var url = µb.redirectEngine.toURL(requestContext);
@ -222,7 +214,8 @@ var onBeforeRootFrameRequest = function(details) {
// Log
var pageStore = µb.bindTabToPageStats(tabId, 'beforeRequest');
if ( pageStore ) {
pageStore.logRequest(context, result);
pageStore.journalAddRootFrame('uncommitted', requestURL);
pageStore.journalAddRequest(requestHostname, result);
}
if ( µb.logger.isEnabled() ) {
@ -299,7 +292,7 @@ var onBeforeBeacon = function(details) {
context.requestType = details.type;
// "g" in "gb:" stands for "global setting"
var result = µb.userSettings.hyperlinkAuditingDisabled ? 'gb:' : '';
pageStore.logRequest(context, result);
pageStore.journalAddRequest(context.requestHostname, result);
if ( µb.logger.isEnabled() ) {
µb.logger.writeOne(
tabId,
@ -343,7 +336,7 @@ var onBeforeBehindTheSceneRequest = function(details) {
result = pageStore.filterRequestNoCache(context);
}
pageStore.logRequest(context, result);
pageStore.journalAddRequest(context.requestHostname, result);
if ( µb.logger.isEnabled() ) {
µb.logger.writeOne(