From 5659194932ad331fc1760258550e65fe4549b81c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 10 Oct 2022 08:38:40 -0400 Subject: [PATCH] Fix broken :has() operator in HTML filtering Related feedback: - https://github.com/uBlockOrigin/uBlock-issues/issues/2228#issuecomment-1273119017 --- src/js/contentscript-extra.js | 27 +++++---------------------- src/js/html-filtering.js | 26 ++++++++++++++++++++------ 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/js/contentscript-extra.js b/src/js/contentscript-extra.js index 11cbc595a..ee4e8ff84 100644 --- a/src/js/contentscript-extra.js +++ b/src/js/contentscript-extra.js @@ -232,25 +232,10 @@ class PSelectorSpathTask extends PSelectorTask { this.spath = `:scope ${this.spath.trim()}`; } } - qsa(node) { - if ( this.nth === false ) { - return node.querySelectorAll(this.spath); - } - const parent = node.parentElement; - if ( parent === null ) { return; } - let pos = 1; - for (;;) { - node = node.previousElementSibling; - if ( node === null ) { break; } - pos += 1; - } - return parent.querySelectorAll( - `:scope > :nth-child(${pos})${this.spath}` - ); - } transpose(node, output) { - const nodes = this.qsa(node); - if ( nodes === undefined ) { return; } + const nodes = this.nth + ? PSelectorSpathTask.qsa(node, this.spath) + : node.querySelectorAll(this.spath); for ( const node of nodes ) { output.push(node); } @@ -394,12 +379,10 @@ class PSelector { prime(input) { const root = input || document; if ( this.selector === '' ) { return [ root ]; } - let selector = this.selector; - if ( input !== document && /^ [>+~]/.test(this.selector) ) { + if ( input !== document && /^ ?[>+~]/.test(this.selector) ) { return Array.from(PSelectorSpathTask.qsa(input, this.selector)); } - const elems = root.querySelectorAll(selector); - return Array.from(elems); + return Array.from(root.querySelectorAll(this.selector)); } exec(input) { let nodes = this.prime(input); diff --git a/src/js/html-filtering.js b/src/js/html-filtering.js index 03b341884..d70346ad9 100644 --- a/src/js/html-filtering.js +++ b/src/js/html-filtering.js @@ -104,22 +104,33 @@ const PSelectorMinTextLengthTask = class { const PSelectorSpathTask = class { constructor(task) { this.spath = task[1]; + this.nth = /^(?:\s*[+~]|:)/.test(this.spath); + if ( this.nth ) { return; } + if ( /^\s*>/.test(this.spath) ) { + this.spath = `:scope ${this.spath.trim()}`; + } } transpose(node, output) { + const nodes = this.nth + ? PSelectorSpathTask.qsa(node, this.spath) + : node.querySelectorAll(this.spath); + for ( const node of nodes ) { + output.push(node); + } + } + // Helper method for other operators. + static qsa(node, selector) { const parent = node.parentElement; - if ( parent === null ) { return; } + if ( parent === null ) { return []; } let pos = 1; for (;;) { node = node.previousElementSibling; if ( node === null ) { break; } pos += 1; } - const nodes = parent.querySelectorAll( - `:scope > :nth-child(${pos})${this.spath}` + return parent.querySelectorAll( + `:scope > :nth-child(${pos})${selector}` ); - for ( const node of nodes ) { - output.push(node); - } } }; @@ -198,6 +209,9 @@ const PSelector = class { prime(input) { const root = input || docRegister; if ( this.selector === '' ) { return [ root ]; } + if ( input !== docRegister && /^ ?[>+~]/.test(this.selector) ) { + return Array.from(PSelectorSpathTask.qsa(input, this.selector)); + } return Array.from(root.querySelectorAll(this.selector)); } exec(input) {