mirror of https://github.com/gorhill/uBlock.git
Add support for procedural :not to HTML filtering
Related issue: <https://github.com/gorhill/uBlock/issues/3683> Additionally, improve compile-time error reporting in the logger
This commit is contained in:
parent
01599b9653
commit
261ef8c510
|
@ -361,25 +361,6 @@ let FilterContainer = function() {
|
||||||
this.reEscapeSequence = /\\([0-9A-Fa-f]+ |.)/g;
|
this.reEscapeSequence = /\\([0-9A-Fa-f]+ |.)/g;
|
||||||
this.reSimpleHighGeneric1 = /^[a-z]*\[[^[]+]$/;
|
this.reSimpleHighGeneric1 = /^[a-z]*\[[^[]+]$/;
|
||||||
this.reHighMedium = /^\[href\^="https?:\/\/([^"]{8})[^"]*"\]$/;
|
this.reHighMedium = /^\[href\^="https?:\/\/([^"]{8})[^"]*"\]$/;
|
||||||
this.reNeedHostname = new RegExp([
|
|
||||||
'^',
|
|
||||||
'(?:',
|
|
||||||
[
|
|
||||||
'.+?:has',
|
|
||||||
'.+?:has-text',
|
|
||||||
'.+?:if',
|
|
||||||
'.+?:if-not',
|
|
||||||
'.+?:matches-css(?:-before|-after)?',
|
|
||||||
'.*?:xpath',
|
|
||||||
'.+?:style',
|
|
||||||
'.+?:-abp-contains', // ABP-specific for `:has-text`
|
|
||||||
'.+?:-abp-has', // ABP-specific for `:if`
|
|
||||||
'.+?:contains' // Adguard-specific for `:has-text`
|
|
||||||
].join('|'),
|
|
||||||
')',
|
|
||||||
'\\(.+\\)',
|
|
||||||
'$'
|
|
||||||
].join(''));
|
|
||||||
|
|
||||||
this.selectorCache = new Map();
|
this.selectorCache = new Map();
|
||||||
this.selectorCachePruneDelay = 10 * 60 * 1000; // 10 minutes
|
this.selectorCachePruneDelay = 10 * 60 * 1000; // 10 minutes
|
||||||
|
@ -584,76 +565,65 @@ FilterContainer.prototype.compileGenericHideSelector = function(
|
||||||
writer
|
writer
|
||||||
) {
|
) {
|
||||||
const selector = parsed.suffix;
|
const selector = parsed.suffix;
|
||||||
|
const type = selector.charCodeAt(0);
|
||||||
|
let key;
|
||||||
|
|
||||||
// For some selectors, it is mandatory to have a hostname or entity:
|
if ( type === 0x23 /* '#' */ ) {
|
||||||
// ##.foo:-abp-contains(...)
|
key = this.keyFromSelector(selector);
|
||||||
// ##.foo:-abp-has(...)
|
// Simple selector-based CSS rule: no need to test for whether the
|
||||||
// ##.foo:contains(...)
|
// selector is valid, the regex took care of this. Most generic
|
||||||
// ##.foo:has(...)
|
// selector falls into that category.
|
||||||
// ##.foo:has-text(...)
|
// - ###ad-bigbox
|
||||||
// ##.foo:if(...)
|
if ( key === selector ) {
|
||||||
// ##.foo:if-not(...)
|
writer.push([ 0, key.slice(1) ]);
|
||||||
// ##.foo:matches-css(...)
|
return;
|
||||||
// ##.foo:matches-css-after(...)
|
}
|
||||||
// ##.foo:matches-css-before(...)
|
} else if ( type === 0x2E /* '.' */ ) {
|
||||||
// ##:xpath(...)
|
key = this.keyFromSelector(selector);
|
||||||
// ##.foo:style(...)
|
// Simple selector-based CSS rule: no need to test for whether the
|
||||||
if ( this.reNeedHostname.test(selector) ) {
|
// selector is valid, the regex took care of this. Most generic
|
||||||
|
// selector falls into that category.
|
||||||
|
// - ##.ads-bigbox
|
||||||
|
if ( key === selector ) {
|
||||||
|
writer.push([ 2, key.slice(1) ]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const compiled = µb.staticExtFilteringEngine.compileSelector(selector);
|
||||||
|
|
||||||
|
// Invalid cosmetic filter, possible reasons:
|
||||||
|
// - Bad syntax
|
||||||
|
// - Procedural filters (can't be generic): the compiled version of
|
||||||
|
// a procedural selector is NEVER equal to its raw version.
|
||||||
|
if ( compiled === undefined || compiled !== selector ) {
|
||||||
|
const who = writer.properties.get('assetKey') || '?';
|
||||||
µb.logger.writeOne({
|
µb.logger.writeOne({
|
||||||
error: 'Cosmetic filtering – invalid generic filter: ##' + selector
|
error: `Invalid generic cosmetic filter in ${who} : ##${selector}`
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let type = selector.charCodeAt(0);
|
// Complex selector-based CSS rule:
|
||||||
|
// - ###tads + div + .c
|
||||||
if ( type === 0x23 /* '#' */ ) {
|
// - ##.rscontainer > .ellip
|
||||||
const key = this.keyFromSelector(selector);
|
if ( key !== undefined ) {
|
||||||
if ( key === undefined ) { return; }
|
writer.push([
|
||||||
// Simple selector-based CSS rule: no need to test for whether the
|
type === 0x23 /* '#' */ ? 1 : 3,
|
||||||
// selector is valid, the regex took care of this. Most generic
|
key.slice(1),
|
||||||
// selector falls into that category.
|
selector ]
|
||||||
if ( key === selector ) {
|
);
|
||||||
writer.push([ 0 /* lg */, key.slice(1) ]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Complex selector-based CSS rule.
|
|
||||||
if ( µb.staticExtFilteringEngine.compileSelector(selector) !== undefined ) {
|
|
||||||
writer.push([ 1 /* lg+ */, key.slice(1), selector ]);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( type === 0x2E /* '.' */ ) {
|
|
||||||
const key = this.keyFromSelector(selector);
|
|
||||||
if ( key === undefined ) { return; }
|
|
||||||
// Simple selector-based CSS rule: no need to test for whether the
|
|
||||||
// selector is valid, the regex took care of this. Most generic
|
|
||||||
// selector falls into that category.
|
|
||||||
if ( key === selector ) {
|
|
||||||
writer.push([ 2 /* lg */, key.slice(1) ]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Complex selector-based CSS rule.
|
|
||||||
if ( µb.staticExtFilteringEngine.compileSelector(selector) !== undefined ) {
|
|
||||||
writer.push([ 3 /* lg+ */, key.slice(1), selector ]);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const compiled = µb.staticExtFilteringEngine.compileSelector(selector);
|
|
||||||
if ( compiled === undefined ) { return; }
|
|
||||||
// TODO: Detect and error on procedural cosmetic filters.
|
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/909
|
// https://github.com/gorhill/uBlock/issues/909
|
||||||
// Anything which contains a plain id/class selector can be classified
|
// Anything which contains a plain id/class selector can be classified
|
||||||
// as a low generic cosmetic filter.
|
// as a low generic cosmetic filter.
|
||||||
const matches = this.rePlainSelectorEx.exec(selector);
|
const matches = this.rePlainSelectorEx.exec(selector);
|
||||||
if ( matches !== null ) {
|
if ( matches !== null ) {
|
||||||
const key = matches[1] || matches[2];
|
const key = matches[1] || matches[2];
|
||||||
type = key.charCodeAt(0);
|
|
||||||
writer.push([
|
writer.push([
|
||||||
type === 0x23 ? 1 : 3 /* lg+ */,
|
key.charCodeAt(0) === 0x23 /* '#' */ ? 1 : 3,
|
||||||
key.slice(1),
|
key.slice(1),
|
||||||
selector
|
selector
|
||||||
]);
|
]);
|
||||||
|
@ -685,7 +655,13 @@ FilterContainer.prototype.compileGenericUnhideSelector = function(
|
||||||
) {
|
) {
|
||||||
// Procedural cosmetic filters are acceptable as generic exception filters.
|
// Procedural cosmetic filters are acceptable as generic exception filters.
|
||||||
let compiled = µb.staticExtFilteringEngine.compileSelector(parsed.suffix);
|
let compiled = µb.staticExtFilteringEngine.compileSelector(parsed.suffix);
|
||||||
if ( compiled === undefined ) { return; }
|
if ( compiled === undefined ) {
|
||||||
|
const who = writer.properties.get('assetKey') || '?';
|
||||||
|
µb.logger.writeOne({
|
||||||
|
error: `Invalid cosmetic filter in ${who} : #@#${parsed.suffix}`
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/497
|
// https://github.com/chrisaljoudi/uBlock/issues/497
|
||||||
// All generic exception filters are put in the same bucket: they are
|
// All generic exception filters are put in the same bucket: they are
|
||||||
|
@ -708,7 +684,13 @@ FilterContainer.prototype.compileSpecificSelector = function(
|
||||||
}
|
}
|
||||||
|
|
||||||
let compiled = µb.staticExtFilteringEngine.compileSelector(parsed.suffix);
|
let compiled = µb.staticExtFilteringEngine.compileSelector(parsed.suffix);
|
||||||
if ( compiled === undefined ) { return; }
|
if ( compiled === undefined ) {
|
||||||
|
const who = writer.properties.get('assetKey') || '?';
|
||||||
|
µb.logger.writeOne({
|
||||||
|
error: `Invalid cosmetic filter in ${who} : ##${parsed.suffix}`
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let hash = µb.staticExtFilteringEngine.compileHostnameToHash(hostname);
|
let hash = µb.staticExtFilteringEngine.compileHostnameToHash(hostname);
|
||||||
|
|
||||||
|
|
|
@ -24,16 +24,24 @@
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
µBlock.htmlFilteringEngine = (function() {
|
µBlock.htmlFilteringEngine = (function() {
|
||||||
const api = {};
|
const µb = µBlock;
|
||||||
|
const pselectors = new Map();
|
||||||
|
const duplicates = new Set();
|
||||||
|
|
||||||
const µb = µBlock,
|
|
||||||
pselectors = new Map(),
|
|
||||||
duplicates = new Set();
|
|
||||||
let filterDB = new µb.staticExtFilteringEngine.HostnameBasedDB(),
|
let filterDB = new µb.staticExtFilteringEngine.HostnameBasedDB(),
|
||||||
acceptedCount = 0,
|
acceptedCount = 0,
|
||||||
discardedCount = 0,
|
discardedCount = 0,
|
||||||
docRegister;
|
docRegister;
|
||||||
|
|
||||||
|
const api = {
|
||||||
|
get acceptedCount() {
|
||||||
|
return acceptedCount;
|
||||||
|
},
|
||||||
|
get discardedCount() {
|
||||||
|
return discardedCount;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const PSelectorHasTextTask = function(task) {
|
const PSelectorHasTextTask = function(task) {
|
||||||
let arg0 = task[1], arg1;
|
let arg0 = task[1], arg1;
|
||||||
if ( Array.isArray(task[1]) ) {
|
if ( Array.isArray(task[1]) ) {
|
||||||
|
@ -42,8 +50,8 @@
|
||||||
this.needle = new RegExp(arg0, arg1);
|
this.needle = new RegExp(arg0, arg1);
|
||||||
};
|
};
|
||||||
PSelectorHasTextTask.prototype.exec = function(input) {
|
PSelectorHasTextTask.prototype.exec = function(input) {
|
||||||
let output = [];
|
const output = [];
|
||||||
for ( let node of input ) {
|
for ( const node of input ) {
|
||||||
if ( this.needle.test(node.textContent) ) {
|
if ( this.needle.test(node.textContent) ) {
|
||||||
output.push(node);
|
output.push(node);
|
||||||
}
|
}
|
||||||
|
@ -61,8 +69,8 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
PSelectorIfTask.prototype.exec = function(input) {
|
PSelectorIfTask.prototype.exec = function(input) {
|
||||||
let output = [];
|
const output = [];
|
||||||
for ( let node of input ) {
|
for ( const node of input ) {
|
||||||
if ( this.pselector.test(node) === this.target ) {
|
if ( this.pselector.test(node) === this.target ) {
|
||||||
output.push(node);
|
output.push(node);
|
||||||
}
|
}
|
||||||
|
@ -81,10 +89,10 @@
|
||||||
this.xpe = task[1];
|
this.xpe = task[1];
|
||||||
};
|
};
|
||||||
PSelectorXpathTask.prototype.exec = function(input) {
|
PSelectorXpathTask.prototype.exec = function(input) {
|
||||||
let output = [],
|
const output = [];
|
||||||
xpe = docRegister.createExpression(this.xpe, null),
|
const xpe = docRegister.createExpression(this.xpe, null);
|
||||||
xpr = null;
|
let xpr = null;
|
||||||
for ( let node of input ) {
|
for ( const node of input ) {
|
||||||
xpr = xpe.evaluate(
|
xpr = xpe.evaluate(
|
||||||
node,
|
node,
|
||||||
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
||||||
|
@ -92,7 +100,7 @@
|
||||||
);
|
);
|
||||||
let j = xpr.snapshotLength;
|
let j = xpr.snapshotLength;
|
||||||
while ( j-- ) {
|
while ( j-- ) {
|
||||||
node = xpr.snapshotItem(j);
|
const node = xpr.snapshotItem(j);
|
||||||
if ( node.nodeType === 1 ) {
|
if ( node.nodeType === 1 ) {
|
||||||
output.push(node);
|
output.push(node);
|
||||||
}
|
}
|
||||||
|
@ -108,6 +116,7 @@
|
||||||
[ ':has-text', PSelectorHasTextTask ],
|
[ ':has-text', PSelectorHasTextTask ],
|
||||||
[ ':if', PSelectorIfTask ],
|
[ ':if', PSelectorIfTask ],
|
||||||
[ ':if-not', PSelectorIfNotTask ],
|
[ ':if-not', PSelectorIfNotTask ],
|
||||||
|
[ ':not', PSelectorIfNotTask ],
|
||||||
[ ':xpath', PSelectorXpathTask ]
|
[ ':xpath', PSelectorXpathTask ]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -115,13 +124,13 @@
|
||||||
this.selector = o.selector;
|
this.selector = o.selector;
|
||||||
this.tasks = [];
|
this.tasks = [];
|
||||||
if ( !o.tasks ) { return; }
|
if ( !o.tasks ) { return; }
|
||||||
for ( let task of o.tasks ) {
|
for ( const task of o.tasks ) {
|
||||||
let ctor = this.operatorToTaskMap.get(task[0]);
|
const ctor = this.operatorToTaskMap.get(task[0]);
|
||||||
if ( ctor === undefined ) {
|
if ( ctor === undefined ) {
|
||||||
this.invalid = true;
|
this.invalid = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let pselector = new ctor(task);
|
const pselector = new ctor(task);
|
||||||
if ( pselector instanceof PSelectorIfTask && pselector.invalid ) {
|
if ( pselector instanceof PSelectorIfTask && pselector.invalid ) {
|
||||||
this.invalid = true;
|
this.invalid = true;
|
||||||
break;
|
break;
|
||||||
|
@ -132,7 +141,7 @@
|
||||||
PSelector.prototype.operatorToTaskMap = undefined;
|
PSelector.prototype.operatorToTaskMap = undefined;
|
||||||
PSelector.prototype.invalid = false;
|
PSelector.prototype.invalid = false;
|
||||||
PSelector.prototype.prime = function(input) {
|
PSelector.prototype.prime = function(input) {
|
||||||
let root = input || docRegister;
|
const root = input || docRegister;
|
||||||
if ( this.selector !== '' ) {
|
if ( this.selector !== '' ) {
|
||||||
return root.querySelectorAll(this.selector);
|
return root.querySelectorAll(this.selector);
|
||||||
}
|
}
|
||||||
|
@ -141,7 +150,7 @@
|
||||||
PSelector.prototype.exec = function(input) {
|
PSelector.prototype.exec = function(input) {
|
||||||
if ( this.invalid ) { return []; }
|
if ( this.invalid ) { return []; }
|
||||||
let nodes = this.prime(input);
|
let nodes = this.prime(input);
|
||||||
for ( let task of this.tasks ) {
|
for ( const task of this.tasks ) {
|
||||||
if ( nodes.length === 0 ) { break; }
|
if ( nodes.length === 0 ) { break; }
|
||||||
nodes = task.exec(nodes);
|
nodes = task.exec(nodes);
|
||||||
}
|
}
|
||||||
|
@ -149,10 +158,12 @@
|
||||||
};
|
};
|
||||||
PSelector.prototype.test = function(input) {
|
PSelector.prototype.test = function(input) {
|
||||||
if ( this.invalid ) { return false; }
|
if ( this.invalid ) { return false; }
|
||||||
let nodes = this.prime(input), AA = [ null ], aa;
|
const nodes = this.prime(input);
|
||||||
for ( let node of nodes ) {
|
const AA = [ null ];
|
||||||
AA[0] = node; aa = AA;
|
for ( const node of nodes ) {
|
||||||
for ( var task of this.tasks ) {
|
AA[0] = node;
|
||||||
|
let aa = AA;
|
||||||
|
for ( const task of this.tasks ) {
|
||||||
aa = task.exec(aa);
|
aa = task.exec(aa);
|
||||||
if ( aa.length === 0 ) { break; }
|
if ( aa.length === 0 ) { break; }
|
||||||
}
|
}
|
||||||
|
@ -182,11 +193,11 @@
|
||||||
pselector = new PSelector(JSON.parse(selector));
|
pselector = new PSelector(JSON.parse(selector));
|
||||||
pselectors.set(selector, pselector);
|
pselectors.set(selector, pselector);
|
||||||
}
|
}
|
||||||
let nodes = pselector.exec(),
|
const nodes = pselector.exec();
|
||||||
i = nodes.length,
|
let i = nodes.length,
|
||||||
modified = false;
|
modified = false;
|
||||||
while ( i-- ) {
|
while ( i-- ) {
|
||||||
let node = nodes[i];
|
const node = nodes[i];
|
||||||
if ( node.parentNode !== null ) {
|
if ( node.parentNode !== null ) {
|
||||||
node.parentNode.removeChild(node);
|
node.parentNode.removeChild(node);
|
||||||
modified = true;
|
modified = true;
|
||||||
|
@ -199,11 +210,11 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyCSSSelector = function(details, selector) {
|
const applyCSSSelector = function(details, selector) {
|
||||||
let nodes = docRegister.querySelectorAll(selector),
|
const nodes = docRegister.querySelectorAll(selector);
|
||||||
i = nodes.length,
|
let i = nodes.length,
|
||||||
modified = false;
|
modified = false;
|
||||||
while ( i-- ) {
|
while ( i-- ) {
|
||||||
let node = nodes[i];
|
const node = nodes[i];
|
||||||
if ( node.parentNode !== null ) {
|
if ( node.parentNode !== null ) {
|
||||||
node.parentNode.removeChild(node);
|
node.parentNode.removeChild(node);
|
||||||
modified = true;
|
modified = true;
|
||||||
|
@ -228,16 +239,22 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
api.compile = function(parsed, writer) {
|
api.compile = function(parsed, writer) {
|
||||||
let selector = parsed.suffix.slice(1).trim(),
|
const selector = parsed.suffix.slice(1).trim();
|
||||||
compiled = µb.staticExtFilteringEngine.compileSelector(selector);
|
const compiled = µb.staticExtFilteringEngine.compileSelector(selector);
|
||||||
if ( compiled === undefined ) { return; }
|
if ( compiled === undefined ) {
|
||||||
|
const who = writer.properties.get('assetKey') || '?';
|
||||||
|
µb.logger.writeOne({
|
||||||
|
error: `Invalid HTML filter in ${who} : ##${selector}`
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 1002 = html filtering
|
// 1002 = html filtering
|
||||||
writer.select(1002);
|
writer.select(1002);
|
||||||
|
|
||||||
// TODO: Mind negated hostnames, they are currently discarded.
|
// TODO: Mind negated hostnames, they are currently discarded.
|
||||||
|
|
||||||
for ( let hn of parsed.hostnames ) {
|
for ( const hn of parsed.hostnames ) {
|
||||||
if ( hn.charCodeAt(0) === 0x7E /* '~' */ ) { continue; }
|
if ( hn.charCodeAt(0) === 0x7E /* '~' */ ) { continue; }
|
||||||
let hash = µb.staticExtFilteringEngine.compileHostnameToHash(hn);
|
let hash = µb.staticExtFilteringEngine.compileHostnameToHash(hn);
|
||||||
if ( parsed.exception ) {
|
if ( parsed.exception ) {
|
||||||
|
@ -261,13 +278,13 @@
|
||||||
|
|
||||||
while ( reader.next() ) {
|
while ( reader.next() ) {
|
||||||
acceptedCount += 1;
|
acceptedCount += 1;
|
||||||
let fingerprint = reader.fingerprint();
|
const fingerprint = reader.fingerprint();
|
||||||
if ( duplicates.has(fingerprint) ) {
|
if ( duplicates.has(fingerprint) ) {
|
||||||
discardedCount += 1;
|
discardedCount += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
duplicates.add(fingerprint);
|
duplicates.add(fingerprint);
|
||||||
let args = reader.args();
|
const args = reader.args();
|
||||||
filterDB.add(args[1], {
|
filterDB.add(args[1], {
|
||||||
type: args[0],
|
type: args[0],
|
||||||
hostname: args[2],
|
hostname: args[2],
|
||||||
|
@ -335,7 +352,7 @@
|
||||||
api.apply = function(doc, details) {
|
api.apply = function(doc, details) {
|
||||||
docRegister = doc;
|
docRegister = doc;
|
||||||
let modified = false;
|
let modified = false;
|
||||||
for ( let entry of details.selectors ) {
|
for ( const entry of details.selectors ) {
|
||||||
if ( entry.type === 64 ) {
|
if ( entry.type === 64 ) {
|
||||||
if ( applyCSSSelector(details, entry.selector) ) {
|
if ( applyCSSSelector(details, entry.selector) ) {
|
||||||
modified = true;
|
modified = true;
|
||||||
|
@ -360,19 +377,6 @@
|
||||||
pselectors.clear();
|
pselectors.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.defineProperties(api, {
|
|
||||||
acceptedCount: {
|
|
||||||
get: function() {
|
|
||||||
return acceptedCount;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
discardedCount: {
|
|
||||||
get: function() {
|
|
||||||
return discardedCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return api;
|
return api;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
|
@ -668,10 +668,6 @@
|
||||||
if ( (compiled = compileProceduralSelector(raw)) ) {
|
if ( (compiled = compileProceduralSelector(raw)) ) {
|
||||||
return compiled;
|
return compiled;
|
||||||
}
|
}
|
||||||
|
|
||||||
µb.logger.writeOne({
|
|
||||||
error: 'Cosmetic filtering – invalid filter: ' + raw
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return entryPoint;
|
return entryPoint;
|
||||||
|
|
|
@ -2189,8 +2189,9 @@ FilterContainer.prototype.compile = function(raw, writer) {
|
||||||
|
|
||||||
// Ignore filters with unsupported options
|
// Ignore filters with unsupported options
|
||||||
if ( parsed.unsupported ) {
|
if ( parsed.unsupported ) {
|
||||||
|
const who = writer.properties.get('assetKey') || '?';
|
||||||
µb.logger.writeOne({
|
µb.logger.writeOne({
|
||||||
error: 'Network filtering – invalid filter: ' + raw
|
error: `Invalid network filter in ${who}: ${raw}`
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -407,34 +407,39 @@
|
||||||
µBlock.appendUserFilters = function(filters) {
|
µBlock.appendUserFilters = function(filters) {
|
||||||
if ( filters.length === 0 ) { return; }
|
if ( filters.length === 0 ) { return; }
|
||||||
|
|
||||||
var µb = this;
|
const onSaved = ( ) => {
|
||||||
|
const compiledFilters = this.compileFilters(
|
||||||
var onSaved = function() {
|
filters,
|
||||||
var compiledFilters = µb.compileFilters(filters),
|
{ assetKey: this.userFiltersPath }
|
||||||
snfe = µb.staticNetFilteringEngine,
|
);
|
||||||
cfe = µb.cosmeticFilteringEngine,
|
const snfe = this.staticNetFilteringEngine;
|
||||||
acceptedCount = snfe.acceptedCount + cfe.acceptedCount,
|
const cfe = this.cosmeticFilteringEngine;
|
||||||
discardedCount = snfe.discardedCount + cfe.discardedCount;
|
const acceptedCount = snfe.acceptedCount + cfe.acceptedCount;
|
||||||
µb.applyCompiledFilters(compiledFilters, true);
|
const discardedCount = snfe.discardedCount + cfe.discardedCount;
|
||||||
var entry = µb.availableFilterLists[µb.userFiltersPath],
|
this.applyCompiledFilters(compiledFilters, true);
|
||||||
deltaEntryCount = snfe.acceptedCount + cfe.acceptedCount - acceptedCount,
|
const entry = this.availableFilterLists[this.userFiltersPath];
|
||||||
deltaEntryUsedCount = deltaEntryCount - (snfe.discardedCount + cfe.discardedCount - discardedCount);
|
const deltaEntryCount =
|
||||||
|
snfe.acceptedCount +
|
||||||
|
cfe.acceptedCount - acceptedCount;
|
||||||
|
const deltaEntryUsedCount =
|
||||||
|
deltaEntryCount -
|
||||||
|
(snfe.discardedCount + cfe.discardedCount - discardedCount);
|
||||||
entry.entryCount += deltaEntryCount;
|
entry.entryCount += deltaEntryCount;
|
||||||
entry.entryUsedCount += deltaEntryUsedCount;
|
entry.entryUsedCount += deltaEntryUsedCount;
|
||||||
vAPI.storage.set({ 'availableFilterLists': µb.availableFilterLists });
|
vAPI.storage.set({ 'availableFilterLists': this.availableFilterLists });
|
||||||
µb.staticNetFilteringEngine.freeze();
|
this.staticNetFilteringEngine.freeze();
|
||||||
µb.redirectEngine.freeze();
|
this.redirectEngine.freeze();
|
||||||
µb.staticExtFilteringEngine.freeze();
|
this.staticExtFilteringEngine.freeze();
|
||||||
µb.selfieManager.destroy();
|
this.selfieManager.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
var onLoaded = function(details) {
|
const onLoaded = details => {
|
||||||
if ( details.error ) { return; }
|
if ( details.error ) { return; }
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/976
|
// https://github.com/chrisaljoudi/uBlock/issues/976
|
||||||
// If we reached this point, the filter quite probably needs to be
|
// If we reached this point, the filter quite probably needs to be
|
||||||
// added for sure: do not try to be too smart, trying to avoid
|
// added for sure: do not try to be too smart, trying to avoid
|
||||||
// duplicates at this point may lead to more issues.
|
// duplicates at this point may lead to more issues.
|
||||||
µb.saveUserFilters(details.content.trim() + '\n\n' + filters.trim(), onSaved);
|
this.saveUserFilters(details.content.trim() + '\n\n' + filters.trim(), onSaved);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.loadUserFilters(onLoaded);
|
this.loadUserFilters(onLoaded);
|
||||||
|
@ -704,7 +709,10 @@
|
||||||
|
|
||||||
var onCompiledListLoaded2 = function(details) {
|
var onCompiledListLoaded2 = function(details) {
|
||||||
if ( details.content === '' ) {
|
if ( details.content === '' ) {
|
||||||
details.content = µb.compileFilters(rawContent);
|
details.content = µb.compileFilters(
|
||||||
|
rawContent,
|
||||||
|
{ assetKey: assetKey }
|
||||||
|
);
|
||||||
µb.assets.put(compiledPath, details.content);
|
µb.assets.put(compiledPath, details.content);
|
||||||
}
|
}
|
||||||
rawContent = undefined;
|
rawContent = undefined;
|
||||||
|
@ -786,19 +794,27 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
µBlock.compileFilters = function(rawText) {
|
µBlock.compileFilters = function(rawText, details) {
|
||||||
let writer = new this.CompiledLineIO.Writer();
|
let writer = new this.CompiledLineIO.Writer();
|
||||||
|
|
||||||
|
// Populate the writer with information potentially useful to the
|
||||||
|
// client compilers.
|
||||||
|
if ( details ) {
|
||||||
|
if ( details.assetKey ) {
|
||||||
|
writer.properties.set('assetKey', details.assetKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Useful references:
|
// Useful references:
|
||||||
// https://adblockplus.org/en/filter-cheatsheet
|
// https://adblockplus.org/en/filter-cheatsheet
|
||||||
// https://adblockplus.org/en/filters
|
// https://adblockplus.org/en/filters
|
||||||
let staticNetFilteringEngine = this.staticNetFilteringEngine,
|
const staticNetFilteringEngine = this.staticNetFilteringEngine;
|
||||||
staticExtFilteringEngine = this.staticExtFilteringEngine,
|
const staticExtFilteringEngine = this.staticExtFilteringEngine;
|
||||||
reIsWhitespaceChar = /\s/,
|
const reIsWhitespaceChar = /\s/;
|
||||||
reMaybeLocalIp = /^[\d:f]/,
|
const reMaybeLocalIp = /^[\d:f]/;
|
||||||
reIsLocalhostRedirect = /\s+(?:0\.0\.0\.0|broadcasthost|localhost|local|ip6-\w+)\b/,
|
const reIsLocalhostRedirect = /\s+(?:0\.0\.0\.0|broadcasthost|localhost|local|ip6-\w+)\b/;
|
||||||
reLocalIp = /^(?:0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)/,
|
const reLocalIp = /^(?:0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)/;
|
||||||
lineIter = new this.LineIterator(this.processDirectives(rawText));
|
const lineIter = new this.LineIterator(this.processDirectives(rawText));
|
||||||
|
|
||||||
while ( lineIter.eot() === false ) {
|
while ( lineIter.eot() === false ) {
|
||||||
// rhill 2014-04-18: The trim is important here, as without it there
|
// rhill 2014-04-18: The trim is important here, as without it there
|
||||||
|
@ -808,7 +824,7 @@
|
||||||
if ( line.length === 0 ) { continue; }
|
if ( line.length === 0 ) { continue; }
|
||||||
|
|
||||||
// Strip comments
|
// Strip comments
|
||||||
let c = line.charAt(0);
|
const c = line.charAt(0);
|
||||||
if ( c === '!' || c === '[' ) { continue; }
|
if ( c === '!' || c === '[' ) { continue; }
|
||||||
|
|
||||||
// Parse or skip cosmetic filters
|
// Parse or skip cosmetic filters
|
||||||
|
@ -827,7 +843,7 @@
|
||||||
// Don't remove:
|
// Don't remove:
|
||||||
// ...#blah blah blah
|
// ...#blah blah blah
|
||||||
// because some ABP filters uses the `#` character (URL fragment)
|
// because some ABP filters uses the `#` character (URL fragment)
|
||||||
let pos = line.indexOf('#');
|
const pos = line.indexOf('#');
|
||||||
if ( pos !== -1 && reIsWhitespaceChar.test(line.charAt(pos - 1)) ) {
|
if ( pos !== -1 && reIsWhitespaceChar.test(line.charAt(pos - 1)) ) {
|
||||||
line = line.slice(0, pos).trim();
|
line = line.slice(0, pos).trim();
|
||||||
}
|
}
|
||||||
|
@ -1259,7 +1275,10 @@
|
||||||
);
|
);
|
||||||
this.assets.put(
|
this.assets.put(
|
||||||
'compiled/' + details.assetKey,
|
'compiled/' + details.assetKey,
|
||||||
this.compileFilters(details.content)
|
this.compileFilters(
|
||||||
|
details.content,
|
||||||
|
{ assetKey: details.assetKey }
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -234,8 +234,9 @@
|
||||||
this.io = µBlock.CompiledLineIO;
|
this.io = µBlock.CompiledLineIO;
|
||||||
this.blockId = undefined;
|
this.blockId = undefined;
|
||||||
this.block = undefined;
|
this.block = undefined;
|
||||||
this.blocks = new Map();
|
|
||||||
this.stringifier = this.io.serialize;
|
this.stringifier = this.io.serialize;
|
||||||
|
this.blocks = new Map();
|
||||||
|
this.properties = new Map();
|
||||||
},
|
},
|
||||||
|
|
||||||
Reader: function(raw, blockId) {
|
Reader: function(raw, blockId) {
|
||||||
|
@ -246,6 +247,7 @@
|
||||||
this.line = '';
|
this.line = '';
|
||||||
this.parser = this.io.unserialize;
|
this.parser = this.io.unserialize;
|
||||||
this.blocks = new Map();
|
this.blocks = new Map();
|
||||||
|
this.properties = new Map();
|
||||||
let reBlockStart = new RegExp(
|
let reBlockStart = new RegExp(
|
||||||
'^' + this.io.blockStartPrefix + '(\\d+)\\n',
|
'^' + this.io.blockStartPrefix + '(\\d+)\\n',
|
||||||
'gm'
|
'gm'
|
||||||
|
|
Loading…
Reference in New Issue