Skip to content

Commit

Permalink
Add procedural cosmetic operators remove() and upward()
Browse files Browse the repository at this point in the history
New procedural cosmetic operator: `:remove()`

Related issue:
- gorhill/uBlock#2252

The purpose is to outright remove elements from the
DOM tree. Since `:remove()` is an "action" operator,
it must only be used as a trailing operator (just
like the `:style()` operator).

AdGuard's cosmetic filter syntax `{ remove: true; }`
will be converted to uBO's `:remove()` operator
internally.

***

New procedural cosmetic operator: `:upward(...)`

The purpose is to lookup an ancestor element.

When used with an integer argument, it is synonym of
`:nth-ancestor()`, which will be deprecated and which
will no longer be supported once no longer used in
mainstream filter lists.

Filter lists maintainers must only use `:upward(int)`
instead of `:nth-ancestor(int)` once the new operator
become available in all stable releases of uBO.

`:upward()` can also accept a CSS selector as argument,
in which case the nearest ancestor which matches the
CSS selector will be selected.

Co-authored-by:  gorhill <585534+gorhill@users.noreply.github.com>
  • Loading branch information
hawkeye116477 and gorhill committed Jul 17, 2020
1 parent e30a8c1 commit 440917e
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 159 deletions.
13 changes: 2 additions & 11 deletions src/js/assets.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ api.fetchText = function(url, onLoad, onError) {
if ( ev.loaded === contentLoaded ) { return; }
contentLoaded = ev.loaded;
if ( timeoutTimer !== undefined ) {
clearTimeout(timeoutTimer);
clearTimeout(timeoutTimer);
}
timeoutTimer = vAPI.setTimeout(onTimeout, timeoutAfter);
};
Expand Down Expand Up @@ -351,16 +351,7 @@ api.fetchFilterList.legacy = {
'(raf-if,': '(requestAnimationFrame-if.js,',
'(norafif,': '(no-requestAnimationFrame-if.js,',
'(noeval)': '(noeval.js)',
'(nowebrtc)': '(nowebrtc.js)',
':upward(1)': ':nth-ancestor(1)',
':upward(2)': ':nth-ancestor(2)',
':upward(3)': ':nth-ancestor(3)',
':upward(4)': ':nth-ancestor(4)',
':upward(5)': ':nth-ancestor(5)',
':upward(6)': ':nth-ancestor(6)',
':upward(7)': ':nth-ancestor(7)',
':upward(8)': ':nth-ancestor(8)',
':upward(9)': ':nth-ancestor(9)'
'(nowebrtc)': '(nowebrtc.js)'
},
get regexRules() {
delete this.regexRules;
Expand Down
2 changes: 1 addition & 1 deletion src/js/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ var µBlock = (function() { // jshint ignore:line

// read-only
systemSettings: {
compiledMagic: 2,
compiledMagic: 3,
selfieMagic: 1
},

Expand Down
116 changes: 73 additions & 43 deletions src/js/contentscript.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,24 +324,6 @@ vAPI.domWatcher = (function() {
/******************************************************************************/
/******************************************************************************/

vAPI.matchesProp = (function() {
const docElem = document.documentElement;
if ( typeof docElem.matches !== 'function' ) {
if ( typeof docElem.mozMatchesSelector === 'function' ) {
return 'mozMatchesSelector';
} else if ( typeof docElem.webkitMatchesSelector === 'function' ) {
return 'webkitMatchesSelector';
} else if ( typeof docElem.msMatchesSelector === 'function' ) {
return 'msMatchesSelector';
}
}
return 'matches';
})();

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

vAPI.injectScriptlet = function(doc, text) {
if ( !doc ) { return; }
try {
Expand Down Expand Up @@ -437,18 +419,10 @@ vAPI.DOMFilterer = (function() {
}
};

const PSelectorNthAncestorTask = class {
constructor(task) {
this.nth = task[1];
const PSelectorPassthru = class {
constructor() {
}
transpose(node, output) {
let nth = this.nth;
for (;;) {
node = node.parentElement;
if ( node === null ) { return; }
nth -= 1;
if ( nth === 0 ) { break; }
}
output.push(node);
}
};
Expand All @@ -475,6 +449,36 @@ vAPI.DOMFilterer = (function() {
}
};

const PSelectorUpwardTask = class {
constructor(task) {
const arg = task[1];
if ( typeof arg === 'number' ) {
this.i = arg;
} else {
this.s = arg;
}
}
transpose(node, output) {
if ( this.s !== '' ) {
const parent = node.parentElement;
if ( parent === null ) { return; }
node = parent.closest(this.s);
if ( node === null ) { return; }
} else {
let nth = this.i;
for (;;) {
node = node.parentElement;
if ( node === null ) { return; }
nth -= 1;
if ( nth === 0 ) { break; }
}
}
output.push(node);
}
};
PSelectorUpwardTask.prototype.i = 0;
PSelectorUpwardTask.prototype.s = '';

const PSelectorWatchAttrs = class {
constructor(task) {
this.observer = null;
Expand Down Expand Up @@ -541,8 +545,10 @@ vAPI.DOMFilterer = (function() {
[ ':matches-css-before', PSelectorMatchesCSSBeforeTask ],
[ ':min-text-length', PSelectorMinTextLengthTask ],
[ ':not', PSelectorIfNotTask ],
[ ':nth-ancestor', PSelectorNthAncestorTask ],
[ ':nth-ancestor', PSelectorUpwardTask ],
[ ':remove', PSelectorPassthru ],
[ ':spath', PSelectorSpathTask ],
[ ':upward', PSelectorUpwardTask ],
[ ':watch-attr', PSelectorWatchAttrs ],
[ ':xpath', PSelectorXpathTask ],
]);
Expand All @@ -554,15 +560,21 @@ vAPI.DOMFilterer = (function() {
this.selector = o.selector;
this.tasks = [];
const tasks = o.tasks;
if ( !tasks ) { return; }
for ( let task of tasks ) {
this.tasks.push(new (this.operatorToTaskMap.get(task[0]))(task));
if ( Array.isArray(tasks) ) {
for ( let task of tasks ) {
this.tasks.push(
new (this.operatorToTaskMap.get(task[0]))(task)
);
}
}
if ( o.action !== undefined ) {
this.action = o.action;
}
}
prime(input) {
const root = input || document;
if ( this.selector === '' ) { return [ root ]; }
return root.querySelectorAll(this.selector);
return Array.from(root.querySelectorAll(this.selector));
}
exec(input) {
let nodes = this.prime(input);
Expand Down Expand Up @@ -593,6 +605,8 @@ vAPI.DOMFilterer = (function() {
return false;
}
};
PSelector.prototype.action = undefined;
PSelector.prototype.hit = false;
PSelector.prototype.operatorToTaskMap = undefined;

const DOMProceduralFilterer = function(domFilterer) {
Expand All @@ -612,20 +626,20 @@ vAPI.DOMFilterer = (function() {
for ( let i = 0, n = aa.length; i < n; i++ ) {
const raw = aa[i];
const o = JSON.parse(raw);
if ( o.style ) {
this.domFilterer.addCSSRule(o.style[0], o.style[1]);
if ( o.action === 'style' ) {
this.domFilterer.addCSSRule(o.selector, o.tasks[0][1]);
mustCommit = true;
continue;
}
if ( o.pseudoclass ) {
if ( o.pseudo !== undefined ) {
this.domFilterer.addCSSRule(
o.raw,
o.selector,
'display:none!important;'
);
mustCommit = true;
continue;
}
if ( o.tasks ) {
if ( o.tasks !== undefined ) {
if ( this.selectors.has(raw) === false ) {
const pselector = new PSelector(o);
this.selectors.set(raw, pselector);
Expand Down Expand Up @@ -662,8 +676,7 @@ vAPI.DOMFilterer = (function() {

let t0 = Date.now();

for ( let entry of this.selectors ) {
const pselector = entry[1];
for (let pselector of this.selectors.values() ) {
const allowance = Math.floor((t0 - pselector.lastAllowanceTime) / 2000);
if ( allowance >= 1 ) {
pselector.budget += allowance * 50;
Expand All @@ -679,9 +692,12 @@ vAPI.DOMFilterer = (function() {
pselector.budget = -0x7FFFFFFF;
}
t0 = t1;
for ( let node of nodes ) {
this.domFilterer.hideNode(node);
this.hiddenNodes.add(node);
if ( nodes.length === 0 ) { continue; }
pselector.hit = true;
if ( pselector.action === 'remove' ) {
this.removeNodes(nodes);
} else {
this.hideNodes(nodes);
}
}

Expand All @@ -692,6 +708,20 @@ vAPI.DOMFilterer = (function() {
//console.timeEnd('procedural selectors/dom layout changed');
},

hideNodes(nodes) {
for ( let node of nodes ) {
if ( node.parentElement === null ) { continue; }
this.domFilterer.hideNode(node);
this.hiddenNodes.add(node);
}
},

removeNodes(nodes) {
for ( let node of nodes ) {
node.remove();
}
},

createProceduralFilter: function(o) {
return new PSelector(o);
},
Expand Down
8 changes: 4 additions & 4 deletions src/js/scriptlets/cosmetic-logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ let processDeclarativeComplex = function(out) {

let processProcedural = function(out) {
if ( proceduralDict.size === 0 ) { return; }
for ( let entry of proceduralDict ) {
if ( entry[1].test() === false ) { continue; }
out.push(entry[1].raw);
proceduralDict.delete(entry[0]);
for ( let [ raw, pselector ] of proceduralDict ) {
if ( pselector.hit === false ) { continue; }
out.push(`##${raw}`);
proceduralDict.delete(raw);
if ( proceduralDict.size === 0 ) { break; }
}
};
Expand Down
6 changes: 3 additions & 3 deletions src/js/scriptlets/element-picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -766,9 +766,9 @@ var filterToDOMInterface = (function() {
return;
}
var elems;
if ( o.style ) {
elems = document.querySelectorAll(o.style[0]);
lastAction = o.style[0] + ' {' + o.style[1] + '}';
if ( o.action === 'style' ) {
elems = document.querySelectorAll(o.selector);
lastAction = o.selector + ' {' + o.tasks[0][1] + '}';
} else if ( o.tasks ) {
elems = vAPI.domFilterer.createProceduralFilter(o).exec();
}
Expand Down
Loading

0 comments on commit 440917e

Please sign in to comment.