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

this addresses #184

This commit is contained in:
gorhill 2014-09-08 17:46:58 -04:00
parent 70533b93fe
commit 65775a5e27
13 changed files with 1008 additions and 244 deletions

View File

@ -380,7 +380,7 @@ var reloadAll = function(update) {
/******************************************************************************/
var buttonApplyHandler = function() {
reloadAll();
reloadAll(false);
uDom('#buttonApply').toggleClass('enabled', false);
};

View File

@ -24,7 +24,7 @@
/******************************************************************************/
µBlock.abpFilters = (function(){
µBlock.netFilteringEngine = (function(){
/******************************************************************************/
@ -137,6 +137,19 @@ var histogram = function(label, categories) {
console.log('\tTotal buckets count: %d', total);
};
*/
/******************************************************************************/
// Could be replaced with encodeURIComponent/decodeURIComponent,
// which seems faster on Firefox.
var encode = JSON.stringify;
var decode = JSON.parse;
var cachedParseInt = parseInt;
var atoi = function(s) {
return cachedParseInt(s, 10);
};
/*******************************************************************************
Filters family tree:
@ -187,10 +200,22 @@ FilterPlain.prototype.match = function(url, tokenBeg) {
return url.substr(tokenBeg - this.tokenBeg, this.s.length) === this.s;
};
FilterPlain.prototype.fid = 'a';
FilterPlain.prototype.toString = function() {
return this.s;
};
FilterPlain.prototype.toSelfie = function() {
return this.s + '\t' +
this.tokenBeg;
};
FilterPlain.fromSelfie = function(s) {
var pos = s.indexOf('\t');
return new FilterPlain(s.slice(0, pos), atoi(s.slice(pos + 1)));
};
/******************************************************************************/
var FilterPlainHostname = function(s, tokenBeg, hostname) {
@ -204,10 +229,23 @@ FilterPlainHostname.prototype.match = function(url, tokenBeg) {
url.substr(tokenBeg - this.tokenBeg, this.s.length) === this.s;
};
FilterPlainHostname.prototype.fid = 'ah';
FilterPlainHostname.prototype.toString = function() {
return this.s + '$domain=' + this.hostname;
};
FilterPlainHostname.prototype.toSelfie = function() {
return this.s + '\t' +
this.tokenBeg + '\t' +
this.hostname;
};
FilterPlainHostname.fromSelfie = function(s) {
var args = s.split('\t');
return new FilterPlainHostname(args[0], atoi(args[1]), args[2]);
};
/******************************************************************************/
var FilterPlainPrefix0 = function(s) {
@ -218,10 +256,20 @@ FilterPlainPrefix0.prototype.match = function(url, tokenBeg) {
return url.substr(tokenBeg, this.s.length) === this.s;
};
FilterPlainPrefix0.prototype.fid = '0a';
FilterPlainPrefix0.prototype.toString = function() {
return this.s;
};
FilterPlainPrefix0.prototype.toSelfie = function() {
return this.s;
};
FilterPlainPrefix0.fromSelfie = function(s) {
return new FilterPlainPrefix0(s);
};
/******************************************************************************/
var FilterPlainPrefix0Hostname = function(s, hostname) {
@ -234,10 +282,22 @@ FilterPlainPrefix0Hostname.prototype.match = function(url, tokenBeg) {
url.substr(tokenBeg, this.s.length) === this.s;
};
FilterPlainPrefix0Hostname.prototype.fid = '0ah';
FilterPlainPrefix0Hostname.prototype.toString = function() {
return this.s + '$domain=' + this.hostname;
};
FilterPlainPrefix0Hostname.prototype.toSelfie = function() {
return this.s + '\t' +
this.hostname;
};
FilterPlainPrefix0Hostname.fromSelfie = function(s) {
var pos = s.indexOf('\t');
return new FilterPlainPrefix0Hostname(s.slice(0, pos), s.slice(pos + 1));
};
/******************************************************************************/
var FilterPlainPrefix1 = function(s) {
@ -248,10 +308,20 @@ FilterPlainPrefix1.prototype.match = function(url, tokenBeg) {
return url.substr(tokenBeg - 1, this.s.length) === this.s;
};
FilterPlainPrefix1.prototype.fid = '1a';
FilterPlainPrefix1.prototype.toString = function() {
return this.s;
};
FilterPlainPrefix1.prototype.toSelfie = function() {
return this.s;
};
FilterPlainPrefix1.fromSelfie = function(s) {
return new FilterPlainPrefix1(s);
};
/******************************************************************************/
var FilterPlainPrefix1Hostname = function(s, hostname) {
@ -264,10 +334,22 @@ FilterPlainPrefix1Hostname.prototype.match = function(url, tokenBeg) {
url.substr(tokenBeg - 1, this.s.length) === this.s;
};
FilterPlainPrefix1Hostname.prototype.fid = '1ah';
FilterPlainPrefix1Hostname.prototype.toString = function() {
return this.s + '$domain=' + this.hostname;
};
FilterPlainPrefix1Hostname.prototype.toSelfie = function() {
return this.s + '\t' +
this.hostname;
};
FilterPlainPrefix1Hostname.fromSelfie = function(s) {
var pos = s.indexOf('\t');
return new FilterPlainPrefix1Hostname(s.slice(0, pos), s.slice(pos + 1));
};
/******************************************************************************/
var FilterPlainLeftAnchored = function(s) {
@ -278,10 +360,20 @@ FilterPlainLeftAnchored.prototype.match = function(url) {
return url.slice(0, this.s.length) === this.s;
};
FilterPlainLeftAnchored.prototype.fid = '|a';
FilterPlainLeftAnchored.prototype.toString = function() {
return '|' + this.s;
};
FilterPlainLeftAnchored.prototype.toSelfie = function() {
return this.s;
};
FilterPlainLeftAnchored.fromSelfie = function(s) {
return new FilterPlainLeftAnchored(s);
};
/******************************************************************************/
var FilterPlainLeftAnchoredHostname = function(s, hostname) {
@ -294,10 +386,22 @@ FilterPlainLeftAnchoredHostname.prototype.match = function(url) {
url.slice(0, this.s.length) === this.s;
};
FilterPlainLeftAnchoredHostname.prototype.fid = '|ah';
FilterPlainLeftAnchoredHostname.prototype.toString = function() {
return '|' + this.s + '$domain=' + this.hostname;
};
FilterPlainLeftAnchoredHostname.prototype.toSelfie = function() {
return this.s + '\t' +
this.hostname;
};
FilterPlainLeftAnchoredHostname.fromSelfie = function(s) {
var pos = s.indexOf('\t');
return new FilterPlainLeftAnchoredHostname(s.slice(0, pos), s.slice(pos + 1));
};
/******************************************************************************/
var FilterPlainRightAnchored = function(s) {
@ -308,10 +412,20 @@ FilterPlainRightAnchored.prototype.match = function(url) {
return url.slice(-this.s.length) === this.s;
};
FilterPlainRightAnchored.prototype.fid = 'a|';
FilterPlainRightAnchored.prototype.toString = function() {
return this.s + '|';
};
FilterPlainRightAnchored.prototype.toSelfie = function() {
return this.s;
};
FilterPlainRightAnchored.fromSelfie = function(s) {
return new FilterPlainRightAnchored(s);
};
/******************************************************************************/
var FilterPlainRightAnchoredHostname = function(s, hostname) {
@ -324,10 +438,22 @@ FilterPlainRightAnchoredHostname.prototype.match = function(url) {
url.slice(-this.s.length) === this.s;
};
FilterPlainRightAnchoredHostname.prototype.fid = 'a|h';
FilterPlainRightAnchoredHostname.prototype.toString = function() {
return this.s + '|$domain=' + this.hostname;
};
FilterPlainRightAnchoredHostname.prototype.toSelfie = function() {
return this.s + '\t' +
this.hostname;
};
FilterPlainRightAnchoredHostname.fromSelfie = function(s) {
var pos = s.indexOf('\t');
return new FilterPlainRightAnchoredHostname(s.slice(0, pos), s.slice(pos + 1));
};
/******************************************************************************/
// With a single wildcard, regex is not optimal.
@ -335,11 +461,10 @@ FilterPlainRightAnchoredHostname.prototype.toString = function() {
// http://jsperf.com/regexp-vs-indexof-abp-miss/3
// http://jsperf.com/regexp-vs-indexof-abp-hit/3
var FilterSingleWildcard = function(s, tokenBeg) {
var FilterSingleWildcard = function(lSegment, rSegment, tokenBeg) {
this.tokenBeg = tokenBeg;
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
this.lSegment = lSegment;
this.rSegment = rSegment;
};
FilterSingleWildcard.prototype.match = function(url, tokenBeg) {
@ -348,17 +473,29 @@ FilterSingleWildcard.prototype.match = function(url, tokenBeg) {
url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
};
FilterSingleWildcard.prototype.fid = '*';
FilterSingleWildcard.prototype.toString = function() {
return this.lSegment + '*' + this.rSegment;
};
FilterSingleWildcard.prototype.toSelfie = function() {
return this.lSegment + '\t' +
this.rSegment + '\t' +
this.tokenBeg;
};
FilterSingleWildcard.fromSelfie = function(s) {
var args = s.split('\t');
return new FilterSingleWildcard(args[0], args[1], atoi(args[2]));
};
/******************************************************************************/
var FilterSingleWildcardHostname = function(s, tokenBeg, hostname) {
var FilterSingleWildcardHostname = function(lSegment, rSegment, tokenBeg, hostname) {
this.tokenBeg = tokenBeg;
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
this.lSegment = lSegment;
this.rSegment = rSegment;
this.hostname = hostname;
};
@ -369,16 +506,29 @@ FilterSingleWildcardHostname.prototype.match = function(url, tokenBeg) {
url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
};
FilterSingleWildcardHostname.prototype.fid = '*h';
FilterSingleWildcardHostname.prototype.toString = function() {
return this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname;
};
FilterSingleWildcardHostname.prototype.toSelfie = function() {
return this.lSegment + '\t' +
this.rSegment + '\t' +
this.tokenBeg + '\t' +
this.hostname;
};
FilterSingleWildcardHostname.fromSelfie = function(s) {
var args = s.split('\t');
return new FilterSingleWildcardHostname(args[0], args[1], atoi(args[2]), args[3]);
};
/******************************************************************************/
var FilterSingleWildcardPrefix0 = function(s) {
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
var FilterSingleWildcardPrefix0 = function(lSegment, rSegment) {
this.lSegment = lSegment;
this.rSegment = rSegment;
};
FilterSingleWildcardPrefix0.prototype.match = function(url, tokenBeg) {
@ -386,16 +536,27 @@ FilterSingleWildcardPrefix0.prototype.match = function(url, tokenBeg) {
url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
};
FilterSingleWildcardPrefix0.prototype.fid = '0*';
FilterSingleWildcardPrefix0.prototype.toString = function() {
return this.lSegment + '*' + this.rSegment;
};
FilterSingleWildcardPrefix0.prototype.toSelfie = function() {
return this.lSegment + '\t' +
this.rSegment;
};
FilterSingleWildcardPrefix0.fromSelfie = function(s) {
var pos = s.indexOf('\t');
return new FilterSingleWildcardPrefix0(s.slice(0, pos), s.slice(pos + 1));
};
/******************************************************************************/
var FilterSingleWildcardPrefix0Hostname = function(s, hostname) {
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
var FilterSingleWildcardPrefix0Hostname = function(lSegment, rSegment, hostname) {
this.lSegment = lSegment;
this.rSegment = rSegment;
this.hostname = hostname;
};
@ -405,21 +566,28 @@ FilterSingleWildcardPrefix0Hostname.prototype.match = function(url, tokenBeg) {
url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
};
FilterSingleWildcardPrefix0Hostname.prototype.fid = '0*h';
FilterSingleWildcardPrefix0Hostname.prototype.toString = function() {
return this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname;
};
FilterSingleWildcardPrefix0Hostname.prototype.toSelfie = function() {
return this.lSegment + '\t' +
this.rSegment + '\t' +
this.hostname;
};
FilterSingleWildcardPrefix0Hostname.fromSelfie = function(s) {
var args = s.split('\t');
return new FilterSingleWildcardPrefix0Hostname(args[0], args[1], args[2]);
};
/******************************************************************************/
// With a single wildcard, regex is not optimal.
// See:
// http://jsperf.com/regexp-vs-indexof-abp-miss/3
// http://jsperf.com/regexp-vs-indexof-abp-hit/3
var FilterSingleWildcardLeftAnchored = function(s) {
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
var FilterSingleWildcardLeftAnchored = function(lSegment, rSegment) {
this.lSegment = lSegment;
this.rSegment = rSegment;
};
FilterSingleWildcardLeftAnchored.prototype.match = function(url) {
@ -427,16 +595,27 @@ FilterSingleWildcardLeftAnchored.prototype.match = function(url) {
url.indexOf(this.rSegment, this.lSegment.length) > 0;
};
FilterSingleWildcardLeftAnchored.prototype.fid = '|*';
FilterSingleWildcardLeftAnchored.prototype.toString = function() {
return '|' + this.lSegment + '*' + this.rSegment;
};
FilterSingleWildcardLeftAnchored.prototype.toSelfie = function() {
return this.lSegment + '\t' +
this.rSegment;
};
FilterSingleWildcardLeftAnchored.fromSelfie = function(s) {
var pos = s.indexOf('\t');
return new FilterSingleWildcardLeftAnchored(s.slice(0, pos), s.slice(pos + 1));
};
/******************************************************************************/
var FilterSingleWildcardLeftAnchoredHostname = function(s, hostname) {
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
var FilterSingleWildcardLeftAnchoredHostname = function(lSegment, rSegment, hostname) {
this.lSegment = lSegment;
this.rSegment = rSegment;
this.hostname = hostname;
};
@ -446,21 +625,28 @@ FilterSingleWildcardLeftAnchoredHostname.prototype.match = function(url) {
url.indexOf(this.rSegment, this.lSegment.length) > 0;
};
FilterSingleWildcardLeftAnchoredHostname.prototype.fid = '|*h';
FilterSingleWildcardLeftAnchoredHostname.prototype.toString = function() {
return '|' + this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname;
};
FilterSingleWildcardLeftAnchoredHostname.prototype.toSelfie = function() {
return this.lSegment + '\t' +
this.rSegment + '\t' +
this.hostname;
};
FilterSingleWildcardLeftAnchoredHostname.fromSelfie = function(s) {
var args = s.split('\t');
return new FilterSingleWildcardLeftAnchoredHostname(args[0], args[1], args[2]);
};
/******************************************************************************/
// With a single wildcard, regex is not optimal.
// See:
// http://jsperf.com/regexp-vs-indexof-abp-miss/3
// http://jsperf.com/regexp-vs-indexof-abp-hit/3
var FilterSingleWildcardRightAnchored = function(s) {
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
var FilterSingleWildcardRightAnchored = function(lSegment, rSegment) {
this.lSegment = lSegment;
this.rSegment = rSegment;
};
FilterSingleWildcardRightAnchored.prototype.match = function(url) {
@ -468,16 +654,27 @@ FilterSingleWildcardRightAnchored.prototype.match = function(url) {
url.lastIndexOf(this.lSegment, url.length - this.rSegment.length - this.lSegment.length) >= 0;
};
FilterSingleWildcardRightAnchored.prototype.fid = '*|';
FilterSingleWildcardRightAnchored.prototype.toString = function() {
return this.lSegment + '*' + this.rSegment + '|';
};
FilterSingleWildcardRightAnchored.prototype.toSelfie = function() {
return this.lSegment + '\t' +
this.rSegment;
};
FilterSingleWildcardRightAnchored.fromSelfie = function(s) {
var pos = s.indexOf('\t');
return new FilterSingleWildcardRightAnchored(s.slice(0, pos), s.slice(pos + 1));
};
/******************************************************************************/
var FilterSingleWildcardRightAnchoredHostname = function(s, hostname) {
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
var FilterSingleWildcardRightAnchoredHostname = function(lSegment, rSegment, hostname) {
this.lSegment = lSegment;
this.rSegment = rSegment;
this.hostname = hostname;
};
@ -487,10 +684,23 @@ FilterSingleWildcardRightAnchoredHostname.prototype.match = function(url) {
url.lastIndexOf(this.lSegment, url.length - this.rSegment.length - this.lSegment.length) >= 0;
};
FilterSingleWildcardRightAnchoredHostname.prototype.fid = '*|h';
FilterSingleWildcardRightAnchoredHostname.prototype.toString = function() {
return this.lSegment + '*' + this.rSegment + '|$domain=' + this.hostname;
};
FilterSingleWildcardRightAnchoredHostname.prototype.toSelfie = function() {
return this.lSegment + '\t' +
this.rSegment + '\t' +
this.hostname;
};
FilterSingleWildcardRightAnchoredHostname.fromSelfie = function(s) {
var args = s.split('\t');
return new FilterSingleWildcardRightAnchoredHostname(args[0], args[1], args[2]);
};
/******************************************************************************/
// With many wildcards, a regex is best.
@ -509,10 +719,22 @@ FilterManyWildcards.prototype.match = function(url, tokenBeg) {
return this.re.test(url.slice(tokenBeg - this.tokenBeg));
};
FilterManyWildcards.prototype.fid = '*+';
FilterManyWildcards.prototype.toString = function() {
return this.s;
};
FilterManyWildcards.prototype.toSelfie = function() {
return this.s + '\t' +
this.tokenBeg;
};
FilterManyWildcards.fromSelfie = function(s) {
var pos = s.indexOf('\t');
return new FilterManyWildcards(s.slice(0, pos), atoi(s.slice(pos + 1)));
};
/******************************************************************************/
var FilterManyWildcardsHostname = function(s, tokenBeg, hostname) {
@ -527,29 +749,90 @@ FilterManyWildcardsHostname.prototype.match = function(url, tokenBeg) {
this.re.test(url.slice(tokenBeg - this.tokenBeg));
};
FilterManyWildcardsHostname.prototype.fid = '*+h';
FilterManyWildcardsHostname.prototype.toString = function() {
return this.s + '$domain=' + this.hostname;
};
FilterManyWildcardsHostname.prototype.toSelfie = function() {
return this.s + '\t' +
this.tokenBeg + '\t' +
this.hostname;
};
FilterManyWildcardsHostname.fromSelfie = function(s) {
var args = s.split('\t');
return new FilterManyWildcardsHostname(args[0], atoi(args[1]), args[2]);
};
/******************************************************************************/
var FilterBucket = function(a, b) {
this.f = null;
this.filters = [];
if ( a !== undefined ) {
this.filters[0] = a;
if ( b !== undefined ) {
this.filters[1] = b;
}
}
};
FilterBucket.prototype.add = function(a) {
this.filters.push(a);
};
FilterBucket.prototype.match = function(url, tokenBeg) {
var filters = this.filters;
var i = filters.length;
while ( i-- ) {
if ( filters[i].match(url, tokenBeg) !== false ) {
this.f = filters[i];
return true;
}
}
return false;
};
FilterBucket.prototype.fid = '[]';
FilterBucket.prototype.toString = function() {
if ( this.f !== null ) {
return this.f.toString();
}
return '';
};
FilterBucket.prototype.toSelfie = function() {
return this.filters.length.toString();
};
FilterBucket.fromSelfie = function() {
return new FilterBucket();
};
/******************************************************************************/
var makeFilter = function(details, tokenBeg) {
var s = details.f;
var wcOffset = s.indexOf('*');
if ( wcOffset > 0 ) {
if ( (/\*[^*]\*/).test(s) ) {
if ( wcOffset !== -1 ) {
if ( s.indexOf('*', wcOffset + 1) !== -1 ) {
return details.anchor === 0 ? new FilterManyWildcards(s, tokenBeg) : null;
}
var lSegment = s.slice(0, wcOffset);
var rSegment = s.slice(wcOffset + 1);
if ( details.anchor < 0 ) {
return new FilterSingleWildcardLeftAnchored(s);
return new FilterSingleWildcardLeftAnchored(lSegment, rSegment);
}
if ( details.anchor > 0 ) {
return new FilterSingleWildcardRightAnchored(s);
return new FilterSingleWildcardRightAnchored(lSegment, rSegment);
}
if ( tokenBeg === 0 ) {
return new FilterSingleWildcardPrefix0(s);
return new FilterSingleWildcardPrefix0(lSegment, rSegment);
}
return new FilterSingleWildcard(s, tokenBeg);
return new FilterSingleWildcard(lSegment, rSegment, tokenBeg);
}
if ( details.anchor < 0 ) {
return new FilterPlainLeftAnchored(s);
@ -571,20 +854,22 @@ var makeFilter = function(details, tokenBeg) {
var makeHostnameFilter = function(details, tokenBeg, hostname) {
var s = details.f;
var wcOffset = s.indexOf('*');
if ( wcOffset > 0 ) {
if ( (/\*[^*]\*/).test(s) ) {
if ( wcOffset !== -1 ) {
if ( s.indexOf('*', wcOffset + 1) !== -1 ) {
return details.anchor === 0 ? new FilterManyWildcardsHostname(s, tokenBeg, hostname) : null;
}
var lSegment = s.slice(0, wcOffset);
var rSegment = s.slice(wcOffset + 1);
if ( details.anchor < 0 ) {
return new FilterSingleWildcardLeftAnchoredHostname(s, hostname);
return new FilterSingleWildcardLeftAnchoredHostname(lSegment, rSegment, hostname);
}
if ( details.anchor > 0 ) {
return new FilterSingleWildcardRightAnchoredHostname(s, hostname);
return new FilterSingleWildcardRightAnchoredHostname(lSegment, rSegment, hostname);
}
if ( tokenBeg === 0 ) {
return new FilterSingleWildcardPrefix0Hostname(s, hostname);
return new FilterSingleWildcardPrefix0Hostname(lSegment, rSegment, hostname);
}
return new FilterSingleWildcardHostname(s, tokenBeg, hostname);
return new FilterSingleWildcardHostname(lSegment, rSegment, tokenBeg, hostname);
}
if ( details.anchor < 0 ) {
return new FilterPlainLeftAnchoredHostname(s, hostname);
@ -613,6 +898,7 @@ var badTokens = {
'com': true,
'http': true,
'https': true,
'icon': true,
'images': true,
'img': true,
'js': true,
@ -661,6 +947,7 @@ var trimChar = function(s, c) {
return s;
};
/******************************************************************************/
/******************************************************************************/
var FilterParser = function() {
@ -783,6 +1070,19 @@ FilterParser.prototype.parse = function(s) {
s = s.slice(2);
}
// options
var pos = s.indexOf('$');
if ( pos > 0 ) {
this.fopts = s.slice(pos + 1);
s = s.slice(0, pos);
}
// regex? (not supported)
if ( s.charAt(0) === '/' && s.slice(-1) === '/' ) {
this.unsupported = true;
return this;
}
// hostname anchoring
if ( s.slice(0, 2) === '||' ) {
this.hostname = true;
@ -795,13 +1095,6 @@ FilterParser.prototype.parse = function(s) {
s = s.slice(1);
}
// options
var pos = s.indexOf('$');
if ( pos > 0 ) {
this.fopts = s.slice(pos + 1);
s = s.slice(0, pos);
}
// right-anchored
if ( s.slice(-1) === '|' ) {
this.anchor = 1;
@ -859,37 +1152,6 @@ FilterParser.prototype.parse = function(s) {
/******************************************************************************/
/******************************************************************************/
var FilterBucket = function(a, b) {
this.filters = [a, b];
this.f = null;
};
FilterBucket.prototype.add = function(a) {
this.filters.push(a);
};
FilterBucket.prototype.match = function(url, tokenBeg) {
var filters = this.filters;
var i = filters.length;
while ( i-- ) {
if ( filters[i].match(url, tokenBeg) !== false ) {
this.f = filters[i];
return true;
}
}
return false;
};
FilterBucket.prototype.toString = function() {
if ( this.f !== null ) {
return this.f.toString();
}
return '';
};
/******************************************************************************/
/******************************************************************************/
var FilterContainer = function() {
this.reAnyToken = /[%0-9a-z]+/g;
this.buckets = new Array(8);
@ -908,6 +1170,7 @@ FilterContainer.prototype.reset = function() {
this.frozen = false;
this.processedFilterCount = 0;
this.acceptedCount = 0;
this.rejectedCount = 0;
this.allowFilterCount = 0;
this.blockFilterCount = 0;
this.duplicateCount = 0;
@ -931,6 +1194,136 @@ FilterContainer.prototype.freeze = function() {
/******************************************************************************/
FilterContainer.prototype.toSelfie = function() {
var categoryToSelfie = function(dict) {
var selfie = [];
var bucket, ff, n, i, f;
for ( var k in dict ) {
if ( dict.hasOwnProperty(k) === false ) {
continue;
}
// We need to encode the key because there could be a `\n` or '\t'
// character in it, which would trip the code at parse time.
selfie.push('k2\t' + encode(k));
bucket = dict[k];
selfie.push(bucket.fid + '\t' + bucket.toSelfie());
if ( bucket.fid !== '[]' ) {
continue;
}
ff = bucket.filters;
n = ff.length;
for ( i = 0; i < n; i++ ) {
f = ff[i];
selfie.push(f.fid + '\t' + f.toSelfie());
}
}
return selfie.join('\n');
};
var categoriesToSelfie = function(dict) {
var selfie = [];
for ( var k in dict ) {
if ( dict.hasOwnProperty(k) === false ) {
continue;
}
// We need to encode the key because there could be a `\n` or '\t'
// character in it, which would trip the code at parse time.
selfie.push('k1\t' + encode(k));
selfie.push(categoryToSelfie(dict[k]));
}
return selfie.join('\n');
};
return {
processedFilterCount: this.processedFilterCount,
acceptedCount: this.acceptedCount,
rejectedCount: this.rejectedCount,
allowFilterCount: this.allowFilterCount,
blockFilterCount: this.blockFilterCount,
duplicateCount: this.duplicateCount,
categories: categoriesToSelfie(this.categories),
blockedAnyPartyHostnames: this.blockedAnyPartyHostnames.toSelfie(),
blocked3rdPartyHostnames: this.blocked3rdPartyHostnames.toSelfie()
};
};
/******************************************************************************/
FilterContainer.prototype.fromSelfie = function(selfie) {
this.frozen = true;
this.processedFilterCount = selfie.processedFilterCount;
this.acceptedCount = selfie.acceptedCount;
this.rejectedCount = selfie.rejectedCount;
this.allowFilterCount = selfie.allowFilterCount;
this.blockFilterCount = selfie.blockFilterCount;
this.duplicateCount = selfie.duplicateCount;
this.blockedAnyPartyHostnames.fromSelfie(selfie.blockedAnyPartyHostnames);
this.blocked3rdPartyHostnames.fromSelfie(selfie.blocked3rdPartyHostnames);
var factories = {
'[]': FilterBucket,
'a': FilterPlain,
'ah': FilterPlainHostname,
'0a': FilterPlainPrefix0,
'0ah': FilterPlainPrefix0Hostname,
'1a': FilterPlainPrefix1,
'1ah': FilterPlainPrefix1Hostname,
'|a': FilterPlainLeftAnchored,
'|ah': FilterPlainLeftAnchoredHostname,
'a|': FilterPlainRightAnchored,
'a|h': FilterPlainRightAnchoredHostname,
'*': FilterSingleWildcard,
'*h': FilterSingleWildcardHostname,
'0*': FilterSingleWildcardPrefix0,
'0*h': FilterSingleWildcardPrefix0Hostname,
'|*': FilterSingleWildcardLeftAnchored,
'|*h': FilterSingleWildcardLeftAnchoredHostname,
'*|': FilterSingleWildcardRightAnchored,
'*|h': FilterSingleWildcardRightAnchoredHostname,
'*+': FilterManyWildcards,
'*+h': FilterManyWildcardsHostname
};
var catKey, tokenKey;
var dict = this.categories, subdict;
var bucket = null;
var rawText = selfie.categories;
var rawEnd = rawText.length;
var lineBeg = 0, lineEnd;
var line, pos, what, factory;
while ( lineBeg < rawEnd ) {
lineEnd = rawText.indexOf('\n', lineBeg);
if ( lineEnd < 0 ) {
lineEnd = rawEnd;
}
line = rawText.slice(lineBeg, lineEnd);
lineBeg = lineEnd + 1;
pos = line.indexOf('\t');
what = line.slice(0, pos);
if ( what === 'k1' ) {
catKey = decode(line.slice(pos + 1));
subdict = dict[catKey] = {};
bucket = null;
continue;
}
if ( what === 'k2' ) {
tokenKey = decode(line.slice(pos + 1));
bucket = null;
continue;
}
factory = factories[what];
if ( bucket === null ) {
bucket = subdict[tokenKey] = factory.fromSelfie(line.slice(pos + 1));
continue;
}
// When token key is reused, it can't be anything
// else than FilterBucket
bucket.add(factory.fromSelfie(line.slice(pos + 1)));
}
};
/******************************************************************************/
FilterContainer.prototype.toDomainBits = function(domain) {
if ( domain === undefined ) {
return 0;
@ -989,6 +1382,13 @@ FilterContainer.prototype.add = function(s) {
var parsed = this.filterParser.parse(s);
// Ignore rules with other conditions for now
if ( parsed.unsupported ) {
this.rejectedCount += 1;
// console.log('µBlock> abp-filter.js/FilterContainer.add(): unsupported filter "%s"', s);
return false;
}
// Ignore element-hiding filters
if ( parsed.elemHiding ) {
return false;
@ -1002,12 +1402,6 @@ FilterContainer.prototype.add = function(s) {
this.processedFilterCount += 1;
// Ignore rules with other conditions for now
if ( parsed.unsupported ) {
// console.log('µBlock> abp-filter.js/FilterContainer.add(): unsupported filter "%s"', s);
return false;
}
// Ignore optionless hostname rules, these will be taken care of by µBlock.
if ( parsed.hostname && parsed.fopts === '' && parsed.action === BlockAction && reHostnameRule.test(parsed.f) ) {
return false;
@ -1145,7 +1539,7 @@ FilterContainer.prototype.addToCategory = function(category, tokenKey, filter) {
categoryBucket[tokenKey] = filter;
return;
}
if ( filterEntry instanceof FilterBucket ) {
if ( filterEntry.fid === '[]' ) {
filterEntry.add(filter);
return;
}

View File

@ -24,13 +24,20 @@
/******************************************************************************/
µBlock.abpHideFilters = (function(){
µBlock.cosmeticFilteringEngine = (function(){
/******************************************************************************/
var µb = µBlock;
/******************************************************************************/
// Could be replaced with encodeURIComponent/decodeURIComponent,
// which seems faster on Firefox.
var encode = JSON.stringify;
var decode = JSON.parse;
/******************************************************************************/
/*
var histogram = function(label, buckets) {
@ -86,6 +93,16 @@ FilterPlain.prototype.retrieve = function(s, out) {
}
};
FilterPlain.prototype.fid = '#';
FilterPlain.prototype.toSelfie = function() {
return this.s;
};
FilterPlain.fromSelfie = function(s) {
return new FilterPlain(s);
};
/******************************************************************************/
// Id- and class-based filters with extra selector stuff following.
@ -104,6 +121,50 @@ FilterPlainMore.prototype.retrieve = function(s, out) {
}
};
FilterPlainMore.prototype.fid = '#+';
FilterPlainMore.prototype.toSelfie = function() {
return this.s;
};
FilterPlainMore.fromSelfie = function(s) {
return new FilterPlainMore(s);
};
/******************************************************************************/
var FilterBucket = function(a, b) {
this.f = null;
this.filters = [];
if ( a !== undefined ) {
this.filters[0] = a;
if ( b !== undefined ) {
this.filters[1] = b;
}
}
};
FilterBucket.prototype.add = function(a) {
this.filters.push(a);
};
FilterBucket.prototype.retrieve = function(s, out) {
var i = this.filters.length;
while ( i-- ) {
this.filters[i].retrieve(s, out);
}
};
FilterBucket.prototype.fid = '[]';
FilterBucket.prototype.toSelfie = function() {
return this.filters.length.toString();
};
FilterBucket.fromSelfie = function() {
return new FilterBucket();
};
/******************************************************************************/
// Any selector specific to a hostname
@ -127,6 +188,17 @@ FilterHostname.prototype.retrieve = function(hostname, out) {
}
};
FilterHostname.prototype.fid = 'h';
FilterHostname.prototype.toSelfie = function() {
return encode(this.s) + '\t' + this.hostname;
};
FilterHostname.fromSelfie = function(s) {
var pos = s.indexOf('\t');
return new FilterHostname(decode(s.slice(0, pos)), s.slice(pos + 1));
};
/******************************************************************************/
// Any selector specific to an entity
@ -144,28 +216,15 @@ FilterEntity.prototype.retrieve = function(entity, out) {
}
};
/******************************************************************************/
/******************************************************************************/
FilterEntity.prototype.fid = 'e';
// TODO: evaluate the gain (if any) from avoiding the use of an array for when
// there are only two filters (or three, etc.). I suppose there is a specific
// number of filters below which using an array is more of an overhead than
// using a couple of property members.
// i.e. FilterBucket2, FilterBucket3, FilterBucketN.
var FilterBucket = function(a, b) {
this.filters = [a, b];
FilterEntity.prototype.toSelfie = function() {
return encode(this.s) + '\t' + this.entity;
};
FilterBucket.prototype.add = function(a) {
this.filters.push(a);
};
FilterBucket.prototype.retrieve = function(s, out) {
var i = this.filters.length;
while ( i-- ) {
this.filters[i].retrieve(s, out);
}
FilterEntity.fromSelfie = function(s) {
var pos = s.indexOf('\t');
return new FilterEntity(decode(s.slice(0, pos)), s.slice(pos + 1));
};
/******************************************************************************/
@ -419,6 +478,7 @@ FilterContainer.prototype.reset = function() {
this.hostnameDonthide = {};
this.entityHide = {};
this.entityDonthide = {};
// permanent
// [class], [id]
this.lowGenericFilters = {};
@ -436,11 +496,12 @@ FilterContainer.prototype.reset = function() {
this.highMediumGenericDonthideCount = 0;
// everything else
this.highHighGenericHide = [];
this.highHighGenericDonthide = [];
this.highHighGenericHide = '';
this.highHighGenericDonthide = '';
this.highHighGenericHideCount = 0;
this.highHighGenericDonthideCount = 0;
// hostname, entity-based filters
this.hostnameFilters = {};
this.entityFilters = {};
};
@ -705,6 +766,120 @@ FilterContainer.prototype.freeze = function() {
/******************************************************************************/
FilterContainer.prototype.toSelfie = function() {
var selfieFromDict = function(dict) {
var selfie = [];
var bucket, ff, n, i, f;
for ( var k in dict ) {
if ( dict.hasOwnProperty(k) === false ) {
continue;
}
// We need to encode the key because there could be a `\n`
// character in it, which would trip the code at parse time.
selfie.push('k\t' + encode(k));
bucket = dict[k];
selfie.push(bucket.fid + '\t' + bucket.toSelfie());
if ( bucket.fid !== '[]' ) {
continue;
}
ff = bucket.filters;
n = ff.length;
for ( i = 0; i < n; i++ ) {
f = ff[i];
selfie.push(f.fid + '\t' + f.toSelfie());
}
}
return selfie.join('\n');
};
return {
acceptedCount: this.acceptedCount,
duplicateCount: this.duplicateCount,
hostnameSpecificFilters: selfieFromDict(this.hostnameFilters),
entitySpecificFilters: selfieFromDict(this.entityFilters),
lowGenericFilters: selfieFromDict(this.lowGenericFilters),
highLowGenericHide: this.highLowGenericHide,
highLowGenericDonthide: this.highLowGenericDonthide,
highLowGenericHideCount: this.highLowGenericHideCount,
highLowGenericDonthideCount: this.highLowGenericDonthideCount,
highMediumGenericHide: this.highMediumGenericHide,
highMediumGenericDonthide: this.highMediumGenericDonthide,
highMediumGenericHideCount: this.highMediumGenericHideCount,
highMediumGenericDonthideCount: this.highMediumGenericDonthideCount,
highHighGenericHide: this.highHighGenericHide,
highHighGenericDonthide: this.highHighGenericDonthide,
highHighGenericHideCount: this.highHighGenericHideCount,
highHighGenericDonthideCount: this.highHighGenericDonthideCount
};
};
/******************************************************************************/
FilterContainer.prototype.fromSelfie = function(selfie) {
var factories = {
'[]': FilterBucket,
'#': FilterPlain,
'#+': FilterPlainMore,
'h': FilterHostname,
'e': FilterEntity
};
var dictFromSelfie = function(selfie) {
var dict = {};
var dictKey;
var bucket = null;
var rawText = selfie;
var rawEnd = rawText.length;
var lineBeg = 0, lineEnd;
var line, pos, what, factory;
while ( lineBeg < rawEnd ) {
lineEnd = rawText.indexOf('\n', lineBeg);
if ( lineEnd < 0 ) {
lineEnd = rawEnd;
}
line = rawText.slice(lineBeg, lineEnd);
lineBeg = lineEnd + 1;
pos = line.indexOf('\t');
what = line.slice(0, pos);
if ( what === 'k' ) {
dictKey = decode(line.slice(pos + 1));
bucket = null;
continue;
}
factory = factories[what];
if ( bucket === null ) {
bucket = dict[dictKey] = factory.fromSelfie(line.slice(pos + 1));
continue;
}
// When token key is reused, it can't be anything
// else than FilterBucket
bucket.add(factory.fromSelfie(line.slice(pos + 1)));
}
return dict;
};
this.frozen = true;
this.acceptedCount = selfie.acceptedCount;
this.duplicateCount = selfie.duplicateCount;
this.hostnameFilters = dictFromSelfie(selfie.hostnameSpecificFilters);
this.entityFilters = dictFromSelfie(selfie.entitySpecificFilters);
this.lowGenericFilters = dictFromSelfie(selfie.lowGenericFilters);
this.highLowGenericHide = selfie.highLowGenericHide;
this.highLowGenericDonthide = selfie.highLowGenericDonthide;
this.highLowGenericHideCount = selfie.highLowGenericHideCount;
this.highLowGenericDonthideCount = selfie.highLowGenericDonthideCount;
this.highMediumGenericHide = selfie.highMediumGenericHide;
this.highMediumGenericDonthide = selfie.highMediumGenericDonthide;
this.highMediumGenericHideCount = selfie.highMediumGenericHideCount;
this.highMediumGenericDonthideCount = selfie.highMediumGenericDonthideCount;
this.highHighGenericHide = selfie.highHighGenericHide;
this.highHighGenericDonthide = selfie.highHighGenericDonthide;
this.highHighGenericHideCount = selfie.highHighGenericHideCount;
this.highHighGenericDonthideCount = selfie.highHighGenericDonthideCount;
};
/******************************************************************************/
FilterContainer.prototype.addToSelectorCache = function(details) {
var hostname = details.hostname;
if ( typeof hostname !== 'string' || hostname === '' ) {

View File

@ -43,16 +43,23 @@ File system structure:
/******************************************************************************/
var oneSecond = 1000;
var oneMinute = 60 * oneSecond;
var oneHour = 60 * oneMinute;
var oneDay = 24 * oneHour;
/******************************************************************************/
var repositoryRoot = µBlock.projectServerRoot;
var nullFunc = function() {};
var reIsExternalPath = /^https?:\/\/[a-z0-9]/;
var reIsUserPath = /^assets\/user\//;
var lastRepoMetaTimestamp = 0;
var refreshRepoMetaPeriod = 6 * 60 * 60 * 1000;
var refreshRepoMetaPeriod = 5 * oneHour;
var exports = {
autoUpdate: true,
autoUpdateDelay: 2 * 24 * 60 * 60 * 1000
autoUpdateDelay: 4 * oneDay
};
/******************************************************************************/
@ -349,7 +356,7 @@ exports.setHomeURL = function(path, homeURL) {
entry = metadata.entries[path] = new AssetEntry();
}
entry.homeURL = homeURL;
}
};
getRepoMetadata(onRepoMetadataReady);
};
@ -548,9 +555,9 @@ var readRepoCopyAsset = function(path, callback) {
var onCacheMetaReady = function(entries) {
// Fetch from remote if:
// - Auto-update enabled AND (not in cache OR in cache but obsolete)
var timestamp = entries[path];
var homeURL = assetEntry.homeURL;
if ( exports.autoUpdate && typeof homeURL === 'string' && homeURL !== '' ) {
var timestamp = entries[path];
var obsolete = Date.now() - exports.autoUpdateDelay;
if ( typeof timestamp !== 'number' || timestamp <= obsolete ) {
//console.log('µBlock> readRepoCopyAsset("%s") / onCacheMetaReady(): not cached or obsolete', path);

View File

@ -63,6 +63,7 @@ return {
updateAssetsEvery: 75 * oneHour + 23 * oneMinute + 53 * oneSecond + 605,
projectServerRoot: 'https://raw.githubusercontent.com/gorhill/uBlock/master/',
userFiltersPath: 'assets/user/filters.txt',
pslPath: 'assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat',
// permanent lists
permanentLists: {
@ -86,11 +87,21 @@ return {
remoteBlacklists: {
},
firstUpdateAfter: 5 * oneMinute,
nextUpdateAfter: 7 * oneHour,
selfieMagic: 'ccolmudazpvm',
selfieAfter: 7 * oneMinute,
pageStores: {},
storageQuota: chrome.storage.local.QUOTA_BYTES,
storageUsed: 0,
noopFunc: function(){},
apiErrorCount: 0,
// so that I don't have to care for last comma
dummy: 0
};

View File

@ -19,6 +19,9 @@
Home: https://github.com/gorhill/uBlock
*/
/* jshint bitwise: false */
/* global µBlock */
/******************************************************************************/
µBlock.LiquidDict = (function() {
@ -94,12 +97,20 @@ LiquidDict.prototype.makeKey = function(word) {
if ( len > 255 ) {
len = 255;
}
var i = len >> 2;
var i8 = len >>> 3;
var i4 = len >>> 2;
var i2 = len >>> 1;
// Be sure the msb is not set, this will guarantee a valid unicode
// character (because 0xD800-0xDFFF).
return String.fromCharCode(
(word.charCodeAt( 0) & 0x03) << 14 |
(word.charCodeAt( i) & 0x03) << 12 |
(word.charCodeAt( i+i) & 0x03) << 10 |
(word.charCodeAt(i+i+i) & 0x03) << 8 |
(word.charCodeAt( i8) & 0x01) << 14 |
(word.charCodeAt( i4 ) & 0x01) << 13 |
(word.charCodeAt( i4+i8) & 0x01) << 12 |
(word.charCodeAt(i2 ) & 0x01) << 11 |
(word.charCodeAt(i2 +i8) & 0x01) << 10 |
(word.charCodeAt(i2+i4 ) & 0x01) << 9 |
(word.charCodeAt(i2+i4+i8) & 0x01) << 8 ,
len
);
};
@ -187,6 +198,26 @@ LiquidDict.prototype.reset = function() {
/******************************************************************************/
LiquidDict.prototype.toSelfie = function() {
return {
count: this.count,
bucketCount: this.bucketCount,
frozenBucketCount: this.frozenBucketCount,
dict: this.dict
};
};
/******************************************************************************/
LiquidDict.prototype.fromSelfie = function(selfie) {
this.count = selfie.count;
this.bucketCount = selfie.bucketCount;
this.frozenBucketCount = selfie.frozenBucketCount;
this.dict = selfie.dict;
};
/******************************************************************************/
return LiquidDict;
/******************************************************************************/

View File

@ -121,7 +121,7 @@ var onMessage = function(request, sender, callback) {
switch ( request.what ) {
case 'retrieveDomainCosmeticSelectors':
if ( pageStore && pageStore.getNetFilteringSwitch() ) {
response = µb.abpHideFilters.retrieveDomainSelectors(request);
response = µb.cosmeticFilteringEngine.retrieveDomainSelectors(request);
}
break;
@ -162,12 +162,12 @@ var onMessage = function(request, sender, callback) {
switch ( request.what ) {
case 'retrieveGenericCosmeticSelectors':
if ( pageStore && pageStore.getNetFilteringSwitch() ) {
response = µb.abpHideFilters.retrieveGenericSelectors(request);
response = µb.cosmeticFilteringEngine.retrieveGenericSelectors(request);
}
break;
case 'injectedSelectors':
µb.abpHideFilters.addToSelectorCache(request);
µb.cosmeticFilteringEngine.addToSelectorCache(request);
break;
case 'blockedRequests':
@ -250,8 +250,8 @@ var getLists = function(callback) {
available: null,
current: µb.remoteBlacklists,
cosmetic: µb.userSettings.parseAllABPHideFilters,
netFilterCount: µb.abpFilters.getFilterCount(),
cosmeticFilterCount: µb.abpHideFilters.getFilterCount(),
netFilterCount: µb.netFilteringEngine.getFilterCount(),
cosmeticFilterCount: µb.cosmeticFilteringEngine.getFilterCount(),
autoUpdate: µb.userSettings.autoUpdate,
userFiltersPath: µb.userFiltersPath,
cache: null

View File

@ -40,12 +40,15 @@ var quickProfiler = (function() {
prompt = s || '';
tstart = timer.now();
};
var stop = function() {
var stop = function(period) {
if ( period === undefined ) {
period = 10000;
}
var now = timer.now();
count += 1;
time += (now - tstart);
if ( (now - lastlog) > 10000 ) {
console.log('µBlock > %s: %s ms (%d samples)', prompt, avg().toFixed(3), count);
if ( (now - lastlog) >= period ) {
console.log('µBlock> %s: %s ms (%d samples)', prompt, avg().toFixed(3), count);
lastlog = now;
}
};

View File

@ -31,36 +31,76 @@
/******************************************************************************/
var µb = µBlock;
var bufferTime = 0 * 60 * 1000;
var exports = {};
var jobCallback = function() {
// Simpler to fire restart here, and safe given how far this will happen
// in the future.
restart();
// If auto-update is disabled, check again in a while.
if ( µb.userSettings.autoUpdate !== true ) {
return;
}
// TODO: need smarter update, currently blindly reloading all.
µb.loadUpdatableAssets(true);
var onMetadataReady = function(metadata) {
// Check PSL
var mdEntry = metadata[µb.pslPath];
if ( mdEntry.repoObsolete ) {
// console.log('µBlock.updater> updating all updatable assets');
µb.loadUpdatableAssets({ update: true });
return;
}
// Check used filter lists
var lists = µb.remoteBlacklists;
for ( var path in lists ) {
if ( lists.hasOwnProperty(path) === false ) {
continue;
}
if ( lists[path].off ) {
continue;
}
if ( metadata.hasOwnProperty(path) === false ) {
continue;
}
mdEntry = metadata[path];
if ( mdEntry.cacheObsolete || mdEntry.repoObsolete ) {
// console.log('µBlock.updater> updating only filter lists');
µb.loadUpdatableAssets({ update: true, psl: false });
return;
}
}
// console.log('µBlock.updater> all is up to date');
};
µb.assets.metadata(onMetadataReady);
};
// https://www.youtube.com/watch?v=cIrGQD84F1g
/******************************************************************************/
exports.restart = function() {
var restart = function(after) {
if ( after === undefined ) {
after = µb.nextUpdateAfter;
}
µb.asyncJobs.add(
'autoUpdateAssets',
null,
jobCallback,
µb.updateAssetsEvery - bufferTime,
true
after,
false
);
};
exports.restart();
/******************************************************************************/
return exports;
return {
restart: restart
};
/******************************************************************************/
})();

View File

@ -139,12 +139,18 @@
/******************************************************************************/
µBlock.appendUserFilters = function(content) {
var µb = this;
var onFiltersReady = function() {
};
var onSaved = function(details) {
if ( details.error ) {
return;
}
µBlock.loadUbiquitousBlacklists();
µb.loadFilterLists(onFiltersReady);
};
var onLoaded = function(details) {
if ( details.error ) {
return;
@ -152,8 +158,9 @@
if ( details.content.indexOf(content.trim()) !== -1 ) {
return;
}
µBlock.saveUserFilters(details.content + '\n' + content, onSaved);
µb.saveUserFilters(details.content + '\n' + content, onSaved);
};
if ( content.length > 0 ) {
this.loadUserFilters(onLoaded);
}
@ -256,15 +263,21 @@
/******************************************************************************/
µBlock.loadUbiquitousBlacklists = function() {
µBlock.loadFilterLists = function(callback) {
var µb = this;
var blacklistLoadCount;
if ( typeof callback !== 'function' ) {
callback = this.noopFunc;
}
var loadBlacklistsEnd = function() {
µb.abpFilters.freeze();
µb.abpHideFilters.freeze();
µb.messaging.announce({ what: 'loadUbiquitousBlacklistCompleted' });
µb.netFilteringEngine.freeze();
µb.cosmeticFilteringEngine.freeze();
chrome.storage.local.set({ 'remoteBlacklists': µb.remoteBlacklists });
µb.messaging.announce({ what: 'loadUbiquitousBlacklistCompleted' });
µb.toSelfieAsync();
callback();
};
var mergeBlacklist = function(details) {
@ -277,10 +290,9 @@
var loadBlacklistsStart = function(lists) {
µb.remoteBlacklists = lists;
// rhill 2013-12-10: set all existing entries to `false`.
µb.abpFilters.reset();
µb.abpHideFilters.reset();
µb.netFilteringEngine.reset();
µb.cosmeticFilteringEngine.reset();
µb.destroySelfie();
var locations = Object.keys(lists);
blacklistLoadCount = locations.length;
if ( blacklistLoadCount === 0 ) {
@ -322,11 +334,11 @@
// Useful references:
// https://adblockplus.org/en/filter-cheatsheet
// https://adblockplus.org/en/filters
var abpFilters = this.abpFilters;
var abpHideFilters = this.abpHideFilters;
var netFilteringEngine = this.netFilteringEngine;
var cosmeticFilteringEngine = this.cosmeticFilteringEngine;
var parseCosmeticFilters = this.userSettings.parseAllABPHideFilters;
var duplicateCount = abpFilters.duplicateCount + abpHideFilters.duplicateCount;
var acceptedCount = abpFilters.acceptedCount + abpHideFilters.acceptedCount;
var duplicateCount = netFilteringEngine.duplicateCount + cosmeticFilteringEngine.duplicateCount;
var acceptedCount = netFilteringEngine.acceptedCount + cosmeticFilteringEngine.acceptedCount;
var reLocalhost = /(^|\s)(localhost\.localdomain|localhost|local|broadcasthost|0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)(?=\s|$)/g;
var reAdblockFilter = /^[^a-z0-9:]|[^a-z0-9]$|[^a-z0-9_:.-]/;
var reAdblockHostFilter = /^\|\|([a-z0-9.-]+[a-z0-9])\^?$/;
@ -360,7 +372,7 @@
// 2014-05-18: ABP element hide filters are allowed to contain space
// characters
if ( parseCosmeticFilters ) {
if ( abpHideFilters.add(line) ) {
if ( cosmeticFilteringEngine.add(line) ) {
continue;
}
}
@ -396,7 +408,7 @@
// Likely an ABP net filter?
if ( reAdblockFilter.test(line) ) {
if ( abpFilters.add(line) ) {
if ( netFilteringEngine.add(line) ) {
continue;
}
// rhill 2014-01-22: Transpose possible Adblock Plus-filter syntax
@ -412,13 +424,13 @@
continue;
}
abpFilters.addAnyPartyHostname(line);
netFilteringEngine.addAnyPartyHostname(line);
}
// For convenience, store the number of entries for this
// blacklist, user might be happy to know this information.
duplicateCount = abpFilters.duplicateCount + abpHideFilters.duplicateCount - duplicateCount;
acceptedCount = abpFilters.acceptedCount + abpHideFilters.acceptedCount - acceptedCount;
duplicateCount = netFilteringEngine.duplicateCount + cosmeticFilteringEngine.duplicateCount - duplicateCount;
acceptedCount = netFilteringEngine.acceptedCount + cosmeticFilteringEngine.acceptedCount - acceptedCount;
this.remoteBlacklists[details.path].entryCount = acceptedCount + duplicateCount;
this.remoteBlacklists[details.path].entryUsedCount = acceptedCount;
@ -449,66 +461,192 @@
}
// Now force reload
this.loadUpdatableAssets(update);
this.loadUpdatableAssets({ update: update, psl: update });
};
/******************************************************************************/
µBlock.loadPublicSuffixList = function(callback) {
if ( typeof callback !== 'function' ) {
callback = this.noopFunc;
}
var applyPublicSuffixList = function(details) {
// TODO: Not getting proper suffix list is a bit serious, I think
// the extension should be force-restarted if it occurs..
if ( !details.error ) {
publicSuffixList.parse(details.content, punycode.toASCII);
}
if ( typeof callback === 'function' ) {
callback();
}
callback();
};
this.assets.get(
'assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat',
applyPublicSuffixList
);
this.assets.get(this.pslPath, applyPublicSuffixList);
};
/******************************************************************************/
// Load updatable assets
µBlock.loadUpdatableAssets = function(update) {
µBlock.loadUpdatableAssets = function(details) {
var µb = this;
details = details || {};
var update = details.update !== false;
this.assets.autoUpdate = update || this.userSettings.autoUpdate;
this.assets.autoUpdateDelay = this.updateAssetsEvery;
this.loadPublicSuffixList();
this.loadUbiquitousBlacklists();
// It could be a manual update, so we reset the auto-updater
if ( update ) {
this.updater.restart();
var onFiltersReady = function() {
if ( update ) {
µb.updater.restart();
}
};
var onPSLReady = function() {
µb.loadFilterLists(onFiltersReady);
};
if ( details.psl !== false ) {
this.loadPublicSuffixList(onPSLReady);
} else {
this.loadFilterLists(onFiltersReady);
}
};
/******************************************************************************/
µBlock.toSelfie = function() {
var selfie = {
magic: this.selfieMagic,
publicSuffixList: publicSuffixList.toSelfie(),
filterLists: this.remoteBlacklists,
netFilteringEngine: this.netFilteringEngine.toSelfie(),
cosmeticFilteringEngine: this.cosmeticFilteringEngine.toSelfie(),
};
chrome.storage.local.set({ selfie: selfie });
// console.log('µBlock.toSelfie> made a selfie!');
};
// This is to be sure the selfie is generated in a sane manner: the selfie will
// be generated if the user doesn't change his filter lists selection for
// some set time.
µBlock.toSelfieAsync = function(after) {
if ( typeof after !== 'number' ) {
after = this.selfieAfter;
}
this.asyncJobs.add(
'toSelfie',
null,
this.toSelfie.bind(this),
after,
false
);
};
/******************************************************************************/
µBlock.fromSelfie = function(callback) {
var µb = this;
if ( typeof callback !== 'function' ) {
callback = this.noopFunc;
}
var onSelfieReady = function(store) {
var selfie = store.selfie;
if ( typeof selfie !== 'object' || selfie.magic !== µb.selfieMagic ) {
callback(false);
return;
}
if ( publicSuffixList.fromSelfie(selfie.publicSuffixList) !== true ) {
callback(false);
return;
}
// console.log('µBlock.fromSelfie> selfie looks good');
µb.remoteBlacklists = selfie.filterLists;
µb.netFilteringEngine.fromSelfie(selfie.netFilteringEngine);
µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmeticFilteringEngine);
callback(true);
};
chrome.storage.local.get('selfie', onSelfieReady);
};
/******************************************************************************/
µBlock.destroySelfie = function() {
chrome.storage.local.remove('selfie');
};
/******************************************************************************/
// Load all
µBlock.load = function() {
var µb = this;
// User whitelist directives and filters need the Public Suffix List to be
// available -- because the way they are stored internally.
// Final initialization steps after all needed assets are in memory
var onAllDone = function(wasAutoUpdated) {
// Initialize internal state with maybe already existing tabs
var bindToTabs = function(tabs) {
var scriptStart = function(tabId) {
var scriptEnd = function() {
chrome.tabs.executeScript(tabId, {
file: 'js/contentscript-end.js',
allFrames: true,
runAt: 'document_idle'
});
};
chrome.tabs.executeScript(tabId, {
file: 'js/contentscript-start.js',
allFrames: true,
runAt: 'document_idle'
}, scriptEnd);
};
var i = tabs.length, tab;
while ( i-- ) {
tab = tabs[i];
µb.bindTabToPageStats(tab.id, tab.url);
// https://github.com/gorhill/uBlock/issues/129
scriptStart(tab.id);
}
};
chrome.tabs.query({ url: 'http://*/*' }, bindToTabs);
chrome.tabs.query({ url: 'https://*/*' }, bindToTabs);
// https://github.com/gorhill/uBlock/issues/184
// If we restored a selfie, check for updates not too far
// in the future.
µb.updater.restart(wasAutoUpdated ? µb.nextUpdateAfter : µb.firstUpdateAfter);
};
// Filters are in memory
var onFiltersReady = function() {
onAllDone(µb.userSettings.autoUpdate);
};
// Load order because dependencies:
// User settings -> PSL -> [filter lists, user whitelist]
var onPSLReady = function() {
µb.loadWhitelist();
µb.loadUbiquitousBlacklists();
µb.loadFilterLists(onFiltersReady);
};
// Public Suffix List loader needs the user settings need to be available
// because we need to know whether to auto-update the list or not.
var onUserSettingsReady = function() {
µb.assets.autoUpdate = µb.userSettings.autoUpdate || true;
// If no selfie available, take the long way, i.e. load and parse
// raw data.
var onSelfieReady = function(success) {
if ( success === true ) {
onAllDone(false);
return;
}
µb.assets.autoUpdate = µb.userSettings.autoUpdate;
µb.loadPublicSuffixList(onPSLReady);
};
this.loadUserSettings(onUserSettingsReady);
// User settings are in memory
var onUserSettingsReady = function() {
µb.fromSelfie(onSelfieReady);
};
this.loadUserSettings(onUserSettingsReady);
this.loadLocalSettings();
this.getBytesInUse();
};

View File

@ -59,41 +59,6 @@
chrome.tabs.onRemoved.addListener(onTabRemoved);
})();
/******************************************************************************/
// Initialize internal state with maybe already existing tabs
// This needs to be executed once, hence it has its own scope, which will
// allow the code to be flushed once completed.
(function(){
var µb = µBlock;
var bindToTabs = function(tabs) {
var scriptStart = function(tabId) {
var scriptEnd = function() {
chrome.tabs.executeScript(tabId, {
file: 'js/contentscript-end.js',
allFrames: true,
runAt: 'document_idle'
});
};
chrome.tabs.executeScript(tabId, {
file: 'js/contentscript-start.js',
allFrames: true,
runAt: 'document_idle'
}, scriptEnd);
};
var i = tabs.length, tab;
while ( i-- ) {
tab = tabs[i];
µb.bindTabToPageStats(tab.id, tab.url);
// https://github.com/gorhill/uBlock/issues/129
scriptStart(tab.id);
}
};
chrome.tabs.query({ url: 'http://*/*' }, bindToTabs);
chrome.tabs.query({ url: 'https://*/*' }, bindToTabs);
})();
/******************************************************************************/
/******************************************************************************/

View File

@ -80,7 +80,7 @@ var onBeforeRequest = function(details) {
var reason = false;
if ( pageStore.getNetFilteringSwitch() ) {
reason = µb.abpFilters.matchString(requestContext, requestURL, requestType, requestHostname);
reason = µb.netFilteringEngine.matchString(requestContext, requestURL, requestType, requestHostname);
}
// Record what happened.
pageStore.recordRequest(requestType, requestURL, reason);
@ -168,7 +168,7 @@ var onBeforeSendHeaders = function(details) {
// in multiple tabs.
var reason = false;
if ( pageStore.getNetFilteringSwitch() ) {
reason = µb.abpFilters.matchStringExactType(
reason = µb.netFilteringEngine.matchStringExactType(
pageDetails,
requestURL,
'popup',

View File

@ -1,17 +1,17 @@
/*! Home: https://github.com/gorhill/publicsuffixlist.js */
;(function(f){var b={};var h={};var a=480;
var g=/[^a-z0-9.-]/;function i(k){if(!k||k.charAt(0)==="."){return""}k=k.toLowerCase();var l=c(k);if(l===k){return""
}var m=k.lastIndexOf(".",k.lastIndexOf(".",k.length-l.length)-1);if(m<=0){return k}return k.slice(m+1)
}function c(k){if(!k){return""}var l;while(true){l=k.indexOf(".");if(l<0){return k}if(j(b,k)){return k.slice(l+1)
}if(j(h,k)){return k}if(j(h,"*"+k.slice(l))){return k}k=k.slice(l+1)}}function j(t,r){var q=r.lastIndexOf(".");
var m,v;if(q<0){m=r;v=r}else{m=r.slice(q+1);v=r.slice(0,q)}var s=t[m];if(!s){return false}if(typeof s==="string"){return s.indexOf(" "+v+" ")>=0
}var n=v.length;var w=s[n];if(!w){return false}var k=0;var u=Math.floor(w.length/n+0.5);var p,o;while(k<u){p=k+u>>1;
o=w.substr(n*p,n);if(v<o){u=p}else{if(v>o){k=p+1}else{return true}}}return false}function d(r,p){b={};
h={};r=r.toLowerCase();var o=0,l;var m=r.length;var s,q,n,k;while(o<m){l=r.indexOf("\n",o);if(l<0){l=m
}s=r.slice(o,l);o=l+1;if(s.length===0){continue}n=s.indexOf("//");if(n>=0){s=s.slice(0,n)}s=s.trim();
if(!s){continue}if(g.test(s)){s=p(s)}if(s.charAt(0)==="!"){q=b;s=s.slice(1)}else{q=h}n=s.lastIndexOf(".");
if(n<0){k=s}else{k=s.slice(n+1);s=s.slice(0,n)}if(!q[k]){q[k]=[]}if(s){q[k].push(s)}}e(b);e(h)}function e(m){var o,q,p,k;
for(var n in m){if(!m.hasOwnProperty(n)){continue}o=m[n].join(" ");if(!o){m[n]="";continue}if(o.length<a){m[n]=" "+o+" ";
continue}p=m[n].length;o=[];while(p--){q=m[n][p];k=q.length;if(!o[k]){o[k]=[]}o[k].push(q)}k=o.length;
while(k--){if(o[k]){o[k]=o[k].sort().join("")}}m[n]=o}return m}f.publicSuffixList={version:"1.0",parse:d,getDomain:i,getPublicSuffix:c}
})(this);
/* Minified using http://refresh-sf.com/yui/ */
;(function(i){var d={};var k={};var g="iscjsfsaolnm";var b=480;
var j=/[^a-z0-9.-]/;function l(n){if(!n||n.charAt(0)==="."){return""}n=n.toLowerCase();var o=e(n);if(o===n){return""}var p=n.lastIndexOf(".",n.lastIndexOf(".",n.length-o.length)-1);
if(p<=0){return n}return n.slice(p+1)}function e(n){if(!n){return""}var o;while(true){o=n.indexOf(".");if(o<0){return n}if(m(d,n)){return n.slice(o+1)
}if(m(k,n)){return n}if(m(k,"*"+n.slice(o))){return n}n=n.slice(o+1)}}function m(v,t){var s=t.lastIndexOf(".");var o,x;if(s<0){o=t;
x=t}else{o=t.slice(s+1);x=t.slice(0,s)}var u=v[o];if(!u){return false}if(typeof u==="string"){return u.indexOf(" "+x+" ")>=0
}var p=x.length;var y=u[p];if(!y){return false}var n=0;var w=Math.floor(y.length/p+0.5);var r,q;while(n<w){r=n+w>>1;q=y.substr(p*r,p);
if(x<q){w=r}else{if(x>q){n=r+1}else{return true}}}return false}function f(u,s){d={};k={};u=u.toLowerCase();var r=0,o;var p=u.length;
var v,t,q,n;while(r<p){o=u.indexOf("\n",r);if(o<0){o=u.indexOf("\r",r);if(o<0){o=p}}v=u.slice(r,o).trim();r=o+1;if(v.length===0){continue
}q=v.indexOf("//");if(q>=0){v=v.slice(0,q)}v=v.trim();if(!v){continue}if(j.test(v)){v=s(v)}if(v.charAt(0)==="!"){t=d;v=v.slice(1)
}else{t=k}q=v.lastIndexOf(".");if(q<0){n=v}else{n=v.slice(q+1);v=v.slice(0,q)}if(!t.hasOwnProperty(n)){t[n]=[]}if(v){t[n].push(v)
}}h(d);h(k)}function h(o){var q,s,r,n;for(var p in o){if(!o.hasOwnProperty(p)){continue}q=o[p].join(" ");if(!q){o[p]="";continue
}if(q.length<b){o[p]=" "+q+" ";continue}r=o[p].length;q=[];while(r--){s=o[p][r];n=s.length;if(!q[n]){q[n]=[]}q[n].push(s)
}n=q.length;while(n--){if(q[n]){q[n]=q[n].sort().join("")}}o[p]=q}return o}function c(){return{magic:g,rules:k,exceptions:d}
}function a(n){if(typeof n!=="object"||typeof n.magic!=="string"||n.magic!==g){return false}k=n.rules;d=n.exceptions;return true
}i=i||window;i.publicSuffixList={version:"1.0",parse:f,getDomain:l,getPublicSuffix:e,toSelfie:c,fromSelfie:a}})(this);