aboutsummaryrefslogtreecommitdiffstats
path: root/js
diff options
context:
space:
mode:
authorgorhill <rhill@raymondhill.net>2014-08-28 09:59:05 -0400
committergorhill <rhill@raymondhill.net>2014-08-28 09:59:05 -0400
commitfe2d7619082d9229e4deeddfa3cc7c7022a9cd13 (patch)
tree930fb27ab21353da21731455ca4275409503deed /js
parent527817d2f287d19c2d6a1119811d9e3d927cf14a (diff)
downloaduBlock-fe2d7619082d9229e4deeddfa3cc7c7022a9cd13.zip
uBlock-fe2d7619082d9229e4deeddfa3cc7c7022a9cd13.tar.gz
uBlock-fe2d7619082d9229e4deeddfa3cc7c7022a9cd13.tar.bz2
this fixes #191 completely
Diffstat (limited to 'js')
-rw-r--r--js/abp-filters.js255
1 files changed, 178 insertions, 77 deletions
diff --git a/js/abp-filters.js b/js/abp-filters.js
index f4e9b52..56c4192 100644
--- a/js/abp-filters.js
+++ b/js/abp-filters.js
@@ -41,6 +41,7 @@
const BlockAction = 0 << 15;
const AllowAction = 1 << 15;
+const ToggleAction = BlockAction ^ AllowAction;
const AnyType = 1 << 11;
@@ -209,6 +210,12 @@ FilterPlain.prototype.match = function(url, tokenBeg) {
return url.substr(tokenBeg - this.tokenBeg, this.s.length) === this.s;
};
+FilterPlain.prototype.toString = function() {
+ return this.s;
+};
+
+/******************************************************************************/
+
var FilterPlainHostname = function(s, tokenBeg, hostname) {
this.s = s;
this.tokenBeg = tokenBeg;
@@ -221,6 +228,10 @@ FilterPlainHostname.prototype.match = function(url, tokenBeg) {
url.substr(tokenBeg - this.tokenBeg, this.s.length) === this.s;
};
+FilterPlainHostname.prototype.toString = function() {
+ return this.s + '$domain=' + this.hostname;
+};
+
/******************************************************************************/
var FilterPlainPrefix0 = function(s) {
@@ -232,6 +243,12 @@ FilterPlainPrefix0.prototype.match = function(url, tokenBeg) {
return url.substr(tokenBeg, this.s.length) === this.s;
};
+FilterPlainPrefix0.prototype.toString = function() {
+ return this.s;
+};
+
+/******************************************************************************/
+
var FilterPlainPrefix0Hostname = function(s, hostname) {
this.s = s;
this.hostname = hostname;
@@ -243,6 +260,10 @@ FilterPlainPrefix0Hostname.prototype.match = function(url, tokenBeg) {
url.substr(tokenBeg, this.s.length) === this.s;
};
+FilterPlainPrefix0Hostname.prototype.toString = function() {
+ return this.s + '$domain=' + this.hostname;
+};
+
/******************************************************************************/
var FilterPlainPrefix1 = function(s) {
@@ -254,6 +275,12 @@ FilterPlainPrefix1.prototype.match = function(url, tokenBeg) {
return url.substr(tokenBeg - 1, this.s.length) === this.s;
};
+FilterPlainPrefix1.prototype.toString = function() {
+ return this.s;
+};
+
+/******************************************************************************/
+
var FilterPlainPrefix1Hostname = function(s, hostname) {
this.s = s;
this.hostname = hostname;
@@ -265,6 +292,10 @@ FilterPlainPrefix1Hostname.prototype.match = function(url, tokenBeg) {
url.substr(tokenBeg - 1, this.s.length) === this.s;
};
+FilterPlainPrefix1Hostname.prototype.toString = function() {
+ return this.s + '$domain=' + this.hostname;
+};
+
/******************************************************************************/
var FilterPlainLeftAnchored = function(s) {
@@ -276,6 +307,12 @@ FilterPlainLeftAnchored.prototype.match = function(url) {
return url.slice(0, this.s.length) === this.s;
};
+FilterPlainLeftAnchored.prototype.toString = function() {
+ return '|' + this.s;
+};
+
+/******************************************************************************/
+
var FilterPlainLeftAnchoredHostname = function(s, hostname) {
this.s = s;
this.hostname = hostname;
@@ -287,6 +324,10 @@ FilterPlainLeftAnchoredHostname.prototype.match = function(url) {
url.slice(0, this.s.length) === this.s;
};
+FilterPlainLeftAnchoredHostname.prototype.toString = function() {
+ return '|' + this.s + '$domain=' + this.hostname;
+};
+
/******************************************************************************/
var FilterPlainRightAnchored = function(s) {
@@ -298,6 +339,12 @@ FilterPlainRightAnchored.prototype.match = function(url) {
return url.slice(-this.s.length) === this.s;
};
+FilterPlainRightAnchored.prototype.toString = function() {
+ return this.s + '|';
+};
+
+/******************************************************************************/
+
var FilterPlainRightAnchoredHostname = function(s, hostname) {
this.s = s;
this.hostname = hostname;
@@ -309,6 +356,10 @@ FilterPlainRightAnchoredHostname.prototype.match = function(url) {
url.slice(-this.s.length) === this.s;
};
+FilterPlainRightAnchoredHostname.prototype.toString = function() {
+ return this.s + '|$domain=' + this.hostname;
+};
+
/******************************************************************************/
// With a single wildcard, regex is not optimal.
@@ -317,7 +368,6 @@ FilterPlainRightAnchoredHostname.prototype.match = function(url) {
// http://jsperf.com/regexp-vs-indexof-abp-hit/3
var FilterSingleWildcard = function(s, tokenBeg) {
- this.s = s;
this.tokenBeg = tokenBeg;
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
@@ -331,8 +381,13 @@ FilterSingleWildcard.prototype.match = function(url, tokenBeg) {
url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
};
+FilterSingleWildcard.prototype.toString = function() {
+ return this.lSegment + '*' + this.rSegment;
+};
+
+/******************************************************************************/
+
var FilterSingleWildcardHostname = function(s, tokenBeg, hostname) {
- this.s = s;
this.tokenBeg = tokenBeg;
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
@@ -348,10 +403,13 @@ FilterSingleWildcardHostname.prototype.match = function(url, tokenBeg) {
url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
};
+FilterSingleWildcardHostname.prototype.toString = function() {
+ return this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname;
+};
+
/******************************************************************************/
var FilterSingleWildcardPrefix0 = function(s) {
- this.s = s;
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
@@ -363,8 +421,13 @@ FilterSingleWildcardPrefix0.prototype.match = function(url, tokenBeg) {
url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
};
+FilterSingleWildcardPrefix0.prototype.toString = function() {
+ return this.lSegment + '*' + this.rSegment;
+};
+
+/******************************************************************************/
+
var FilterSingleWildcardPrefix0Hostname = function(s, hostname) {
- this.s = s;
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
@@ -378,6 +441,10 @@ FilterSingleWildcardPrefix0Hostname.prototype.match = function(url, tokenBeg) {
url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
};
+FilterSingleWildcardPrefix0Hostname.prototype.toString = function() {
+ return this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname;
+};
+
/******************************************************************************/
// With a single wildcard, regex is not optimal.
@@ -386,7 +453,6 @@ FilterSingleWildcardPrefix0Hostname.prototype.match = function(url, tokenBeg) {
// http://jsperf.com/regexp-vs-indexof-abp-hit/3
var FilterSingleWildcardLeftAnchored = function(s) {
- this.s = s;
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
@@ -398,8 +464,13 @@ FilterSingleWildcardLeftAnchored.prototype.match = function(url) {
url.indexOf(this.rSegment, this.lSegment.length) > 0;
};
+FilterSingleWildcardLeftAnchored.prototype.toString = function() {
+ return '|' + this.lSegment + '*' + this.rSegment;
+};
+
+/******************************************************************************/
+
var FilterSingleWildcardLeftAnchoredHostname = function(s, hostname) {
- this.s = s;
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
@@ -413,6 +484,10 @@ FilterSingleWildcardLeftAnchoredHostname.prototype.match = function(url) {
url.indexOf(this.rSegment, this.lSegment.length) > 0;
};
+FilterSingleWildcardLeftAnchoredHostname.prototype.toString = function() {
+ return '|' + this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname;
+};
+
/******************************************************************************/
// With a single wildcard, regex is not optimal.
@@ -421,7 +496,6 @@ FilterSingleWildcardLeftAnchoredHostname.prototype.match = function(url) {
// http://jsperf.com/regexp-vs-indexof-abp-hit/3
var FilterSingleWildcardRightAnchored = function(s) {
- this.s = s;
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
@@ -433,8 +507,13 @@ FilterSingleWildcardRightAnchored.prototype.match = function(url) {
url.lastIndexOf(this.lSegment, url.length - this.rSegment.length - this.lSegment.length) >= 0;
};
+FilterSingleWildcardRightAnchored.prototype.toString = function() {
+ return this.lSegment + '*' + this.rSegment + '|';
+};
+
+/******************************************************************************/
+
var FilterSingleWildcardRightAnchoredHostname = function(s, hostname) {
- this.s = s;
var wcOffset = s.indexOf('*');
this.lSegment = s.slice(0, wcOffset);
this.rSegment = s.slice(wcOffset + 1);
@@ -448,6 +527,10 @@ FilterSingleWildcardRightAnchoredHostname.prototype.match = function(url) {
url.lastIndexOf(this.lSegment, url.length - this.rSegment.length - this.lSegment.length) >= 0;
};
+FilterSingleWildcardRightAnchoredHostname.prototype.toString = function() {
+ return this.lSegment + '*' + this.rSegment + '|$domain=' + this.hostname;
+};
+
/******************************************************************************/
// With many wildcards, a regex is best.
@@ -467,6 +550,12 @@ FilterManyWildcards.prototype.match = function(url, tokenBeg) {
return this.re.test(url.slice(tokenBeg - this.tokenBeg));
};
+FilterManyWildcards.prototype.toString = function() {
+ return this.s;
+};
+
+/******************************************************************************/
+
var FilterManyWildcardsHostname = function(s, tokenBeg, hostname) {
this.s = s;
this.tokenBeg = tokenBeg;
@@ -480,6 +569,10 @@ FilterManyWildcardsHostname.prototype.match = function(url, tokenBeg) {
this.re.test(url.slice(tokenBeg - this.tokenBeg));
};
+FilterManyWildcardsHostname.prototype.toString = function() {
+ return this.s + '$domain=' + this.hostname;
+};
+
/******************************************************************************/
var makeFilter = function(details, tokenBeg) {
@@ -613,20 +706,10 @@ var trimChar = function(s, c) {
/******************************************************************************/
var FilterParser = function() {
- this.action = BlockAction;
- this.anchor = 0;
this.domains = [];
- this.elemHiding = false;
- this.f = '';
- this.firstParty = false;
- this.fopts = '';
- this.hostname = false;
this.hostnames = [];
- this.notDomains = [];
- this.notHostnames = [];
- this.thirdParty = false;
this.types = [];
- this.unsupported = false;
+ this.reset();
};
/******************************************************************************/
@@ -648,17 +731,16 @@ FilterParser.prototype.toNormalizedType = {
FilterParser.prototype.reset = function() {
this.action = BlockAction;
this.anchor = 0;
- this.domains = [];
+ this.domains.length = 0;
this.elemHiding = false;
this.f = '';
this.firstParty = false;
this.fopts = '';
this.hostname = false;
- this.hostnames = [];
- this.notDomains = [];
- this.notHostnames = [];
+ this.hostnames.length = 0;
+ this.notHostname = false;
this.thirdParty = false;
- this.types = [];
+ this.types.length = 0;
this.unsupported = false;
return this;
};
@@ -710,13 +792,17 @@ FilterParser.prototype.parseOptHostnames = function(raw) {
if ( domain === '' ) {
domain = noDomainName;
}
- if ( not ) {
- this.notHostnames.push(hostname);
- this.notDomains.push(domain);
- } else {
- this.hostnames.push(hostname);
- this.domains.push(domain);
+ // https://github.com/gorhill/uBlock/issues/191
+ // Well it doesn't seem to make a whole lot of sense to have both
+ // non-negated hostnames mixed with negated hostnames.
+ if ( this.hostnames.length !== 0 && not !== this.notHostname ) {
+ console.error('FilterContainer.parseOptHostnames(): ambiguous filter syntax: "%s"', this.f);
+ this.unsupported = true;
+ return;
}
+ this.notHostname = not;
+ this.hostnames.push(hostname);
+ this.domains.push(domain);
}
};
@@ -811,29 +897,32 @@ FilterParser.prototype.parse = function(s) {
var FilterBucket = function(a, b) {
this.filters = [a, b];
- this.s = '';
+ 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.s = filters[i].s;
+ this.f = filters[i];
return true;
}
}
return false;
};
+FilterBucket.prototype.toString = function() {
+ if ( this.f !== null ) {
+ return this.f.toString();
+ }
+ return '';
+};
+
/******************************************************************************/
/******************************************************************************/
@@ -996,19 +1085,12 @@ FilterContainer.prototype.addFilter = function(parsed) {
}
var tokenBeg = matches.index;
var tokenEnd = parsed.hostname ? reHostnameToken.lastIndex : reGoodToken.lastIndex;
- var i, n, filter;
+ var filter;
- // https://github.com/gorhill/uBlock/issues/191
- // Well it doesn't seem to make a whole lot of sense to have both
- // non-negated hostnames mixed with negated hostnames.
- if ( parsed.hostnames.length && parsed.notHostnames.length ) {
- console.error('FilterContainer.addFilter(): ambiguous filter syntax', parsed.f);
- return false;
- }
+ var i = parsed.hostnames.length;
- if ( parsed.hostnames.length ) {
- n = parsed.hostnames.length;
- for ( i = 0; i < n; i++ ) {
+ if ( i !== 0 && !parsed.notHostname ) {
+ while ( i-- ) {
filter = makeHostnameFilter(parsed, tokenBeg, parsed.hostnames[i]);
if ( !filter ) {
return false;
@@ -1026,24 +1108,29 @@ FilterContainer.prototype.addFilter = function(parsed) {
// https://github.com/gorhill/uBlock/issues/191
// Invert the purpose of the filter for negated hostnames
- if ( parsed.notHostnames.length ) {
+ if ( i !== 0 && parsed.notHostname ) {
filter = makeFilter(parsed, tokenBeg);
if ( !filter ) {
return false;
}
this.addFilterEntry(filter, parsed, AnyParty, tokenBeg, tokenEnd);
// Reverse purpose of filter
- parsed.action ^= (BlockAction ^ AllowAction);
- n = parsed.notHostnames.length;
- for ( i = 0; i < n; i++ ) {
- filter = makeHostnameFilter(parsed, tokenBeg, parsed.notHostnames[i]);
+ parsed.action ^= ToggleAction;
+ while ( i-- ) {
+ filter = makeHostnameFilter(parsed, tokenBeg, parsed.hostnames[i]);
if ( !filter ) {
return false;
}
+ // https://github.com/gorhill/uBlock/issues/191#issuecomment-53654024
+ // If it is a block filter, we need to reverse the order of
+ // evaluation.
+ if ( parsed.action === BlockAction ) {
+ filter.important = true;
+ }
this.addFilterEntry(
filter,
parsed,
- SpecificParty | this.toDomainBits(parsed.notDomains[i]),
+ SpecificParty | this.toDomainBits(parsed.domains[i]),
tokenBeg,
tokenEnd
);
@@ -1123,49 +1210,49 @@ FilterContainer.prototype.matchTokens = function(url) {
if ( bucket0 !== undefined ) {
f = bucket0[token];
if ( f !== undefined && f.match(url, beg) !== false ) {
- return f.s;
+ return f;
}
}
if ( bucket1 !== undefined ) {
f = bucket1[token];
if ( f !== undefined && f.match(url, beg) !== false ) {
- return f.s;
+ return f;
}
}
if ( bucket2 !== undefined ) {
f = bucket2[token];
if ( f !== undefined && f.match(url, beg) !== false ) {
- return f.s;
+ return f;
}
}
if ( bucket3 !== undefined ) {
f = bucket3[token];
if ( f !== undefined && f.match(url, beg) !== false ) {
- return f.s;
+ return f;
}
}
if ( bucket4 !== undefined ) {
f = bucket4[token];
if ( f !== undefined && f.match(url, beg) !== false ) {
- return f.s;
+ return f;
}
}
if ( bucket5 !== undefined ) {
f = bucket5[token];
if ( f !== undefined && f.match(url, beg) !== false ) {
- return f.s;
+ return f;
}
}
if ( bucket6 !== undefined ) {
f = bucket6[token];
if ( f !== undefined && f.match(url, beg) !== false ) {
- return f.s;
+ return f;
}
}
if ( bucket7 !== undefined ) {
f = bucket7[token];
if ( f !== undefined && f.match(url, beg) !== false ) {
- return f.s;
+ return f;
}
}
}
@@ -1251,24 +1338,31 @@ FilterContainer.prototype.matchStringExactType = function(pageDetails, requestUR
buckets[4] = categories[this.makeCategoryKey(BlockAction | type | party)];
buckets[5] = categories[this.makeCategoryKey(BlockOneParty | type | domainParty)];
buckets[7] = categories[this.makeCategoryKey(BlockOneParty | type | this.noDomainBits)];
- var br = this.matchTokens(url);
+ var bf = this.matchTokens(url);
// If there is no block filter, no need to test against allow filters
- if ( br === false ) {
+ if ( bf === false ) {
return false;
}
+ // The purpose of the `important` property is to reverse the order of
+ // evaluation. Normally, it is "evaluate block then evaluate allow", with
+ // the `important` property it is "evaluate allow then evaluate block".
+ if ( bf.important === true ) {
+ return bf.toString();
+ }
+
// Test against allow filters
buckets[3] = categories[this.makeCategoryKey(AllowAnyParty | type)];
buckets[4] = categories[this.makeCategoryKey(AllowAction | type | party)];
buckets[5] = categories[this.makeCategoryKey(AllowOneParty | type | domainParty)];
buckets[7] = categories[this.makeCategoryKey(AllowOneParty | type | this.noDomainBits)];
- var ar = this.matchTokens(url);
- if ( ar !== false ) {
- return '@@' + ar;
+ var af = this.matchTokens(url);
+ if ( af !== false ) {
+ return '@@' + af.toString();
}
- return br;
+ return bf.toString();
};
/******************************************************************************/
@@ -1309,9 +1403,9 @@ FilterContainer.prototype.matchString = function(pageDetails, requestURL, reques
ThirdParty;
// Test hostname-based block filters
- var br = this.matchAnyPartyHostname(requestHostname);
- if ( br === false && party === ThirdParty ) {
- br = this.match3rdPartyHostname(requestHostname);
+ var bf = this.matchAnyPartyHostname(requestHostname);
+ if ( bf === false && party === ThirdParty ) {
+ bf = this.match3rdPartyHostname(requestHostname);
}
// This will be used by hostname-based filters
@@ -1323,7 +1417,7 @@ FilterContainer.prototype.matchString = function(pageDetails, requestURL, reques
var buckets = this.buckets;
// Test against block filters
- if ( br === false ) {
+ if ( bf === false ) {
buckets[0] = categories[this.makeCategoryKey(BlockAnyTypeAnyParty)];
buckets[1] = categories[this.makeCategoryKey(BlockAnyType | party)];
buckets[2] = categories[this.makeCategoryKey(BlockAnyTypeOneParty | domainParty)];
@@ -1334,14 +1428,21 @@ FilterContainer.prototype.matchString = function(pageDetails, requestURL, reques
// Test for synthetic domain as well
buckets[6] = categories[this.makeCategoryKey(BlockAnyTypeOneParty | this.noDomainBits)];
buckets[7] = categories[this.makeCategoryKey(BlockOneParty | type | this.noDomainBits)];
- br = this.matchTokens(url);
+ bf = this.matchTokens(url);
}
// If there is no block filter, no need to test against allow filters
- if ( br === false ) {
+ if ( bf === false ) {
return false;
}
+ // The purpose of the `important` property is to reverse the order of
+ // evaluation. Normally, it is "evaluate block then evaluate allow", with
+ // the `important` property it is "evaluate allow then evaluate block".
+ if ( bf.important === true ) {
+ return bf.toString();
+ }
+
// Test against allow filters
buckets[0] = categories[this.makeCategoryKey(AllowAnyTypeAnyParty)];
buckets[1] = categories[this.makeCategoryKey(AllowAnyType | party)];
@@ -1353,12 +1454,12 @@ FilterContainer.prototype.matchString = function(pageDetails, requestURL, reques
// Test for synthetic domain as well
buckets[6] = categories[this.makeCategoryKey(AllowAnyTypeOneParty | this.noDomainBits)];
buckets[7] = categories[this.makeCategoryKey(AllowOneParty | type | this.noDomainBits)];
- var ar = this.matchTokens(url);
- if ( ar !== false ) {
- return '@@' + ar;
+ var af = this.matchTokens(url);
+ if ( af !== false ) {
+ return '@@' + af.toString();
}
- return br;
+ return bf.toString();
};
/******************************************************************************/