Skip to content

Commit

Permalink
Extend matches-css() to support any pseudo-element
Browse files Browse the repository at this point in the history
This commit deprecates matches-css-before() and matches-css-after(): these
should no longer be used once 1.45.0 is published and widespread. The
deprecated syntax will eventually be removed in some future.

The syntax of procedural operator matches-css() has been extended to also
be able to target pesudo elements. Examples:

Same as before:

    example.com##p:matches-css(opacity: 0.5)

This is the new way to target an `::after` pseudo-element:

    example.com##p:matches-css(after, content: Ads)

This is the new way to target a `::before` pseudo-element:

    example.com##p:matches-css(before, content: Ads)

The new syntax also means any valid pseudo-element can now be used as
a target:

    example.com##p:matches-css(first-letter, opacity: 0.5)

If the first argument does not match the pattern "property name: value",
then it will be deemed a pseudo-element to target, and the second argument
will be the "property name: value".

Related issue:
- AdguardTeam/ExtendedCss#150
  • Loading branch information
gorhill committed Aug 18, 2022
1 parent bdc68f3 commit 7bc0b5d
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 8 deletions.
13 changes: 9 additions & 4 deletions src/js/contentscript-extra.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class PSelectorMatchesCSSTask extends PSelectorTask {
constructor(task) {
super();
this.name = task[1].name;
this.pseudo = task[1].pseudo ? `::${task[1].pseudo}` : null;
let arg0 = task[1].value, arg1;
if ( Array.isArray(arg0) ) {
arg1 = arg0[1]; arg0 = arg0[0];
Expand All @@ -94,15 +95,19 @@ class PSelectorMatchesCSSTask extends PSelectorTask {
}
}
}
PSelectorMatchesCSSTask.prototype.pseudo = null;

class PSelectorMatchesCSSAfterTask extends PSelectorMatchesCSSTask {
constructor(task) {
super(task);
this.pseudo = 'after';
}
}
PSelectorMatchesCSSAfterTask.prototype.pseudo = ':after';

class PSelectorMatchesCSSBeforeTask extends PSelectorMatchesCSSTask {
constructor(task) {
super(task);
this.pseudo = 'before';
}
}
PSelectorMatchesCSSBeforeTask.prototype.pseudo = ':before';

class PSelectorMatchesMediaTask extends PSelectorTask {
constructor(task) {
Expand Down
19 changes: 15 additions & 4 deletions src/js/static-filtering-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,13 @@ Parser.prototype.SelectorCompiler = class {
}

compileCSSDeclaration(s) {
let pseudo; {
const match = /^[a-z-]+,/.exec(s);
if ( match !== null ) {
pseudo = match[0].slice(0, -1);
s = s.slice(match[0].length).trim();
}
}
const pos = s.indexOf(':');
if ( pos === -1 ) { return; }
const name = s.slice(0, pos).trim();
Expand All @@ -1576,7 +1583,7 @@ Parser.prototype.SelectorCompiler = class {
regexDetails = '^' + value.replace(this.reEscapeRegex, '\\$&') + '$';
this.regexToRawValue.set(regexDetails, value);
}
return { name: name, value: regexDetails };
return { name, pseudo, value: regexDetails };
}

compileInteger(s, min = 0, max = 0x7FFFFFFF) {
Expand Down Expand Up @@ -1711,7 +1718,11 @@ Parser.prototype.SelectorCompiler = class {
value = `/${task[1].value}/`;
}
}
raw.push(`${task[0]}(${task[1].name}: ${value})`);
if ( task[1].pseudo ) {
raw.push(`:matches-css(${task[1].pseudo}, ${task[1].name}: ${value})`);
} else {
raw.push(`:matches-css(${task[1].name}: ${value})`);
}
break;
case ':not':
case ':if-not':
Expand Down Expand Up @@ -1907,9 +1918,9 @@ Parser.prototype.SelectorCompiler = class {
case ':matches-css':
return this.compileCSSDeclaration(args);
case ':matches-css-after':
return this.compileCSSDeclaration(args);
return this.compileCSSDeclaration(`after, ${args}`);
case ':matches-css-before':
return this.compileCSSDeclaration(args);
return this.compileCSSDeclaration(`before, ${args}`);
case ':matches-media':
return this.compileMediaQuery(args);
case ':matches-path':
Expand Down

0 comments on commit 7bc0b5d

Please sign in to comment.