mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-10 09:07:54 +01:00
Add new procedural cosmetic filter operator: :matches-media()
Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/2185 The argument must be a valid media query as documented on MDN, i.e. what appears between the `@media` at-rule and the first opening curly bracket (including the parentheses when required): - https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries Best practice: Use `:matches-media()` after plain CSS selectors, if any. Good: example.com###target-1 > .target-2:matches-media((min-width: 800px)) Bad (though this will still work): example.com##:matches-media((min-width: 800px)) #target-1 > .target-2 The reason for this is to keep the door open for a future optimisation where uBO could convert `:matches-media()`-based filters into CSS media rules injected declaratively in a user stylesheet.
This commit is contained in:
parent
deb5fea0ba
commit
40c315a107
2 changed files with 42 additions and 6 deletions
|
@ -104,15 +104,22 @@ class PSelectorMatchesCSSBeforeTask extends PSelectorMatchesCSSTask {
|
|||
}
|
||||
PSelectorMatchesCSSBeforeTask.prototype.pseudo = ':before';
|
||||
|
||||
class PSelectorMinTextLengthTask extends PSelectorTask {
|
||||
class PSelectorMatchesMediaTask extends PSelectorTask {
|
||||
constructor(task) {
|
||||
super();
|
||||
this.min = task[1];
|
||||
this.mql = window.matchMedia(task[1]);
|
||||
if ( this.mql.media === 'not all' ) { return; }
|
||||
this.mql.addEventListener('change', ( ) => {
|
||||
if ( typeof vAPI !== 'object' ) { return; }
|
||||
if ( vAPI === null ) { return; }
|
||||
const filterer = vAPI.domFilterer && vAPI.domFilterer.proceduralFilterer;
|
||||
if ( filterer instanceof Object === false ) { return; }
|
||||
filterer.onDOMChanged([ null ]);
|
||||
});
|
||||
}
|
||||
transpose(node, output) {
|
||||
if ( node.textContent.length >= this.min ) {
|
||||
output.push(node);
|
||||
}
|
||||
if ( this.mql.matches === false ) { return; }
|
||||
output.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,6 +139,18 @@ class PSelectorMatchesPathTask extends PSelectorTask {
|
|||
}
|
||||
}
|
||||
|
||||
class PSelectorMinTextLengthTask extends PSelectorTask {
|
||||
constructor(task) {
|
||||
super();
|
||||
this.min = task[1];
|
||||
}
|
||||
transpose(node, output) {
|
||||
if ( node.textContent.length >= this.min ) {
|
||||
output.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PSelectorOthersTask extends PSelectorTask {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -322,6 +341,7 @@ class PSelector {
|
|||
[ ':matches-css', PSelectorMatchesCSSTask ],
|
||||
[ ':matches-css-after', PSelectorMatchesCSSAfterTask ],
|
||||
[ ':matches-css-before', PSelectorMatchesCSSBeforeTask ],
|
||||
[ ':matches-media', PSelectorMatchesMediaTask ],
|
||||
[ ':matches-path', PSelectorMatchesPathTask ],
|
||||
[ ':min-text-length', PSelectorMinTextLengthTask ],
|
||||
[ ':not', PSelectorIfNotTask ],
|
||||
|
|
|
@ -1581,6 +1581,18 @@ Parser.prototype.SelectorCompiler = class {
|
|||
return n;
|
||||
}
|
||||
|
||||
compileMediaQuery(s) {
|
||||
if ( typeof self !== 'object' ) { return; }
|
||||
if ( self === null ) { return; }
|
||||
if ( typeof self.matchMedia !== 'function' ) { return; }
|
||||
try {
|
||||
const mql = self.matchMedia(s);
|
||||
if ( mql instanceof self.MediaQueryList === false ) { return; }
|
||||
if ( mql.media !== 'not all' ) { return s; }
|
||||
} catch(ex) {
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/341#issuecomment-447603588
|
||||
// Reject instances of :not() filters for which the argument is
|
||||
// a valid CSS selector, otherwise we would be adversely changing the
|
||||
|
@ -1702,6 +1714,7 @@ Parser.prototype.SelectorCompiler = class {
|
|||
case ':spath':
|
||||
raw.push(task[1]);
|
||||
break;
|
||||
case ':matches-media':
|
||||
case ':min-text-length':
|
||||
case ':others':
|
||||
case ':upward':
|
||||
|
@ -1878,6 +1891,8 @@ Parser.prototype.SelectorCompiler = class {
|
|||
return this.compileCSSDeclaration(args);
|
||||
case ':matches-css-before':
|
||||
return this.compileCSSDeclaration(args);
|
||||
case ':matches-media':
|
||||
return this.compileMediaQuery(args);
|
||||
case ':matches-path':
|
||||
return this.compileText(args);
|
||||
case ':min-text-length':
|
||||
|
@ -1918,7 +1933,8 @@ Parser.prototype.proceduralOperatorTokens = new Map([
|
|||
[ 'matches-css', 0b11 ],
|
||||
[ 'matches-css-after', 0b11 ],
|
||||
[ 'matches-css-before', 0b11 ],
|
||||
[ 'matches-path', 0b01 ],
|
||||
[ 'matches-media', 0b11 ],
|
||||
[ 'matches-path', 0b11 ],
|
||||
[ 'min-text-length', 0b01 ],
|
||||
[ 'not', 0b01 ],
|
||||
[ 'nth-ancestor', 0b00 ],
|
||||
|
|
Loading…
Reference in a new issue