From 7bc0b5d2bda39ff0ddb1eb62bb748c239a02d994 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 18 Aug 2022 11:28:44 -0400 Subject: [PATCH] Extend matches-css() to support any pseudo-element 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: - https://github.com/AdguardTeam/ExtendedCss/issues/150 --- src/js/contentscript-extra.js | 13 +++++++++---- src/js/static-filtering-parser.js | 19 +++++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/js/contentscript-extra.js b/src/js/contentscript-extra.js index 658fd5c5a513c..6308ac55756ea 100644 --- a/src/js/contentscript-extra.js +++ b/src/js/contentscript-extra.js @@ -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]; @@ -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) { diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index beaf5862a2b81..325dbf127cb88 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -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(); @@ -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) { @@ -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': @@ -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':