mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-02 00:42:45 +01:00
#1004: reduce overhead of CSS selector validation
This commit is contained in:
parent
58e8b5bf5f
commit
c81a9925b2
@ -239,15 +239,6 @@ var FilterParser = function() {
|
|||||||
this.invalid = false;
|
this.invalid = false;
|
||||||
this.cosmetic = true;
|
this.cosmetic = true;
|
||||||
this.reParser = /^\s*([^#]*)(##|#@#)(.+)\s*$/;
|
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) {
|
FilterParser.prototype.parse = function(s) {
|
||||||
// important!
|
// important!
|
||||||
this.reset();
|
this.reset();
|
||||||
@ -306,13 +285,6 @@ FilterParser.prototype.parse = function(s) {
|
|||||||
this.suffix = this.suffix.slice(1);
|
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;
|
this.unhide = matches[2].charAt(1) === '@' ? 1 : 0;
|
||||||
if ( this.prefix !== '' ) {
|
if ( this.prefix !== '' ) {
|
||||||
this.hostnames = this.prefix.split(/\s*,\s*/);
|
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) {
|
FilterContainer.prototype.compile = function(s, out) {
|
||||||
var parsed = this.parser.parse(s);
|
var parsed = this.parser.parse(s);
|
||||||
if ( parsed.cosmetic === false ) {
|
if ( parsed.cosmetic === false ) {
|
||||||
@ -603,6 +601,15 @@ 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.isValidSelector(parsed.suffix) === false
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/151
|
// https://github.com/gorhill/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.
|
||||||
@ -635,11 +642,9 @@ FilterContainer.prototype.compileGenericSelector = function(parsed, out) {
|
|||||||
// All generic exception filters are put in the same bucket: they are
|
// All generic exception filters are put in the same bucket: they are
|
||||||
// expected to be very rare.
|
// expected to be very rare.
|
||||||
if ( parsed.unhide ) {
|
if ( parsed.unhide ) {
|
||||||
out.push(
|
if ( this.isValidSelector(selector) ) {
|
||||||
'c\v' +
|
out.push('c\vg1\v' + selector);
|
||||||
'g1\v' +
|
}
|
||||||
selector
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,45 +656,56 @@ FilterContainer.prototype.compileGenericSelector = function(parsed, out) {
|
|||||||
if ( matches === null ) {
|
if ( matches === null ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
out.push(
|
// Single-CSS rule: no need to test for whether the selector
|
||||||
'c\v' +
|
// is valid, the regex took care of this. Most generic selector falls
|
||||||
(matches[1] === selector ? 'lg\v' : 'lg+\v') +
|
// into that category.
|
||||||
makeHash(0, matches[1], this.genericHashMask) + '\v' +
|
if ( matches[1] === selector ) {
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ["title"] and ["alt"] will go in high-low generic bin.
|
// ["title"] and ["alt"] will go in high-low generic bin.
|
||||||
if ( this.reHighLow.test(selector) ) {
|
if ( this.reHighLow.test(selector) ) {
|
||||||
out.push(
|
if ( this.isValidSelector(selector) ) {
|
||||||
'c\v' +
|
out.push('c\vhlg0\v' + selector);
|
||||||
'hlg0\v' +
|
}
|
||||||
selector
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// [href^="..."] will go in high-medium generic bin.
|
// [href^="..."] will go in high-medium generic bin.
|
||||||
matches = this.reHighMedium.exec(selector);
|
matches = this.reHighMedium.exec(selector);
|
||||||
if ( matches && matches.length === 2 ) {
|
if ( matches && matches.length === 2 ) {
|
||||||
out.push(
|
if ( this.isValidSelector(selector) ) {
|
||||||
'c\v' +
|
out.push(
|
||||||
'hmg0\v' +
|
'c\vhmg0\v' +
|
||||||
matches[1] + '\v' +
|
matches[1] + '\v' +
|
||||||
selector
|
selector
|
||||||
);
|
);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All else
|
// All else
|
||||||
out.push(
|
if ( this.isValidSelector(selector) ) {
|
||||||
'c\v' +
|
out.push('c\vhhg0\v' + selector);
|
||||||
'hhg0\v' +
|
}
|
||||||
selector
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FilterContainer.prototype.reClassOrIdSelector = /^([#.][\w-]+)$/;
|
||||||
FilterContainer.prototype.rePlainSelector = /^([#.][\w-]+)/;
|
FilterContainer.prototype.rePlainSelector = /^([#.][\w-]+)/;
|
||||||
FilterContainer.prototype.reHighLow = /^[a-z]*\[(?:alt|title)="[^"]+"\]$/;
|
FilterContainer.prototype.reHighLow = /^[a-z]*\[(?:alt|title)="[^"]+"\]$/;
|
||||||
FilterContainer.prototype.reHighMedium = /^\[href\^="https?:\/\/([^"]{8})[^"]*"\]$/;
|
FilterContainer.prototype.reHighMedium = /^\[href\^="https?:\/\/([^"]{8})[^"]*"\]$/;
|
||||||
|
Loading…
Reference in New Issue
Block a user