Fix use of sibling-related CSS syntax at prefix position

Related discussion:
- https://www.reddit.com/r/uBlockOrigin/comments/c6iem5/
This commit is contained in:
Raymond Hill 2019-06-29 14:07:54 -04:00
parent 3a8608b49a
commit c1bdc123f2
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
5 changed files with 43 additions and 31 deletions

View file

@ -163,8 +163,8 @@
</div>
<div id="a17" class="tile">
<div class="pass"><div class="fail"></div><a></a><b></b></div>
<code>#pcf #a17 .fail:has(~ b)</code>
<div class="pass"><div class="fail"></div><a><b></b></a></div>
<code>#pcf #a17 .fail:has(~ a:has(b))</code>
</div>
</div>

View file

@ -128,18 +128,18 @@
<code>^#phf #a11 .pass > a:has(b) + .fail:has(b)</code>
</div>
</div>
<div id="a12" class="tile">
<div class="pass"><div class="fail"></div><a></a><b></b></div>
<code>^#phf #a12 .fail:has(+ a)</code>
</div>
<div id="a13" class="tile">
<div class="pass"><div class="fail"></div><a></a><b></b></div>
<code>^#phf #a13 .fail:has(~ b)</code>
<div class="pass"><div class="fail"></div><a><b></b></a></div>
<code>^#phf #a13 .fail:has(~ a:has(b))</code>
</div>
</div>
<script>
const hostname = self.location.hostname;
const filters = [];

View file

@ -660,10 +660,8 @@ vAPI.DOMFilterer = (function() {
}
prime(input) {
const root = input || document;
if ( this.selector !== '' ) {
return root.querySelectorAll(this.selector);
}
return [ root ];
if ( this.selector === '' ) { return [ root ]; }
return root.querySelectorAll(this.selector);
}
exec(input) {
let nodes = this.prime(input);

View file

@ -169,10 +169,8 @@
}
prime(input) {
const root = input || docRegister;
if ( this.selector !== '' ) {
return root.querySelectorAll(this.selector);
}
return [ root ];
if ( this.selector === '' ) { return [ root ]; }
return root.querySelectorAll(this.selector);
}
exec(input) {
if ( this.invalid ) { return []; }

View file

@ -68,9 +68,9 @@
parsed.suffix = '';
};
const isValidCSSSelector = (function() {
var div = document.createElement('div'),
matchesFn;
const isValidCSSSelector = (( ) => {
const div = document.createElement('div');
let matchesFn;
// Keep in mind:
// https://github.com/gorhill/uBlock/issues/693
// https://github.com/gorhill/uBlock/issues/1955
@ -95,11 +95,11 @@
}
// Quick regex-based validation -- most cosmetic filters are of the
// simple form and in such case a regex is much faster.
var reSimple = /^[#.][\w-]+$/;
return function(s) {
const reSimple = /^[#.][\w-]+$/;
return s => {
if ( reSimple.test(s) ) { return true; }
try {
matchesFn(s + ', ' + s + ':not(#foo)');
matchesFn(`${s}, ${s}:not(#foo)`);
} catch (ex) {
return false;
}
@ -152,7 +152,7 @@
return hostnames;
};
const compileProceduralSelector = (function() {
const compileProceduralSelector = (( ) => {
const reProceduralOperator = new RegExp([
'^(?:',
[
@ -178,8 +178,9 @@
const reEatBackslashes = /\\([()])/g;
const reEscapeRegex = /[.*+?^${}()|[\]\\]/g;
const reNeedScope = /^\s*[+>~]/;
const reIsDanglingSelector = /(?:[+>~]\s*|\s+)$/;
const reNeedScope = /^\s*>/;
const reIsDanglingSelector = /[+>~\s]\s*$/;
const reIsSiblingSelector = /^\s*[+~]/;
const regexToRawValue = new Map();
let lastProceduralSelector = '',
@ -226,9 +227,9 @@
const compileConditionalSelector = function(s) {
// https://github.com/AdguardTeam/ExtendedCss/issues/31#issuecomment-302391277
// Prepend `:scope ` if needed.
// Prepend `:scope ` if needed.
if ( reNeedScope.test(s) ) {
s = ':scope ' + s;
s = `:scope ${s}`;
}
return compile(s);
};
@ -367,7 +368,7 @@
return raw.join('');
};
const compile = function(raw) {
const compile = function(raw, root = false) {
if ( raw === '' ) { return; }
let prefix = '',
tasks = [];
@ -436,16 +437,31 @@
// At least one task found: nothing should be left to parse.
if ( tasks.length === 0 ) {
prefix = raw;
tasks = undefined;
} else if ( opPrefixBeg < n ) {
const spath = compileSpathExpression(raw.slice(opPrefixBeg));
if ( spath === undefined ) { return; }
tasks.push([ ':spath', spath ]);
}
// https://github.com/NanoAdblocker/NanoCore/issues/1#issuecomment-354394894
// https://www.reddit.com/r/uBlockOrigin/comments/c6iem5/
// Convert sibling-selector prefix into :spath operator, but
// only if context is not the root.
if ( prefix !== '' ) {
if ( reIsDanglingSelector.test(prefix) ) { prefix += '*'; }
if ( isValidCSSSelector(prefix) === false ) { return; }
if ( isValidCSSSelector(prefix) === false ) {
if (
root ||
reIsSiblingSelector.test(prefix) === false ||
compileSpathExpression(prefix) === undefined
) {
return;
}
tasks.unshift([ ':spath', prefix ]);
prefix = '';
}
}
if ( tasks.length === 0 ) {
tasks = undefined;
}
return { selector: prefix, tasks: tasks };
};
@ -455,7 +471,7 @@
return lastProceduralSelectorCompiled;
}
lastProceduralSelector = raw;
let compiled = compile(raw);
let compiled = compile(raw, true);
if ( compiled !== undefined ) {
compiled.raw = decompile(compiled);
compiled = JSON.stringify(compiled);
@ -717,8 +733,8 @@
}
// Procedural selector?
let compiled;
if ( (compiled = compileProceduralSelector(raw)) ) {
const compiled = compileProceduralSelector(raw);
if ( compiled !== undefined ) {
return compiled;
}
};