this fixes #59: now accurately reporting static filters in logger

This commit is contained in:
gorhill 2015-06-09 10:27:08 -04:00
parent 323378fb38
commit 2234933b82
6 changed files with 646 additions and 322 deletions

View File

@ -104,7 +104,7 @@ var proceedPermanent = function() {
/******************************************************************************/
uDom('.what').text(details.url);
uDom('#why').text(details.why.slice(3));
uDom('#why').text(details.why);
if ( window.history.length > 1 ) {
uDom('#back').on('click', function() { window.history.back(); });

View File

@ -103,61 +103,24 @@ var tabIdFromClassName = function(className) {
/******************************************************************************/
var retextFromStaticFilteringResult = function(result) {
var retext = result.slice(3);
var pos = retext.indexOf('$');
if ( pos > 0 ) {
retext = retext.slice(0, pos);
}
if ( retext === '*' ) {
return '^.*$';
}
if ( retext.charAt(0) === '/' && retext.slice(-1) === '/' ) {
return retext.slice(1, -1);
}
return retext
.replace(/\./g, '\\.')
.replace(/\?/g, '\\?')
.replace('||', '')
.replace(/\^/g, '.')
.replace(/^\|/g, '^')
.replace(/\|$/g, '$')
.replace(/\*/g, '.*')
;
};
/******************************************************************************/
var retextFromURLFilteringResult = function(result) {
var regexFromURLFilteringResult = function(result) {
var beg = result.indexOf(' ');
var end = result.indexOf(' ', beg + 1);
var url = result.slice(beg + 1, end);
if ( url === '*' ) {
return '^.*$';
return new RegExp('^.*$', 'gi');
}
return '^' + url.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return new RegExp('^' + url.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
};
/******************************************************************************/
// Emphasize hostname in URL, as this is what matters in uMatrix's rules.
var nodeFromURL = function(url, filter) {
var filterType = filter.charAt(0);
if ( filterType !== 's' && filterType !== 'l' ) {
var nodeFromURL = function(url, re) {
if ( re instanceof RegExp === false ) {
return document.createTextNode(url);
}
// make a regex out of the filter
var retext = '';
if ( filterType === 's' ) {
retext = retextFromStaticFilteringResult(filter);
} else if ( filterType === 'l' ) {
retext = retextFromURLFilteringResult(filter);
}
if ( retext === '' ) {
return document.createTextNode(url);
}
var re = new RegExp(retext, 'gi');
var matches = re.exec(url);
if ( matches === null || matches[0].length === 0 ) {
return document.createTextNode(url);
@ -173,6 +136,187 @@ var renderedURLTemplate = document.querySelector('#renderedURLTemplate > span');
/******************************************************************************/
// Pretty much same logic as found in:
// µBlock.staticNetFilteringEngine.filterStringFromCompiled
// µBlock.staticNetFilteringEngine.filterRegexFromCompiled
var filterDecompiler = (function() {
var typeValToTypeName = {
1: 'stylesheet',
2: 'image',
3: 'object',
4: 'script',
5: 'xmlhttprequest',
6: 'sub_frame',
7: 'font',
8: 'other',
13: 'elemhide',
14: 'inline-script',
15: 'popup'
};
var toString = function(compiled) {
var opts = [];
var vfields = compiled.split('\v');
var filter = '';
var bits = parseInt(vfields[1], 16) | 0;
if ( bits & 0x01 ) {
filter += '@@';
}
var fid = vfields[2] === '.' ? '.' : vfields[3];
var tfields = fid !== '.' ? vfields[4].split('\t') : [];
var tfield0 = tfields[0];
switch ( fid ) {
case '.':
filter += '||' + vfields[3] + '^';
break;
case 'a':
case 'ah':
case '0a':
case '0ah':
case '1a':
case '1ah':
case '_':
case '_h':
filter += tfield0;
// If the filter resemble a regex, add a trailing `*` as is
// customary to prevent ambiguity in logger.
if ( tfield0.charAt(0) === '/' && tfield0.slice(-1) === '/' ) {
filter += '*';
}
break;
case '|a':
case '|ah':
filter += '|' + tfield0;
break;
case 'a|':
case 'a|h':
filter += tfield0 + '|';
break;
case '||a':
case '||ah':
case '||_':
case '||_h':
filter += '||' + tfield0;
break;
case '//':
case '//h':
filter += '/' + tfield0 + '/';
break;
default:
break;
}
// Domain option?
switch ( fid ) {
case '0ah':
case '1ah':
case '|ah':
case 'a|h':
case '||ah':
case '||_h':
case '//h':
opts.push('domain=' + tfields[1]);
break;
case 'ah':
case '_h':
opts.push('domain=' + tfields[2]);
break;
default:
break;
}
// Filter options
if ( bits & 0x02 ) {
opts.push('important');
}
if ( bits & 0x08 ) {
opts.push('third-party');
} else if ( bits & 0x04 ) {
opts.push('first-party');
}
var typeVal = bits >>> 4 & 0x0F;
if ( typeVal ) {
opts.push(typeValToTypeName[typeVal]);
// Because of the way `elemhide` is implemented
if ( typeVal === 13 ) {
filter = '@@' + filter;
}
}
if ( opts.length !== 0 ) {
filter += '$' + opts.join(',');
}
return filter;
};
var reEscape = /[.+?^${}()|[\]\\]/g;
var reWildcards = /\*+/g;
var toRegex = function(compiled) {
var vfields = compiled.split('\v');
var fid = vfields[2] === '.' ? '.' : vfields[3];
var tfields = fid !== '.' ? vfields[4].split('\t') : [];
var reStr;
switch ( fid ) {
case '.':
reStr = vfields[3]
.replace(reEscape, '\\$&');
break;
case 'a':
case 'ah':
case '0a':
case '0ah':
case '1a':
case '1ah':
case '_':
case '_h':
case '||a':
case '||ah':
case '||_':
case '||_h':
reStr = tfields[0]
.replace(reEscape, '\\$&')
.replace(reWildcards, '.*');
break;
case '|a':
case '|ah':
reStr = '^' + tfields[0].
replace(reEscape, '\\$&')
.replace(reWildcards, '.*');
break;
case 'a|':
case 'a|h':
reStr = tfields[0]
.replace(reEscape, '\\$&')
.replace(reWildcards, '.*') + '$';
break;
case '//':
case '//h':
reStr = tfields[0];
break;
default:
break;
}
if ( reStr === undefined) {
return null;
}
return new RegExp(reStr, 'gi');
};
return {
toString: toString,
toRegex: toRegex
};
})();
/******************************************************************************/
var createCellAt = function(tr, index) {
var td = tr.cells[index];
var mustAppend = !td;
@ -276,26 +420,31 @@ var renderNetLogEntry = function(tr, entry) {
tr.setAttribute('data-hn-frame', entry.d4);
}
// Cosmetic filter?
var filterCat = filter.slice(0, 3);
if ( filterCat.charAt(2) === ':' ) {
tr.classList.add(filterCat.slice(0, 2));
}
var filterText = filter.slice(3);
if ( filter.lastIndexOf('sa', 0) === 0 ) {
filterText = '@@' + filterText;
var filteringType = filterCat.charAt(0);
td = tr.cells[2];
if ( filter !== '' ) {
filter = filter.slice(3);
if ( filteringType === 's' ) {
td.textContent = filterDecompiler.toString(filter);
} else {
td.textContent = filter;
}
}
tr.cells[2].textContent = filterText;
td = tr.cells[3];
if ( filter.charAt(1) === 'b' ) {
var filteringOp = filterCat.charAt(1);
if ( filteringOp === 'b' ) {
tr.classList.add('blocked');
td.textContent = '--';
} else if ( filter.charAt(1) === 'a' ) {
} else if ( filteringOp === 'a' ) {
tr.classList.add('allowed');
td.textContent = '++';
} else if ( filter.charAt(1) === 'n' ) {
} else if ( filteringOp === 'n' ) {
tr.classList.add('nooped');
td.textContent = '**';
} else {
@ -303,7 +452,14 @@ var renderNetLogEntry = function(tr, entry) {
}
tr.cells[4].textContent = (prettyRequestTypes[type] || type);
tr.cells[5].appendChild(nodeFromURL(url, filter));
var re = null;
if ( filteringType === 's' ) {
re = filterDecompiler.toRegex(filter);
} else if ( filteringType === 'l' ) {
re = regexFromURLFilteringResult(filter);
}
tr.cells[5].appendChild(nodeFromURL(url, re));
};
/******************************************************************************/

View File

@ -114,7 +114,7 @@ NetFilteringResultCache.factory = function() {
NetFilteringResultCache.prototype.init = function() {
this.urls = {};
this.count = 0;
this.shelfLife = 60 * 1000;
this.shelfLife = 15 * 1000;
this.timer = null;
this.boundPruneAsyncCallback = this.pruneAsyncCallback.bind(this);
};
@ -309,11 +309,22 @@ PageStore.prototype.init = function(tabId) {
// Support `elemhide` filter option. Called at this point so the required
// context is all setup at this point.
var context = this.createContextFromPage();
this.skipCosmeticFiltering = µb.staticNetFilteringEngine
.matchStringExactType(context, tabContext.normalURL, 'cosmetic-filtering')
.charAt(1) === 'b';
this.skipCosmeticFiltering = µb.staticNetFilteringEngine.matchStringExactType(
this.createContextFromPage(),
tabContext.normalURL,
'cosmetic-filtering'
);
if ( this.skipCosmeticFiltering && µb.logger.isEnabled() ) {
µb.logger.writeOne(
tabId,
'net',
µb.staticNetFilteringEngine.toResultString(true),
'elemhide',
tabContext.rawURL,
this.tabHostname,
this.tabHostname
);
}
return this;
};
@ -515,7 +526,9 @@ PageStore.prototype.filterRequest = function(context) {
// Static filtering never override dynamic filtering
if ( result === '' || result.charAt(1) === 'n' ) {
result = µb.staticNetFilteringEngine.matchString(context) || result;
if ( µb.staticNetFilteringEngine.matchString(context) ) {
result = µb.staticNetFilteringEngine.toResultString(µb.logger.isEnabled());
}
}
//console.debug('cache MISS: PageStore.filterRequest("%s")', context.requestURL);
@ -555,7 +568,9 @@ PageStore.prototype.filterRequestNoCache = function(context) {
// Static filtering never override dynamic filtering
if ( result === '' || result.charAt(1) === 'n' ) {
result = µb.staticNetFilteringEngine.matchString(context) || result;
if ( µb.staticNetFilteringEngine.matchString(context) ) {
result = µb.staticNetFilteringEngine.toResultString(µb.logger.isEnabled());
}
}
return result;

View File

@ -1,7 +1,7 @@
/*******************************************************************************
µBlock - a browser extension to block requests.
Copyright (C) 2014 Raymond Hill
uBlock - a browser extension to block requests.
Copyright (C) 2014-2015 Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -70,6 +70,20 @@ var typeNameToTypeValue = {
};
var typeOtherValue = typeNameToTypeValue.other;
var typeValueToTypeName = {
1: 'stylesheet',
2: 'image',
3: 'object',
4: 'script',
5: 'xmlhttprequest',
6: 'sub_frame',
7: 'font',
8: 'other',
13: 'cosmetic-filtering',
14: 'inline-script',
15: 'popup'
};
// All network request types to bitmap
// bring origin to 0 (from 4 -- see typeNameToTypeValue)
// left-shift 1 by the above-calculated value
@ -175,7 +189,7 @@ var alwaysTruePseudoRegex = {
}
};
var strToRegex = function(s, anchor) {
var strToRegex = function(s, anchor, flags) {
// https://github.com/chrisaljoudi/uBlock/issues/1038
// Special case: always match.
if ( s === '*' ) {
@ -193,7 +207,7 @@ var strToRegex = function(s, anchor) {
}
//console.debug('µBlock.staticNetFilteringEngine: created RegExp("%s")', reStr);
return new RegExp(reStr);
return new RegExp(reStr, flags);
};
/*******************************************************************************
@ -235,15 +249,13 @@ FilterPlain.prototype.match = function(url, tokenBeg) {
return url.substr(tokenBeg - this.tokenBeg, this.s.length) === this.s;
};
FilterPlain.fid = FilterPlain.prototype.fid = 'a';
FilterPlain.fid =
FilterPlain.prototype.fid =
FilterPlain.prototype.rtfid = 'a';
FilterPlain.prototype.toString = function() {
return this.s;
};
FilterPlain.prototype.toSelfie = function() {
return this.s + '\t' +
this.tokenBeg;
FilterPlain.prototype.toSelfie =
FilterPlain.prototype.rtCompile = function() {
return this.s + '\t' + this.tokenBeg;
};
FilterPlain.compile = function(details) {
@ -268,22 +280,17 @@ FilterPlainHostname.prototype.match = function(url, tokenBeg) {
url.substr(tokenBeg - this.tokenBeg, this.s.length) === this.s;
};
FilterPlainHostname.fid = FilterPlainHostname.prototype.fid = 'ah';
FilterPlainHostname.fid =
FilterPlainHostname.prototype.fid =
FilterPlainHostname.prototype.rtfid = '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.prototype.toSelfie =
FilterPlainHostname.prototype.rtCompile = function() {
return this.s + '\t' + this.tokenBeg + '\t' + this.hostname;
};
FilterPlainHostname.compile = function(details, hostname) {
return details.f + '\t' +
details.tokenBeg + '\t' +
hostname;
return details.f + '\t' + details.tokenBeg + '\t' + hostname;
};
FilterPlainHostname.fromSelfie = function(s) {
@ -301,13 +308,12 @@ FilterPlainPrefix0.prototype.match = function(url, tokenBeg) {
return url.substr(tokenBeg, this.s.length) === this.s;
};
FilterPlainPrefix0.fid = FilterPlainPrefix0.prototype.fid = '0a';
FilterPlainPrefix0.fid =
FilterPlainPrefix0.prototype.fid =
FilterPlainPrefix0.prototype.rtfid = '0a';
FilterPlainPrefix0.prototype.toString = function() {
return this.s;
};
FilterPlainPrefix0.prototype.toSelfie = function() {
FilterPlainPrefix0.prototype.toSelfie =
FilterPlainPrefix0.prototype.rtCompile = function() {
return this.s;
};
@ -331,15 +337,13 @@ FilterPlainPrefix0Hostname.prototype.match = function(url, tokenBeg) {
url.substr(tokenBeg, this.s.length) === this.s;
};
FilterPlainPrefix0Hostname.fid = FilterPlainPrefix0Hostname.prototype.fid = '0ah';
FilterPlainPrefix0Hostname.fid =
FilterPlainPrefix0Hostname.prototype.fid =
FilterPlainPrefix0Hostname.prototype.rtfid = '0ah';
FilterPlainPrefix0Hostname.prototype.toString = function() {
return this.s + '$domain=' + this.hostname;
};
FilterPlainPrefix0Hostname.prototype.toSelfie = function() {
return this.s + '\t' +
this.hostname;
FilterPlainPrefix0Hostname.prototype.toSelfie =
FilterPlainPrefix0Hostname.prototype.rtCompile = function() {
return this.s + '\t' + this.hostname;
};
FilterPlainPrefix0Hostname.compile = function(details, hostname) {
@ -361,13 +365,12 @@ FilterPlainPrefix1.prototype.match = function(url, tokenBeg) {
return url.substr(tokenBeg - 1, this.s.length) === this.s;
};
FilterPlainPrefix1.fid = FilterPlainPrefix1.prototype.fid = '1a';
FilterPlainPrefix1.fid =
FilterPlainPrefix1.prototype.fid =
FilterPlainPrefix1.prototype.rtfid = '1a';
FilterPlainPrefix1.prototype.toString = function() {
return this.s;
};
FilterPlainPrefix1.prototype.toSelfie = function() {
FilterPlainPrefix1.prototype.toSelfie =
FilterPlainPrefix1.prototype.rtCompile = function() {
return this.s;
};
@ -391,15 +394,13 @@ FilterPlainPrefix1Hostname.prototype.match = function(url, tokenBeg) {
url.substr(tokenBeg - 1, this.s.length) === this.s;
};
FilterPlainPrefix1Hostname.fid = FilterPlainPrefix1Hostname.prototype.fid = '1ah';
FilterPlainPrefix1Hostname.fid =
FilterPlainPrefix1Hostname.prototype.fid =
FilterPlainPrefix1Hostname.prototype.rtfid = '1ah';
FilterPlainPrefix1Hostname.prototype.toString = function() {
return this.s + '$domain=' + this.hostname;
};
FilterPlainPrefix1Hostname.prototype.toSelfie = function() {
return this.s + '\t' +
this.hostname;
FilterPlainPrefix1Hostname.prototype.toSelfie =
FilterPlainPrefix1Hostname.prototype.rtCompile = function() {
return this.s + '\t' + this.hostname;
};
FilterPlainPrefix1Hostname.compile = function(details, hostname) {
@ -421,13 +422,12 @@ FilterPlainLeftAnchored.prototype.match = function(url) {
return url.slice(0, this.s.length) === this.s;
};
FilterPlainLeftAnchored.fid = FilterPlainLeftAnchored.prototype.fid = '|a';
FilterPlainLeftAnchored.fid =
FilterPlainLeftAnchored.prototype.fid =
FilterPlainLeftAnchored.prototype.rtfid = '|a';
FilterPlainLeftAnchored.prototype.toString = function() {
return '|' + this.s;
};
FilterPlainLeftAnchored.prototype.toSelfie = function() {
FilterPlainLeftAnchored.prototype.toSelfie =
FilterPlainLeftAnchored.prototype.rtCompile = function() {
return this.s;
};
@ -451,15 +451,13 @@ FilterPlainLeftAnchoredHostname.prototype.match = function(url) {
url.slice(0, this.s.length) === this.s;
};
FilterPlainLeftAnchoredHostname.fid = FilterPlainLeftAnchoredHostname.prototype.fid = '|ah';
FilterPlainLeftAnchoredHostname.fid =
FilterPlainLeftAnchoredHostname.prototype.fid =
FilterPlainLeftAnchoredHostname.prototype.rtfid = '|ah';
FilterPlainLeftAnchoredHostname.prototype.toString = function() {
return '|' + this.s + '$domain=' + this.hostname;
};
FilterPlainLeftAnchoredHostname.prototype.toSelfie = function() {
return this.s + '\t' +
this.hostname;
FilterPlainLeftAnchoredHostname.prototype.toSelfie =
FilterPlainLeftAnchoredHostname.prototype.rtCompile = function() {
return this.s + '\t' + this.hostname;
};
FilterPlainLeftAnchoredHostname.compile = function(details, hostname) {
@ -481,13 +479,12 @@ FilterPlainRightAnchored.prototype.match = function(url) {
return url.slice(-this.s.length) === this.s;
};
FilterPlainRightAnchored.fid = FilterPlainRightAnchored.prototype.fid = 'a|';
FilterPlainRightAnchored.fid =
FilterPlainRightAnchored.prototype.fid =
FilterPlainRightAnchored.prototype.rtfid = 'a|';
FilterPlainRightAnchored.prototype.toString = function() {
return this.s + '|';
};
FilterPlainRightAnchored.prototype.toSelfie = function() {
FilterPlainRightAnchored.prototype.toSelfie =
FilterPlainRightAnchored.prototype.rtCompile = function() {
return this.s;
};
@ -511,15 +508,13 @@ FilterPlainRightAnchoredHostname.prototype.match = function(url) {
url.slice(-this.s.length) === this.s;
};
FilterPlainRightAnchoredHostname.fid = FilterPlainRightAnchoredHostname.prototype.fid = 'a|h';
FilterPlainRightAnchoredHostname.fid =
FilterPlainRightAnchoredHostname.prototype.fid =
FilterPlainRightAnchoredHostname.prototype.rtfid = 'a|h';
FilterPlainRightAnchoredHostname.prototype.toString = function() {
return this.s + '|$domain=' + this.hostname;
};
FilterPlainRightAnchoredHostname.prototype.toSelfie = function() {
return this.s + '\t' +
this.hostname;
FilterPlainRightAnchoredHostname.prototype.toSelfie =
FilterPlainRightAnchoredHostname.prototype.rtCompile = function() {
return this.s + '\t' + this.hostname;
};
FilterPlainRightAnchoredHostname.compile = function(details, hostname) {
@ -550,13 +545,12 @@ FilterPlainHnAnchored.prototype.match = function(url, tokenBeg) {
reURLPostHostnameAnchors.test(url.slice(pos + 3, tokenBeg)) === false;
};
FilterPlainHnAnchored.fid = FilterPlainHnAnchored.prototype.fid = '||a';
FilterPlainHnAnchored.fid =
FilterPlainHnAnchored.prototype.fid =
FilterPlainHnAnchored.prototype.rtfid = '||a';
FilterPlainHnAnchored.prototype.toString = function() {
return '||' + this.s;
};
FilterPlainHnAnchored.prototype.toSelfie = function() {
FilterPlainHnAnchored.prototype.toSelfie =
FilterPlainHnAnchored.prototype.rtCompile = function() {
return this.s;
};
@ -592,13 +586,12 @@ FilterPlainHnAnchoredHostname.prototype.match = function(url, tokenBeg) {
reURLPostHostnameAnchors.test(url.slice(pos + 3, tokenBeg)) === false;
};
FilterPlainHnAnchoredHostname.fid = FilterPlainHnAnchoredHostname.prototype.fid = '||ah';
FilterPlainHnAnchoredHostname.fid =
FilterPlainHnAnchoredHostname.prototype.fid =
FilterPlainHnAnchoredHostname.prototype.rtfid = '||ah';
FilterPlainHnAnchoredHostname.prototype.toString = function() {
return '||' + this.s;
};
FilterPlainHnAnchoredHostname.prototype.toSelfie = function() {
FilterPlainHnAnchoredHostname.prototype.toSelfie =
FilterPlainHnAnchoredHostname.prototype.rtCompile = function() {
return this.s + '\t' + this.hostname;
};
@ -628,19 +621,12 @@ FilterGeneric.prototype.match = function(url) {
return this.re.test(url);
};
FilterGeneric.fid = FilterGeneric.prototype.fid = '_';
FilterGeneric.fid =
FilterGeneric.prototype.fid =
FilterGeneric.prototype.rtfid = '_';
FilterGeneric.prototype.toString = function() {
if ( this.anchor === 0 ) {
return this.s;
}
if ( this.anchor < 0 ) {
return '|' + this.s;
}
return this.s + '|';
};
FilterGeneric.prototype.toSelfie = function() {
FilterGeneric.prototype.toSelfie =
FilterGeneric.prototype.rtCompile = function() {
return this.s + '\t' + this.anchor;
};
@ -671,13 +657,12 @@ FilterGenericHostname.prototype.match = function(url) {
return FilterGeneric.prototype.match.call(this, url);
};
FilterGenericHostname.fid = FilterGenericHostname.prototype.fid = '_h';
FilterGenericHostname.fid =
FilterGenericHostname.prototype.fid =
FilterGenericHostname.prototype.rtfid = '_h';
FilterGenericHostname.prototype.toString = function() {
return FilterGeneric.prototype.toString.call(this) + '$domain=' + this.hostname;
};
FilterGenericHostname.prototype.toSelfie = function() {
FilterGenericHostname.prototype.toSelfie =
FilterGenericHostname.prototype.rtCompile = function() {
return FilterGeneric.prototype.toSelfie.call(this) + '\t' + this.hostname;
};
@ -717,13 +702,12 @@ FilterGenericHnAnchored.prototype.match = function(url) {
reURLPostHostnameAnchors.test(url.slice(pos + 3, match.index)) === false;
};
FilterGenericHnAnchored.fid = FilterGenericHnAnchored.prototype.fid = '||_';
FilterGenericHnAnchored.fid =
FilterGenericHnAnchored.prototype.fid =
FilterGenericHnAnchored.prototype.rtfid = '||_';
FilterGenericHnAnchored.prototype.toString = function() {
return '||' + this.s;
};
FilterGenericHnAnchored.prototype.toSelfie = function() {
FilterGenericHnAnchored.prototype.toSelfie =
FilterGenericHnAnchored.prototype.rtCompile = function() {
return this.s;
};
@ -751,13 +735,12 @@ FilterGenericHnAnchoredHostname.prototype.match = function(url) {
return FilterGenericHnAnchored.prototype.match.call(this, url);
};
FilterGenericHnAnchoredHostname.fid = FilterGenericHnAnchoredHostname.prototype.fid = '||_h';
FilterGenericHnAnchoredHostname.fid =
FilterGenericHnAnchoredHostname.prototype.fid =
FilterGenericHnAnchoredHostname.prototype.rtfid = '||_h';
FilterGenericHnAnchoredHostname.prototype.toString = function() {
return '||' + this.s + '$domain=' + this.hostname;
};
FilterGenericHnAnchoredHostname.prototype.toSelfie = function() {
FilterGenericHnAnchoredHostname.prototype.toSelfie =
FilterGenericHnAnchoredHostname.prototype.rtCompile = function() {
return this.s + '\t' + this.hostname;
};
@ -782,13 +765,12 @@ FilterRegex.prototype.match = function(url) {
return this.re.test(url);
};
FilterRegex.fid = FilterRegex.prototype.fid = '//';
FilterRegex.fid =
FilterRegex.prototype.fid =
FilterRegex.prototype.rtfid = '//';
FilterRegex.prototype.toString = function() {
return '/' + this.re.source + '/';
};
FilterRegex.prototype.toSelfie = function() {
FilterRegex.prototype.toSelfie =
FilterRegex.prototype.rtCompile = function() {
return this.re.source;
};
@ -813,13 +795,12 @@ FilterRegexHostname.prototype.match = function(url) {
this.re.test(url);
};
FilterRegexHostname.fid = FilterRegexHostname.prototype.fid = '//h';
FilterRegexHostname.fid =
FilterRegexHostname.prototype.fid =
FilterRegexHostname.prototype.rtfid = '//h';
FilterRegexHostname.prototype.toString = function() {
return '/' + this.re.source + '/$domain=' + this.hostname;
};
FilterRegexHostname.prototype.toSelfie = function() {
FilterRegexHostname.prototype.toSelfie =
FilterRegexHostname.prototype.rtCompile = function() {
return this.re.source + '\t' + this.hostname;
};
@ -1004,13 +985,15 @@ FilterHostnameDict.prototype.match = function() {
}
hostname = hostname.slice(pos + 1);
}
this.h = '||' + hostname + '^';
this.h = hostname;
return this;
};
FilterHostnameDict.fid = FilterHostnameDict.prototype.fid = '{h}';
FilterHostnameDict.fid =
FilterHostnameDict.prototype.fid = '{h}';
FilterHostnameDict.rtfid = '.';
FilterHostnameDict.prototype.toString = function() {
FilterHostnameDict.prototype.rtCompile = function() {
return this.h;
};
@ -1075,6 +1058,12 @@ var FilterBucket = function(a, b) {
this.filters[1] = b;
}
}
Object.defineProperty(this, 'rtfid', {
get: function() {
return this.f.rtfid;
}
});
};
FilterBucket.prototype.add = function(a) {
@ -1106,7 +1095,7 @@ FilterBucket.prototype.match = function(url, tokenBeg) {
var filters = this.filters;
var n = filters.length;
for ( var i = 0; i < n; i++ ) {
if ( filters[i].match(url, tokenBeg) !== false ) {
if ( filters[i].match(url, tokenBeg) ) {
this.f = filters[i];
if ( i >= this.vip ) {
this.promote(i);
@ -1119,17 +1108,15 @@ FilterBucket.prototype.match = function(url, tokenBeg) {
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();
};
// Not supposed to be called without a valid filter hit.
FilterBucket.prototype.rtCompile = function() {
return this.f.rtCompile();
};
FilterBucket.fromSelfie = function() {
return new FilterBucket();
};
@ -1601,6 +1588,11 @@ FilterContainer.prototype.reset = function() {
this.categories = Object.create(null);
this.filterParser.reset();
this.filterCounts = {};
// Runtime registers
this.keyRegister = undefined;
this.tokenRegister = undefined;
this.fRegister = null;
};
/******************************************************************************/
@ -2019,6 +2011,140 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg) {
/******************************************************************************/
FilterContainer.prototype.filterStringFromCompiled = function(compiled) {
var opts = [];
var vfields = compiled.split('\v');
var filter = '';
var bits = parseInt(vfields[1], 16) | 0;
if ( bits & 0x01 ) {
filter += '@@';
}
var rfid = vfields[2] === '.' ? '.' : vfields[3];
var tfields = rfid !== '.' ? vfields[4].split('\t') : [];
switch ( rfid ) {
case '.':
filter += '||' + vfields[3] + '^';
break;
case 'a':
case 'ah':
case '0a':
case '0ah':
case '1a':
case '1ah':
case '_':
case '_h':
filter += tfields[0];
break;
case '|a':
case '|ah':
filter += '|' + tfields[0];
break;
case 'a|':
case 'a|h':
filter += tfields[0] + '|';
break;
case '||a':
case '||ah':
case '||_':
case '||_h':
filter += '||' + tfields[0];
break;
case '//':
case '//h':
filter += '/' + tfields[0] + '/';
break;
default:
break;
}
// Domain option?
switch ( rfid ) {
case '0ah':
case '1ah':
case '|ah':
case 'a|h':
case '||ah':
case '||_h':
case '//h':
opts.push('domain=' + tfields[1]);
break;
case 'ah':
case '_h':
opts.push('domain=' + tfields[2]);
break;
default:
break;
}
// Filter options
if ( bits & 0x02 ) {
opts.push('important');
}
if ( bits & 0x08 ) {
opts.push('third-party');
} else if ( bits & 0x04 ) {
opts.push('first-party');
}
if ( bits & 0xF0 ) {
opts.push(typeValueToTypeName[bits >>> 4]);
}
if ( opts.length !== 0 ) {
filter += '$' + opts.join(',');
}
return filter;
};
/******************************************************************************/
FilterContainer.prototype.filterRegexFromCompiled = function(compiled, flags) {
var vfields = compiled.split('\v');
var rfid = vfields[2] === '.' ? '.' : vfields[3];
var tfields = rfid !== '.' ? vfields[4].split('\t') : [];
var re = null;
switch ( rfid ) {
case '.':
re = strToRegex(vfields[3], 0, flags);
break;
case 'a':
case 'ah':
case '0a':
case '0ah':
case '1a':
case '1ah':
case '_':
case '_h':
case '||a':
case '||ah':
case '||_':
case '||_h':
re = strToRegex(tfields[0], 0, flags);
break;
case '|a':
case '|ah':
re = strToRegex(tfields[0], -1, flags);
break;
case 'a|':
case 'a|h':
re = strToRegex(tfields[0], 1, flags);
break;
case '//':
case '//h':
re = new RegExp(tfields[0]);
break;
default:
break;
}
return re;
};
/******************************************************************************/
// Since the addition of the `important` evaluation, this means it is now
// likely that the url will have to be scanned more than once. So this is
// to ensure we do it once only, and reuse results.
@ -2058,8 +2184,10 @@ FilterContainer.prototype.tokenize = function(url) {
FilterContainer.prototype.matchTokens = function(bucket, url) {
// Hostname-only filters
var f = bucket['.'];
if ( f !== undefined && f.match() !== false ) {
return f;
if ( f !== undefined && f.match() ) {
this.tokenRegister = '.';
this.fRegister = f;
return true;
}
var tokens = this.tokens;
@ -2072,15 +2200,19 @@ FilterContainer.prototype.matchTokens = function(bucket, url) {
break;
}
f = bucket[token];
if ( f !== undefined && f.match(url, tokenEntry.beg) !== false ) {
return f;
if ( f !== undefined && f.match(url, tokenEntry.beg) ) {
this.tokenRegister = token;
this.fRegister = f;
return true;
}
}
// Regex-based filters
f = bucket['*'];
if ( f !== undefined && f.match(url) !== false ) {
return f;
if ( f !== undefined && f.match(url) ) {
this.tokenRegister = '*';
this.fRegister = f;
return true;
}
return false;
@ -2106,60 +2238,66 @@ FilterContainer.prototype.matchStringExactType = function(context, requestURL, r
// Be prepared to support unknown types
var type = typeNameToTypeValue[requestType] || 0;
if ( type === 0 ) {
return '';
return false;
}
var categories = this.categories;
var bf = false, bucket;
var bucket;
// Tokenize only once
this.tokenize(url);
this.fRegister = null;
// https://github.com/chrisaljoudi/uBlock/issues/139
// Test against important block filters
if ( bucket = categories[this.makeCategoryKey(BlockAnyParty | Important | type)] ) {
bf = this.matchTokens(bucket, url);
if ( bf !== false ) {
return 'sb:' + bf.toString();
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = BlockAnyParty | Important | type;
return true;
}
}
if ( bucket = categories[this.makeCategoryKey(BlockAction | Important | type | party)] ) {
bf = this.matchTokens(bucket, url);
if ( bf !== false ) {
return 'sb:' + bf.toString();
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = BlockAction | Important | type | party;
return true;
}
}
// Test against block filters
if ( bucket = categories[this.makeCategoryKey(BlockAnyParty | type)] ) {
bf = this.matchTokens(bucket, url);
}
if ( bf === false ) {
if ( bucket = categories[this.makeCategoryKey(BlockAction | type | party)] ) {
bf = this.matchTokens(bucket, url);
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = BlockAnyParty | type;
}
}
if ( this.fRegister === null ) {
if ( bucket = categories[this.makeCategoryKey(BlockAction | type | party)] ) {
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = BlockAction | type | party;
}
}
}
// If there is no block filter, no need to test against allow filters
if ( bf === false ) {
return '';
if ( this.fRegister === null ) {
return false;
}
// Test against allow filters
var af;
if ( bucket = categories[this.makeCategoryKey(AllowAnyParty | type)] ) {
af = this.matchTokens(bucket, url);
if ( af !== false ) {
return 'sa:' + af.toString();
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = AllowAnyParty | type;
return false;
}
}
if ( bucket = categories[this.makeCategoryKey(AllowAction | type | party)] ) {
af = this.matchTokens(bucket, url);
if ( af !== false ) {
return 'sa:' + af.toString();
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = AllowAction | type | party;
return false;
}
}
return 'sb:' + bf.toString();
return true;
};
/******************************************************************************/
@ -2212,7 +2350,7 @@ FilterContainer.prototype.matchString = function(context) {
// Tokenize only once
this.tokenize(url);
var bf = false;
this.fRegister = null;
// https://github.com/chrisaljoudi/uBlock/issues/139
// Test against important block filters.
@ -2220,86 +2358,113 @@ FilterContainer.prototype.matchString = function(context) {
// evaluation. Normally, it is "evaluate block then evaluate allow", with
// the `important` property it is "evaluate allow then evaluate block".
if ( bucket = filterClasses[this.makeCategoryKey(BlockAnyTypeAnyParty | Important)] ) {
bf = this.matchTokens(bucket, url);
if ( bf !== false ) {
return 'sb:' + bf.toString() + '$important';
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = BlockAnyTypeAnyParty | Important;
return true;
}
}
if ( bucket = filterClasses[this.makeCategoryKey(BlockAnyType | Important | party)] ) {
bf = this.matchTokens(bucket, url);
if ( bf !== false ) {
return 'sb:' + bf.toString() + '$important';
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = BlockAnyType | Important | party;
return true;
}
}
if ( bucket = filterClasses[this.makeCategoryKey(BlockAnyParty | Important | type)] ) {
bf = this.matchTokens(bucket, url);
if ( bf !== false ) {
return 'sb:' + bf.toString() + '$important';
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = BlockAnyParty | Important | type;
return true;
}
}
if ( bucket = filterClasses[this.makeCategoryKey(BlockAction | Important | type | party)] ) {
bf = this.matchTokens(bucket, url);
if ( bf !== false ) {
return 'sb:' + bf.toString() + '$important';
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = BlockAction | Important | type | party;
return true;
}
}
// Test against block filters
if ( bf === false ) {
if ( bucket = filterClasses[this.makeCategoryKey(BlockAnyTypeAnyParty)] ) {
bf = this.matchTokens(bucket, url);
if ( bucket = filterClasses[this.makeCategoryKey(BlockAnyTypeAnyParty)] ) {
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = BlockAnyTypeAnyParty;
}
}
if ( bf === false ) {
if ( this.fRegister === null ) {
if ( bucket = filterClasses[this.makeCategoryKey(BlockAnyType | party)] ) {
bf = this.matchTokens(bucket, url);
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = BlockAnyType | party;
}
}
}
if ( bf === false ) {
if ( bucket = filterClasses[this.makeCategoryKey(BlockAnyParty | type)] ) {
bf = this.matchTokens(bucket, url);
}
}
if ( bf === false ) {
if ( bucket = filterClasses[this.makeCategoryKey(BlockAction | type | party)] ) {
bf = this.matchTokens(bucket, url);
if ( this.fRegister === null ) {
if ( bucket = filterClasses[this.makeCategoryKey(BlockAnyParty | type)] ) {
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = BlockAnyParty | type;
}
}
if ( this.fRegister === null ) {
if ( bucket = filterClasses[this.makeCategoryKey(BlockAction | type | party)] ) {
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = BlockAction | type | party;
}
}
}
}
}
// If there is no block filter, no need to test against allow filters
if ( bf === false ) {
return '';
if ( this.fRegister === null ) {
return false;
}
// Test against allow filters
var af;
if ( bucket = filterClasses[this.makeCategoryKey(AllowAnyTypeAnyParty)] ) {
af = this.matchTokens(bucket, url);
if ( af !== false ) {
return 'sa:' + af.toString();
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = AllowAnyTypeAnyParty;
return false;
}
}
if ( bucket = filterClasses[this.makeCategoryKey(AllowAnyType | party)] ) {
af = this.matchTokens(bucket, url);
if ( af !== false ) {
return 'sa:' + af.toString();
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = AllowAnyType | party;
return false;
}
}
if ( bucket = filterClasses[this.makeCategoryKey(AllowAnyParty | type)] ) {
af = this.matchTokens(bucket, url);
if ( af !== false ) {
return 'sa:' + af.toString();
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = AllowAnyParty | type;
return false;
}
}
if ( bucket = filterClasses[this.makeCategoryKey(AllowAction | type | party)] ) {
af = this.matchTokens(bucket, url);
if ( af !== false ) {
return 'sa:' + af.toString();
if ( this.matchTokens(bucket, url) ) {
this.keyRegister = AllowAction | type | party;
return false;
}
}
return 'sb:' + bf.toString();
return true;
};
/******************************************************************************/
// The `verbose` argment tells whether to return a short or long version of
// the filter string. Typically, if the logger is not enabled, there is no
// point in returning the long version: this saves overhead.
FilterContainer.prototype.toResultString = function(verbose) {
if ( this.fRegister === null ) {
return '';
}
var s = this.keyRegister & 0x01 ? 'sa:' : 'sb:';
if ( !verbose ) {
return s;
}
s += 'n\v' + this.makeCategoryKey(this.keyRegister) + '\v' + this.tokenRegister + '\v';
if ( this.tokenRegister === '.' ) {
s += this.fRegister.rtCompile();
} else {
s += this.fRegister.rtfid + '\v' + this.fRegister.rtCompile();
}
return s;
};
/******************************************************************************/

View File

@ -1,7 +1,7 @@
/*******************************************************************************
µBlock - a browser extension to block requests.
Copyright (C) 2014 Raymond Hill
uBlock - a browser extension to block requests.
Copyright (C) 2014-2015 Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -492,6 +492,7 @@ vAPI.tabs.onPopup = function(details) {
};
var result = '';
var loggerEnabled = µb.logger.isEnabled();
// Check user switch first
if ( µb.hnSwitches.evaluateZ('no-popups', openerHostname) ) {
@ -506,7 +507,9 @@ vAPI.tabs.onPopup = function(details) {
µb.getNetFilteringSwitch(openerURL) &&
µb.getNetFilteringSwitch(targetURL)
) {
result = µb.staticNetFilteringEngine.matchStringExactType(context, targetURL, 'popup');
if ( µb.staticNetFilteringEngine.matchStringExactType(context, targetURL, 'popup') ) {
result = µb.staticNetFilteringEngine.toResultString(loggerEnabled);
}
}
// https://github.com/chrisaljoudi/uBlock/issues/91
@ -514,15 +517,17 @@ vAPI.tabs.onPopup = function(details) {
if ( pageStore ) {
pageStore.logRequest(context, result);
}
µb.logger.writeOne(
details.openerTabId,
'net',
result,
'popup',
targetURL,
openerHostname,
openerHostname
);
if ( loggerEnabled ) {
µb.logger.writeOne(
details.openerTabId,
'net',
result,
'popup',
targetURL,
openerHostname,
openerHostname
);
}
// Not blocked
if ( µb.isAllowResult(result) ) {

View File

@ -1,7 +1,7 @@
/*******************************************************************************
µBlock - a browser extension to block requests.
Copyright (C) 2014 Raymond Hill
uBlock - a browser extension to block requests.
Copyright (C) 2014-2015 Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -185,11 +185,14 @@ var onBeforeRootFrameRequest = function(details) {
// Filtering
if ( result === '' ) {
result = µb.staticNetFilteringEngine.matchString(context);
// https://github.com/chrisaljoudi/uBlock/issues/1128
// Do not block if the match begins after the hostname.
if ( result !== '' ) {
result = toBlockDocResult(requestURL, requestHostname, result);
if ( µb.staticNetFilteringEngine.matchString(context) ) {
// We always need the long-form result here.
result = µb.staticNetFilteringEngine.toResultString(true);
// https://github.com/chrisaljoudi/uBlock/issues/1128
// Do not block if the match begins after the hostname.
if ( result.charAt(1) === 'b' ) {
result = toBlockDocResult(requestURL, requestHostname, result);
}
}
}
@ -221,7 +224,7 @@ var onBeforeRootFrameRequest = function(details) {
url: requestURL,
hn: requestHostname,
dn: requestDomain,
why: result
why: µb.staticNetFilteringEngine.filterStringFromCompiled(result.slice(3))
}));
vAPI.tabs.replace(tabId, vAPI.getURL('document-blocked.html?details=') + query);
@ -232,32 +235,12 @@ var onBeforeRootFrameRequest = function(details) {
/******************************************************************************/
var toBlockDocResult = function(url, hostname, result) {
if ( result.charAt(1) !== 'b' ) {
// Make a regex out of the result
var re = µBlock.staticNetFilteringEngine
.filterRegexFromCompiled(result.slice(3), 'gi');
if ( re === null ) {
return '';
}
// Make a regex out of the result
var reText = result.slice(3);
var pos = reText.indexOf('$');
if ( pos > 0 ) {
reText = reText.slice(0, pos);
}
// We are going to have to take the long way to find out
if ( reText.charAt(0) === '/' && reText.slice(-1) === '/' ) {
reText = reText.slice(1, -1);
} else {
reText = reText
.replace(/\./g, '\\.')
.replace(/\?/g, '\\?')
.replace(/^\|\|/, '')
.replace(/\^/g, '.')
.replace(/^\|/g, '^')
.replace(/\|$/g, '$')
.replace(/\*/g, '.*');
}
var re = new RegExp(reText, 'gi');
var matches = re.exec(url);
if ( matches === null ) {
return '';