mirror of https://github.com/gorhill/uBlock.git
code review of procedural cosmetic filters + better validate :style option (#2278)
This commit is contained in:
parent
7d08b9da39
commit
c6dbdbd23b
|
@ -108,8 +108,8 @@ return {
|
||||||
|
|
||||||
// read-only
|
// read-only
|
||||||
systemSettings: {
|
systemSettings: {
|
||||||
compiledMagic: 'xhjvmgkamffc',
|
compiledMagic: 'zelhzxrhkfjr',
|
||||||
selfieMagic: 'xhjvmgkamffc'
|
selfieMagic: 'zelhzxrhkfjr'
|
||||||
},
|
},
|
||||||
|
|
||||||
restoreBackupSettings: {
|
restoreBackupSettings: {
|
||||||
|
|
|
@ -417,8 +417,9 @@ var PSelector = function(o) {
|
||||||
this.raw = o.raw;
|
this.raw = o.raw;
|
||||||
this.selector = o.selector;
|
this.selector = o.selector;
|
||||||
this.tasks = [];
|
this.tasks = [];
|
||||||
var tasks = o.tasks, task, ctor;
|
var tasks = o.tasks;
|
||||||
for ( var i = 0; i < tasks.length; i++ ) {
|
if ( !tasks ) { return; }
|
||||||
|
for ( var i = 0, task, ctor; i < tasks.length; i++ ) {
|
||||||
task = tasks[i];
|
task = tasks[i];
|
||||||
ctor = this.operatorToTaskMap.get(task[0]);
|
ctor = this.operatorToTaskMap.get(task[0]);
|
||||||
this.tasks.push(new ctor(task));
|
this.tasks.push(new ctor(task));
|
||||||
|
@ -455,26 +456,6 @@ PSelector.prototype.test = function(input) {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
var PSelectors = function() {
|
|
||||||
this.entries = [];
|
|
||||||
};
|
|
||||||
PSelectors.prototype.add = function(o) {
|
|
||||||
this.entries.push(new PSelector(o));
|
|
||||||
};
|
|
||||||
PSelectors.prototype.forEachNode = function(callback) {
|
|
||||||
var pfilters = this.entries,
|
|
||||||
i = pfilters.length,
|
|
||||||
pfilter, nodes, j;
|
|
||||||
while ( i-- ) {
|
|
||||||
pfilter = pfilters[i];
|
|
||||||
nodes = pfilter.exec();
|
|
||||||
j = nodes.length;
|
|
||||||
while ( j-- ) {
|
|
||||||
callback(nodes[j], pfilter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var domFilterer = {
|
var domFilterer = {
|
||||||
|
@ -498,8 +479,6 @@ var domFilterer = {
|
||||||
this.entries.push(selector);
|
this.entries.push(selector);
|
||||||
this.selector = undefined;
|
this.selector = undefined;
|
||||||
},
|
},
|
||||||
forEachNodeOfSelector: function(/*callback, root, extra*/) {
|
|
||||||
},
|
|
||||||
forEachNode: function(callback, root, extra) {
|
forEachNode: function(callback, root, extra) {
|
||||||
if ( this.selector === undefined ) {
|
if ( this.selector === undefined ) {
|
||||||
this.selector = this.entries.join(extra + ',') + extra;
|
this.selector = this.entries.join(extra + ',') + extra;
|
||||||
|
@ -532,7 +511,29 @@ var domFilterer = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
proceduralSelectors: new PSelectors(), // Hiding filters: procedural
|
styleSelectors: { // Style filters
|
||||||
|
entries: [],
|
||||||
|
add: function(o) {
|
||||||
|
this.entries.push(o);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
proceduralSelectors: { // Hiding filters: procedural
|
||||||
|
entries: [],
|
||||||
|
add: function(o) {
|
||||||
|
this.entries.push(new PSelector(o));
|
||||||
|
},
|
||||||
|
forEachNode: function(callback) {
|
||||||
|
var pfilters = this.entries, i = pfilters.length, pfilter, nodes, j;
|
||||||
|
while ( i-- ) {
|
||||||
|
pfilter = pfilters[i];
|
||||||
|
nodes = pfilter.exec();
|
||||||
|
j = nodes.length;
|
||||||
|
while ( j-- ) {
|
||||||
|
callback(nodes[j], pfilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
addExceptions: function(aa) {
|
addExceptions: function(aa) {
|
||||||
for ( var i = 0, n = aa.length; i < n; i++ ) {
|
for ( var i = 0, n = aa.length; i < n; i++ ) {
|
||||||
|
@ -556,11 +557,13 @@ var domFilterer = {
|
||||||
}
|
}
|
||||||
var o = JSON.parse(selector);
|
var o = JSON.parse(selector);
|
||||||
if ( o.style ) {
|
if ( o.style ) {
|
||||||
this.newStyleRuleBuffer.push(o.parts.join(' '));
|
this.newStyleRuleBuffer.push(o.style.join(' '));
|
||||||
|
this.styleSelectors.add(o);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( o.procedural ) {
|
if ( o.tasks ) {
|
||||||
this.proceduralSelectors.add(o);
|
this.proceduralSelectors.add(o);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -242,7 +242,7 @@ FilterBucket.fromSelfie = function() {
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var FilterParser = function() {
|
var FilterParser = function() {
|
||||||
this.prefix = this.suffix = this.style = '';
|
this.prefix = this.suffix = '';
|
||||||
this.unhide = 0;
|
this.unhide = 0;
|
||||||
this.hostnames = [];
|
this.hostnames = [];
|
||||||
this.invalid = false;
|
this.invalid = false;
|
||||||
|
@ -254,7 +254,7 @@ var FilterParser = function() {
|
||||||
|
|
||||||
FilterParser.prototype.reset = function() {
|
FilterParser.prototype.reset = function() {
|
||||||
this.raw = '';
|
this.raw = '';
|
||||||
this.prefix = this.suffix = this.style = '';
|
this.prefix = this.suffix = '';
|
||||||
this.unhide = 0;
|
this.unhide = 0;
|
||||||
this.hostnames.length = 0;
|
this.hostnames.length = 0;
|
||||||
this.invalid = false;
|
this.invalid = false;
|
||||||
|
@ -628,7 +628,6 @@ var FilterContainer = function() {
|
||||||
this.netSelectorCacheCountMax = netSelectorCacheHighWaterMark;
|
this.netSelectorCacheCountMax = netSelectorCacheHighWaterMark;
|
||||||
this.selectorCacheTimer = null;
|
this.selectorCacheTimer = null;
|
||||||
this.reHasUnicode = /[^\x00-\x7F]/;
|
this.reHasUnicode = /[^\x00-\x7F]/;
|
||||||
this.reClassOrIdSelector = /^[#.][\w-]+$/;
|
|
||||||
this.rePlainSelector = /^[#.][\w\\-]+/;
|
this.rePlainSelector = /^[#.][\w\\-]+/;
|
||||||
this.rePlainSelectorEscaped = /^[#.](?:\\[0-9A-Fa-f]+ |\\.|\w|-)+/;
|
this.rePlainSelectorEscaped = /^[#.](?:\\[0-9A-Fa-f]+ |\\.|\w|-)+/;
|
||||||
this.rePlainSelectorEx = /^[^#.\[(]+([#.][\w-]+)/;
|
this.rePlainSelectorEx = /^[^#.\[(]+([#.][\w-]+)/;
|
||||||
|
@ -735,7 +734,16 @@ FilterContainer.prototype.freeze = function() {
|
||||||
FilterContainer.prototype.compileSelector = (function() {
|
FilterContainer.prototype.compileSelector = (function() {
|
||||||
var reStyleSelector = /^(.+?):style\((.+?)\)$/,
|
var reStyleSelector = /^(.+?):style\((.+?)\)$/,
|
||||||
reStyleBad = /url\([^)]+\)/,
|
reStyleBad = /url\([^)]+\)/,
|
||||||
reScriptSelector = /^script:(contains|inject)\((.+)\)$/;
|
reScriptSelector = /^script:(contains|inject)\((.+)\)$/,
|
||||||
|
div = document.createElement('div');
|
||||||
|
|
||||||
|
var isValidStyleProperty = function(cssText) {
|
||||||
|
if ( reStyleBad.test(cssText) ) { return false; }
|
||||||
|
div.style.cssText = cssText;
|
||||||
|
if ( div.style.cssText === '' ) { return false; }
|
||||||
|
div.style.cssText = '';
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
return function(raw) {
|
return function(raw) {
|
||||||
if ( isValidCSSSelector(raw) && raw.indexOf('[-abp-properties=') === -1 ) {
|
if ( isValidCSSSelector(raw) && raw.indexOf('[-abp-properties=') === -1 ) {
|
||||||
|
@ -747,11 +755,10 @@ FilterContainer.prototype.compileSelector = (function() {
|
||||||
|
|
||||||
// `:style` selector?
|
// `:style` selector?
|
||||||
if ( (matches = reStyleSelector.exec(raw)) !== null ) {
|
if ( (matches = reStyleSelector.exec(raw)) !== null ) {
|
||||||
if ( isValidCSSSelector(matches[1]) && reStyleBad.test(matches[2]) === false ) {
|
if ( isValidCSSSelector(matches[1]) && isValidStyleProperty(matches[2]) ) {
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
style: true,
|
|
||||||
raw: raw,
|
raw: raw,
|
||||||
parts: [ matches[1], '{' + matches[2] + '}' ]
|
style: [ matches[1], '{' + matches[2] + '}' ]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -784,7 +791,7 @@ FilterContainer.prototype.compileSelector = (function() {
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
FilterContainer.prototype.compileProceduralSelector = (function() {
|
FilterContainer.prototype.compileProceduralSelector = (function() {
|
||||||
var reParserEx = /(:(?:has|has-text|if|if-not|matches-css|matches-css-after|matches-css-before|xpath))\(.+\)$/,
|
var reOperatorParser = /(:(?:has|has-text|if|if-not|matches-css|matches-css-after|matches-css-before|xpath))\(.+\)$/,
|
||||||
reFirstParentheses = /^\(*/,
|
reFirstParentheses = /^\(*/,
|
||||||
reLastParentheses = /\)*$/,
|
reLastParentheses = /\)*$/,
|
||||||
reEscapeRegex = /[.*+?^${}()|[\]\\]/g;
|
reEscapeRegex = /[.*+?^${}()|[\]\\]/g;
|
||||||
|
@ -849,11 +856,9 @@ FilterContainer.prototype.compileProceduralSelector = (function() {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
var compile = function(raw) {
|
var compile = function(raw) {
|
||||||
var matches = reParserEx.exec(raw);
|
var matches = reOperatorParser.exec(raw);
|
||||||
if ( matches === null ) {
|
if ( matches === null ) {
|
||||||
if ( isValidCSSSelector(raw) ) {
|
if ( isValidCSSSelector(raw) ) { return { selector: raw }; }
|
||||||
return { selector: raw, tasks: [] };
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var tasks = [],
|
var tasks = [],
|
||||||
|
@ -864,7 +869,7 @@ FilterContainer.prototype.compileProceduralSelector = (function() {
|
||||||
depth = 0, opening, closing;
|
depth = 0, opening, closing;
|
||||||
if ( firstOperand !== '' && isValidCSSSelector(firstOperand) === false ) { return; }
|
if ( firstOperand !== '' && isValidCSSSelector(firstOperand) === false ) { return; }
|
||||||
for (;;) {
|
for (;;) {
|
||||||
matches = reParserEx.exec(selector);
|
matches = reOperatorParser.exec(selector);
|
||||||
if ( matches !== null ) {
|
if ( matches !== null ) {
|
||||||
nextOperand = selector.slice(0, matches.index);
|
nextOperand = selector.slice(0, matches.index);
|
||||||
nextOperator = matches[1];
|
nextOperator = matches[1];
|
||||||
|
@ -903,7 +908,6 @@ FilterContainer.prototype.compileProceduralSelector = (function() {
|
||||||
lastProceduralSelector = raw;
|
lastProceduralSelector = raw;
|
||||||
var compiled = compile(raw);
|
var compiled = compile(raw);
|
||||||
if ( compiled !== undefined ) {
|
if ( compiled !== undefined ) {
|
||||||
compiled.procedural = true;
|
|
||||||
compiled.raw = raw;
|
compiled.raw = raw;
|
||||||
compiled = JSON.stringify(compiled);
|
compiled = JSON.stringify(compiled);
|
||||||
}
|
}
|
||||||
|
@ -965,15 +969,6 @@ FilterContainer.prototype.compile = function(s, out) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For hostname- or entity-based filters, class- or id-based selectors are
|
|
||||||
// still the most common, and can easily be tested using a plain regex.
|
|
||||||
if (
|
|
||||||
this.reClassOrIdSelector.test(parsed.suffix) === false &&
|
|
||||||
this.compileSelector(parsed.suffix) === undefined
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/151
|
// https://github.com/chrisaljoudi/uBlock/issues/151
|
||||||
// Negated hostname means the filter applies to all non-negated hostnames
|
// Negated hostname means the filter applies to all non-negated hostnames
|
||||||
// of same filter OR globally if there is no non-negated hostnames.
|
// of same filter OR globally if there is no non-negated hostnames.
|
||||||
|
|
|
@ -139,9 +139,9 @@ var fromCosmeticFilter = function(details) {
|
||||||
// compiled form of a filter.
|
// compiled form of a filter.
|
||||||
var filterEx = '(' +
|
var filterEx = '(' +
|
||||||
reEscape(filter) +
|
reEscape(filter) +
|
||||||
'|[^\\v]+' +
|
'|\{[^\\v]*' +
|
||||||
reEscape(JSON.stringify({ raw: filter }).slice(1,-1)) +
|
reEscape(JSON.stringify({ raw: filter }).slice(1,-1)) +
|
||||||
'[^\\v]+)';
|
'[^\\v]*\})';
|
||||||
|
|
||||||
// Second step: find hostname-based versions.
|
// Second step: find hostname-based versions.
|
||||||
// Reference: FilterContainer.compileHostnameSelector().
|
// Reference: FilterContainer.compileHostnameSelector().
|
||||||
|
|
|
@ -51,6 +51,17 @@ vAPI.domFilterer.simpleHideSelectors.entries.forEach(evaluateSelector);
|
||||||
// Complex CSS selector-based cosmetic filters.
|
// Complex CSS selector-based cosmetic filters.
|
||||||
vAPI.domFilterer.complexHideSelectors.entries.forEach(evaluateSelector);
|
vAPI.domFilterer.complexHideSelectors.entries.forEach(evaluateSelector);
|
||||||
|
|
||||||
|
// Style cosmetic filters.
|
||||||
|
vAPI.domFilterer.styleSelectors.entries.forEach(function(filter) {
|
||||||
|
if (
|
||||||
|
loggedSelectors.hasOwnProperty(filter.raw) === false &&
|
||||||
|
document.querySelector(filter.style[0]) !== null
|
||||||
|
) {
|
||||||
|
loggedSelectors[filter.raw] = true;
|
||||||
|
matchedSelectors.push(filter.raw);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Procedural cosmetic filters.
|
// Procedural cosmetic filters.
|
||||||
vAPI.domFilterer.proceduralSelectors.entries.forEach(function(pfilter) {
|
vAPI.domFilterer.proceduralSelectors.entries.forEach(function(pfilter) {
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -770,9 +770,9 @@ var filterToDOMInterface = (function() {
|
||||||
}
|
}
|
||||||
var elems;
|
var elems;
|
||||||
if ( o.style ) {
|
if ( o.style ) {
|
||||||
elems = document.querySelectorAll(o.parts[0]);
|
elems = document.querySelectorAll(o.style[0]);
|
||||||
lastAction = o.parts.join(' ');
|
lastAction = o.style.join(' ');
|
||||||
} else if ( o.procedural ) {
|
} else if ( o.tasks ) {
|
||||||
elems = vAPI.domFilterer.createProceduralFilter(o).exec();
|
elems = vAPI.domFilterer.createProceduralFilter(o).exec();
|
||||||
}
|
}
|
||||||
if ( !elems ) { return; }
|
if ( !elems ) { return; }
|
||||||
|
|
Loading…
Reference in New Issue