mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-10 17:17:57 +01:00
#1004: reduce overhead of CSS selector validation
This commit is contained in:
parent
58e8b5bf5f
commit
c81a9925b2
1 changed files with 71 additions and 55 deletions
|
@ -239,15 +239,6 @@ var FilterParser = function() {
|
|||
this.invalid = false;
|
||||
this.cosmetic = true;
|
||||
this.reParser = /^\s*([^#]*)(##|#@#)(.+)\s*$/;
|
||||
|
||||
// Not all browsers support `Element.matches`:
|
||||
// http://caniuse.com/#feat=matchesselector
|
||||
this.div = document.createElement('div');
|
||||
if ( typeof this.div.matches !== 'function' ) {
|
||||
this.div = {
|
||||
matches: function() { return true; }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -264,18 +255,6 @@ FilterParser.prototype.reset = function() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
FilterParser.prototype.isValidSelector = function(s) {
|
||||
try {
|
||||
this.div.matches(s);
|
||||
} catch (e) {
|
||||
console.error('uBlock> invalid cosmetic filter:', s);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
FilterParser.prototype.parse = function(s) {
|
||||
// important!
|
||||
this.reset();
|
||||
|
@ -306,13 +285,6 @@ FilterParser.prototype.parse = function(s) {
|
|||
this.suffix = this.suffix.slice(1);
|
||||
}
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/1004
|
||||
// Detect and report invalid CSS selectors.
|
||||
if ( this.isValidSelector(this.suffix) === false ) {
|
||||
this.invalid = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
this.unhide = matches[2].charAt(1) === '@' ? 1 : 0;
|
||||
if ( this.prefix !== '' ) {
|
||||
this.hostnames = this.prefix.split(/\s*,\s*/);
|
||||
|
@ -587,6 +559,32 @@ FilterContainer.prototype.reset = function() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/1004
|
||||
// Detect and report invalid CSS selectors.
|
||||
|
||||
FilterContainer.prototype.div = document.createElement('div');
|
||||
|
||||
// Not all browsers support `Element.matches`:
|
||||
// http://caniuse.com/#feat=matchesselector
|
||||
|
||||
if ( typeof FilterContainer.prototype.div.matches === 'function' ) {
|
||||
FilterContainer.prototype.isValidSelector = function(s) {
|
||||
try {
|
||||
this.div.matches(s);
|
||||
} catch (e) {
|
||||
console.error('uBlock> invalid cosmetic filter:', s);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
} else {
|
||||
FilterContainer.prototype.isValidSelector = function() {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
FilterContainer.prototype.compile = function(s, out) {
|
||||
var parsed = this.parser.parse(s);
|
||||
if ( parsed.cosmetic === false ) {
|
||||
|
@ -603,6 +601,15 @@ FilterContainer.prototype.compile = function(s, out) {
|
|||
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.isValidSelector(parsed.suffix) === false
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/151
|
||||
// Negated hostname means the filter applies to all non-negated hostnames
|
||||
// of same filter OR globally if there is no non-negated hostnames.
|
||||
|
@ -635,11 +642,9 @@ FilterContainer.prototype.compileGenericSelector = function(parsed, out) {
|
|||
// All generic exception filters are put in the same bucket: they are
|
||||
// expected to be very rare.
|
||||
if ( parsed.unhide ) {
|
||||
out.push(
|
||||
'c\v' +
|
||||
'g1\v' +
|
||||
selector
|
||||
);
|
||||
if ( this.isValidSelector(selector) ) {
|
||||
out.push('c\vg1\v' + selector);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -651,45 +656,56 @@ FilterContainer.prototype.compileGenericSelector = function(parsed, out) {
|
|||
if ( matches === null ) {
|
||||
return;
|
||||
}
|
||||
out.push(
|
||||
'c\v' +
|
||||
(matches[1] === selector ? 'lg\v' : 'lg+\v') +
|
||||
makeHash(0, matches[1], this.genericHashMask) + '\v' +
|
||||
selector
|
||||
);
|
||||
// Single-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 ( matches[1] === selector ) {
|
||||
out.push(
|
||||
'c\vlg\v' +
|
||||
makeHash(0, matches[1], this.genericHashMask) + '\v' +
|
||||
selector
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Many-CSS rules
|
||||
if ( this.isValidSelector(selector) ) {
|
||||
out.push(
|
||||
'c\vlg+\v' +
|
||||
makeHash(0, matches[1], this.genericHashMask) + '\v' +
|
||||
selector
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ["title"] and ["alt"] will go in high-low generic bin.
|
||||
if ( this.reHighLow.test(selector) ) {
|
||||
out.push(
|
||||
'c\v' +
|
||||
'hlg0\v' +
|
||||
selector
|
||||
);
|
||||
if ( this.isValidSelector(selector) ) {
|
||||
out.push('c\vhlg0\v' + selector);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// [href^="..."] will go in high-medium generic bin.
|
||||
matches = this.reHighMedium.exec(selector);
|
||||
if ( matches && matches.length === 2 ) {
|
||||
out.push(
|
||||
'c\v' +
|
||||
'hmg0\v' +
|
||||
matches[1] + '\v' +
|
||||
selector
|
||||
);
|
||||
if ( this.isValidSelector(selector) ) {
|
||||
out.push(
|
||||
'c\vhmg0\v' +
|
||||
matches[1] + '\v' +
|
||||
selector
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// All else
|
||||
out.push(
|
||||
'c\v' +
|
||||
'hhg0\v' +
|
||||
selector
|
||||
);
|
||||
if ( this.isValidSelector(selector) ) {
|
||||
out.push('c\vhhg0\v' + selector);
|
||||
}
|
||||
};
|
||||
|
||||
FilterContainer.prototype.reClassOrIdSelector = /^([#.][\w-]+)$/;
|
||||
FilterContainer.prototype.rePlainSelector = /^([#.][\w-]+)/;
|
||||
FilterContainer.prototype.reHighLow = /^[a-z]*\[(?:alt|title)="[^"]+"\]$/;
|
||||
FilterContainer.prototype.reHighMedium = /^\[href\^="https?:\/\/([^"]{8})[^"]*"\]$/;
|
||||
|
|
Loading…
Reference in a new issue