1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-02 17:19:38 +02:00
This commit is contained in:
gorhill 2014-12-26 15:26:44 -05:00
parent e42fbb0612
commit c0b9e634b3

View File

@ -21,12 +21,12 @@
/* jshint bitwise: false */
/* global µBlock */
'use strict';
/******************************************************************************/
µBlock.cosmeticFilteringEngine = (function(){
'use strict';
/******************************************************************************/
@ -294,14 +294,46 @@ FilterParser.prototype.parse = function(s) {
/******************************************************************************/
var SelectorCacheEntry = function() {
this.reset();
};
/******************************************************************************/
SelectorCacheEntry.junkyard = [];
SelectorCacheEntry.factory = function() {
var entry = SelectorCacheEntry.junkyard.pop();
if ( entry ) {
return entry.reset();
}
return new SelectorCacheEntry();
};
/******************************************************************************/
SelectorCacheEntry.prototype.netLowWaterMark = 20;
SelectorCacheEntry.prototype.netHighWaterMark = 30;
/******************************************************************************/
SelectorCacheEntry.prototype.reset = function() {
this.cosmetic = {};
this.net = {};
this.netCount = 0;
this.lastAccessTime = Date.now();
return this;
};
SelectorCacheEntry.prototype.netLowWaterMark = 20;
SelectorCacheEntry.prototype.netHighWaterMark = 30;
/******************************************************************************/
SelectorCacheEntry.prototype.dispose = function() {
this.cosmetic = this.net = null;
if ( SelectorCacheEntry.junkyard.length < 25 ) {
SelectorCacheEntry.junkyard.push(this);
}
};
/******************************************************************************/
SelectorCacheEntry.prototype.addCosmetic = function(selectors) {
var dict = this.cosmetic;
@ -311,6 +343,8 @@ SelectorCacheEntry.prototype.addCosmetic = function(selectors) {
}
};
/******************************************************************************/
SelectorCacheEntry.prototype.addNet = function(selectors) {
if ( typeof selectors === 'string' ) {
this.addNetOne(selectors, Date.now());
@ -333,6 +367,8 @@ SelectorCacheEntry.prototype.addNet = function(selectors) {
}
};
/******************************************************************************/
SelectorCacheEntry.prototype.addNetOne = function(selector, now) {
var dict = this.net;
if ( dict[selector] === undefined ) {
@ -341,6 +377,8 @@ SelectorCacheEntry.prototype.addNetOne = function(selector, now) {
dict[selector] = now;
};
/******************************************************************************/
SelectorCacheEntry.prototype.addNetMany = function(selectors, now) {
var dict = this.net;
var i = selectors.length || 0;
@ -354,6 +392,8 @@ SelectorCacheEntry.prototype.addNetMany = function(selectors, now) {
}
};
/******************************************************************************/
SelectorCacheEntry.prototype.add = function(selectors, type) {
this.lastAccessTime = Date.now();
if ( type === 'cosmetic' ) {
@ -363,6 +403,8 @@ SelectorCacheEntry.prototype.add = function(selectors, type) {
}
};
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/420
SelectorCacheEntry.prototype.remove = function(type) {
this.lastAccessTime = Date.now();
@ -374,6 +416,8 @@ SelectorCacheEntry.prototype.remove = function(type) {
}
};
/******************************************************************************/
SelectorCacheEntry.prototype.retrieve = function(type, out) {
this.lastAccessTime = Date.now();
var dict = type === 'cosmetic' ? this.cosmetic : this.net;
@ -463,6 +507,10 @@ var FilterContainer = function() {
this.type0NoDomainHash = 'type0NoDomain';
this.type1NoDomainHash = 'type1NoDomain';
this.parser = new FilterParser();
this.selectorCachePruneDelay = 5 * 60 * 1000; // 5 minutes
this.selectorCacheAgeMax = 20 * 60 * 1000; // 20 minutes
this.selectorCacheCountMin = 10;
this.selectorCacheTimer = null;
this.reset();
};
@ -478,8 +526,6 @@ FilterContainer.prototype.reset = function() {
this.selectorCache = {};
this.selectorCacheCount = 0;
this.selectorCacheLowWaterMark = 75;
this.selectorCacheHighWaterMark = 100;
// temporary (at parse time)
this.lowGenericHide = {};
@ -907,6 +953,23 @@ FilterContainer.prototype.fromSelfie = function(selfie) {
/******************************************************************************/
FilterContainer.prototype.triggerSelectorCachePruner = function() {
if ( this.selectorCacheTimer !== null ) {
return;
}
if ( this.selectorCacheCount <= this.selectorCacheCountMin ) {
return;
}
// Of interest: http://fitzgeraldnick.com/weblog/40/
// http://googlecode.blogspot.ca/2009/07/gmail-for-mobile-html5-series-using.html
this.selectorCacheTimer = setTimeout(
this.pruneSelectorCacheAsync.bind(this),
this.selectorCachePruneDelay
);
};
/******************************************************************************/
FilterContainer.prototype.addToSelectorCache = function(details) {
var hostname = details.hostname;
if ( typeof hostname !== 'string' || hostname === '' ) {
@ -918,11 +981,9 @@ FilterContainer.prototype.addToSelectorCache = function(details) {
}
var entry = this.selectorCache[hostname];
if ( entry === undefined ) {
entry = this.selectorCache[hostname] = new SelectorCacheEntry();
entry = this.selectorCache[hostname] = SelectorCacheEntry.factory();
this.selectorCacheCount += 1;
if ( this.selectorCacheCount > this.selectorCacheHighWaterMark ) {
this.pruneSelectorCache();
}
this.triggerSelectorCachePruner();
}
entry.add(selectors, details.type);
};
@ -953,17 +1014,33 @@ FilterContainer.prototype.retrieveFromSelectorCache = function(hostname, type, o
/******************************************************************************/
FilterContainer.prototype.pruneSelectorCache = function() {
var cache = this.selectorCache;
var hostnames = Object.keys(cache).sort(function(a ,b) {
return cache[b].lastAccessTime - cache[a].lastAccessTime;
});
var toRemove = hostnames.slice(this.selectorCacheLowWaterMark);
var i = toRemove.length;
while ( i-- ) {
delete cache[toRemove[i]];
FilterContainer.prototype.pruneSelectorCacheAsync = function() {
this.selectorCacheTimer = null;
if ( this.selectorCacheCount <= this.selectorCacheCountMin ) {
return;
}
this.selectorCacheCount -= toRemove.length;
var cache = this.selectorCache;
// Sorted from most-recently-used to least-recently-used, because
// we loop beginning at the end below.
// We can't avoid sorting because we have to keep a minimum number of
// entries, and these entries should always be the most-recently-used.
var hostnames = Object.keys(cache)
.sort(function(a, b) { return cache[b].lastAccessTime - cache[a].lastAccessTime; })
.slice(this.selectorCacheCountMin);
var obsolete = Date.now() - this.selectorCacheAgeMax;
var hostname, entry;
var i = hostnames.length;
while ( i-- ) {
hostname = hostnames[i];
entry = cache[hostname];
if ( entry.lastAccessTime > obsolete ) {
break;
}
// console.debug('pruneSelectorCacheAsync: flushing "%s"', hostname);
entry.dispose();
delete cache[hostname];
}
this.triggerSelectorCachePruner();
};
/******************************************************************************/