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, blockedRequestCount: 0,
allowedRequestCount: 0 allowedRequestCount: 0
}, },
localSettingsModifyTime: 0, localSettingsLastModified: 0,
localSettingsSaveTime: 0, localSettingsLastSaved: 0,
// read-only // read-only
systemSettings: { systemSettings: {

View file

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

View file

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

View file

@ -266,6 +266,10 @@ var pageStoreJunkyardMax = 10;
var PageStore = function(tabId) { var PageStore = function(tabId) {
this.init(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.tabHostname = tabContext.rootHostname;
this.title = tabContext.rawURL; this.title = tabContext.rawURL;
this.rawURL = tabContext.rawURL; this.rawURL = tabContext.rawURL;
this.hostnameToCountMap = {}; this.hostnameToCountMap = new Map();
this.contentLastModified = 0; this.contentLastModified = 0;
this.frames = Object.create(null); this.frames = Object.create(null);
this.perLoadBlockedRequestCount = 0; this.perLoadBlockedRequestCount = 0;
@ -388,10 +392,6 @@ PageStore.prototype.reuse = function(context) {
/******************************************************************************/ /******************************************************************************/
PageStore.prototype.dispose = function() { 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.tabHostname = '';
this.title = ''; this.title = '';
this.rawURL = ''; this.rawURL = '';
@ -403,6 +403,12 @@ PageStore.prototype.dispose = function() {
} }
this.disposeFrameStores(); this.disposeFrameStores();
this.netFilteringCache = this.netFilteringCache.dispose(); this.netFilteringCache = this.netFilteringCache.dispose();
if ( this.journalTimer !== null ) {
clearTimeout(this.journalTimer);
this.journalTimer = null;
}
this.journal = [];
this.journalLastUncommittedURL = undefined;
if ( pageStoreJunkyard.length < pageStoreJunkyardMax ) { if ( pageStoreJunkyard.length < pageStoreJunkyardMax ) {
pageStoreJunkyard.push(this); 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) { PageStore.prototype.filterRequest = function(context) {
var requestType = context.requestType; var requestType = context.requestType;
@ -625,34 +717,6 @@ PageStore.prototype.filterRequestNoCache = function(context) {
return result; 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 // https://www.youtube.com/watch?v=drW8p_dTLD4
/******************************************************************************/ /******************************************************************************/

View file

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

View file

@ -465,23 +465,10 @@ vAPI.tabs.onNavigation = function(details) {
if ( details.frameId !== 0 ) { if ( details.frameId !== 0 ) {
return; return;
} }
µb.tabContextManager.commit(details.tabId, details.url);
var tabContext = µb.tabContextManager.commit(details.tabId, details.url); var pageStore = µb.bindTabToPageStats(details.tabId, 'tabCommitted');
var pageStore = µb.bindTabToPageStats(details.tabId, 'tabChanged'); if ( pageStore ) {
pageStore.journalAddRootFrame('committed', details.url);
// 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;
} }
}; };
@ -778,7 +765,7 @@ vAPI.tabs.onPopupUpdated = (function() {
// filtering pane. // filtering pane.
var pageStore = µb.pageStoreFromTabId(openerTabId); var pageStore = µb.pageStoreFromTabId(openerTabId);
if ( pageStore ) { if ( pageStore ) {
pageStore.logRequest(context, result); pageStore.journalAddRequest(context.requestHostname, result);
pageStore.popupBlockedCount += 1; pageStore.popupBlockedCount += 1;
} }
@ -827,13 +814,13 @@ vAPI.tabs.registerListeners();
} }
// https://github.com/chrisaljoudi/uBlock/issues/516 // https://github.com/chrisaljoudi/uBlock/issues/516
// Never rebind behind-the-scene scope // Never rebind behind-the-scene scope.
if ( vAPI.isBehindTheSceneTabId(tabId) ) { if ( vAPI.isBehindTheSceneTabId(tabId) ) {
return pageStore; return pageStore;
} }
// https://github.com/gorhill/uBlock/issues/516 // https://github.com/chrisaljoudi/uBlock/issues/516
// If context if 'beforeRequest', do not rebind, wait for confirmation. // If context is 'beforeRequest', do not rebind, wait for confirmation.
if ( context === 'beforeRequest' ) { if ( context === 'beforeRequest' ) {
return pageStore; return pageStore;
} }

View file

@ -89,9 +89,7 @@ var onBeforeRequest = function(details) {
var result = pageStore.filterRequest(requestContext); var result = pageStore.filterRequest(requestContext);
// Possible outcomes: blocked, allowed-passthru, allowed-mirror pageStore.journalAddRequest(requestContext.requestHostname, result);
pageStore.logRequest(requestContext, result);
if ( µb.logger.isEnabled() ) { if ( µb.logger.isEnabled() ) {
µb.logger.writeOne( µb.logger.writeOne(
@ -117,12 +115,6 @@ var onBeforeRequest = function(details) {
// Blocked // 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 // https://github.com/gorhill/uBlock/issues/949
// Redirect blocked request? // Redirect blocked request?
var url = µb.redirectEngine.toURL(requestContext); var url = µb.redirectEngine.toURL(requestContext);
@ -222,7 +214,8 @@ var onBeforeRootFrameRequest = function(details) {
// Log // Log
var pageStore = µb.bindTabToPageStats(tabId, 'beforeRequest'); var pageStore = µb.bindTabToPageStats(tabId, 'beforeRequest');
if ( pageStore ) { if ( pageStore ) {
pageStore.logRequest(context, result); pageStore.journalAddRootFrame('uncommitted', requestURL);
pageStore.journalAddRequest(requestHostname, result);
} }
if ( µb.logger.isEnabled() ) { if ( µb.logger.isEnabled() ) {
@ -299,7 +292,7 @@ var onBeforeBeacon = function(details) {
context.requestType = details.type; context.requestType = details.type;
// "g" in "gb:" stands for "global setting" // "g" in "gb:" stands for "global setting"
var result = µb.userSettings.hyperlinkAuditingDisabled ? 'gb:' : ''; var result = µb.userSettings.hyperlinkAuditingDisabled ? 'gb:' : '';
pageStore.logRequest(context, result); pageStore.journalAddRequest(context.requestHostname, result);
if ( µb.logger.isEnabled() ) { if ( µb.logger.isEnabled() ) {
µb.logger.writeOne( µb.logger.writeOne(
tabId, tabId,
@ -343,7 +336,7 @@ var onBeforeBehindTheSceneRequest = function(details) {
result = pageStore.filterRequestNoCache(context); result = pageStore.filterRequestNoCache(context);
} }
pageStore.logRequest(context, result); pageStore.journalAddRequest(context.requestHostname, result);
if ( µb.logger.isEnabled() ) { if ( µb.logger.isEnabled() ) {
µb.logger.writeOne( µb.logger.writeOne(