From 2839d96fbe8153b724f6eb89a4429637cd6621c5 Mon Sep 17 00:00:00 2001 From: Radon Rosborough Date: Fri, 16 Jan 2026 20:10:27 -0800 Subject: [PATCH 1/2] Fill fields inside shadow roots --- src/inject.js | 122 +++++++++++++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 52 deletions(-) diff --git a/src/inject.js b/src/inject.js index 7eb5793b..80b5020a 100644 --- a/src/inject.js +++ b/src/inject.js @@ -270,6 +270,20 @@ return result; } + /** + * Get list of shadow DOM roots, recursively. + * + * @since 3.12.0 + * + * @param DOMElement parent Parent element to query + * @return array List of shadow root elements plus parent + */ + function queryShadowRoots(parent) { + return [parent, ...parent.querySelectorAll("*")] + .filter((e) => e.shadowRoot) + .flatMap((e) => [e.shadowRoot, ...queryShadowRoots(e.shadowRoot)]); + } + /** * Query all visible elements * @@ -282,60 +296,64 @@ */ function queryAllVisible(parent, field, form) { const result = []; - for (let i = 0; i < field.selectors.length; i++) { - let elems = parent.querySelectorAll(field.selectors[i]); - for (let j = 0; j < elems.length; j++) { - let elem = elems[j]; - // Select only elements from specified form - if (form && form != elem.form) { - continue; - } - // Ignore disabled fields - if (elem.disabled) { - continue; - } - // Elem or its parent has a style 'display: none', - // or it is just too narrow to be a real field (a trap for spammers?). - if (elem.offsetWidth < 30 || elem.offsetHeight < 10) { - continue; - } - // We may have a whitelist of acceptable field types. If so, skip elements of a different type. - if (field.types && field.types.indexOf(elem.type.toLowerCase()) < 0) { - continue; - } - // Elem takes space on the screen, but it or its parent is hidden with a visibility style. - let style = window.getComputedStyle(elem); - if (style.visibility == "hidden") { - continue; - } - // Elem is outside of the boundaries of the visible viewport. - let rect = elem.getBoundingClientRect(); - if ( - rect.x + rect.width < 0 || - rect.y + rect.height < 0 || - rect.x > window.innerWidth || - rect.y > window.innerHeight - ) { - continue; - } - // Elem is hidden by its or or its parent's opacity rules - const OPACITY_LIMIT = 0.1; - let opacity = 1; - for ( - let testElem = elem; - opacity >= OPACITY_LIMIT && testElem && testElem.nodeType === Node.ELEMENT_NODE; - testElem = testElem.parentNode - ) { - let style = window.getComputedStyle(testElem); - if (style.opacity) { - opacity *= parseFloat(style.opacity); + for (let root of queryShadowRoots(parent)) { + for (let i = 0; i < field.selectors.length; i++) { + let elems = root.querySelectorAll(field.selectors[i]); + for (let j = 0; j < elems.length; j++) { + let elem = elems[j]; + // Select only elements from specified form + if (form && form != elem.form) { + continue; } + // Ignore disabled fields + if (elem.disabled) { + continue; + } + // Elem or its parent has a style 'display: none', + // or it is just too narrow to be a real field (a trap for spammers?). + if (elem.offsetWidth < 30 || elem.offsetHeight < 10) { + continue; + } + // We may have a whitelist of acceptable field types. If so, skip elements of a different type. + if (field.types && field.types.indexOf(elem.type.toLowerCase()) < 0) { + continue; + } + // Elem takes space on the screen, but it or its parent is hidden with a visibility style. + let style = window.getComputedStyle(elem); + if (style.visibility == "hidden") { + continue; + } + // Elem is outside of the boundaries of the visible viewport. + let rect = elem.getBoundingClientRect(); + if ( + rect.x + rect.width < 0 || + rect.y + rect.height < 0 || + rect.x > window.innerWidth || + rect.y > window.innerHeight + ) { + continue; + } + // Elem is hidden by its or or its parent's opacity rules + const OPACITY_LIMIT = 0.1; + let opacity = 1; + for ( + let testElem = elem; + opacity >= OPACITY_LIMIT && + testElem && + testElem.nodeType === Node.ELEMENT_NODE; + testElem = testElem.parentNode + ) { + let style = window.getComputedStyle(testElem); + if (style.opacity) { + opacity *= parseFloat(style.opacity); + } + } + if (opacity < OPACITY_LIMIT) { + continue; + } + // This element is visible, will use it. + result.push(elem); } - if (opacity < OPACITY_LIMIT) { - continue; - } - // This element is visible, will use it. - result.push(elem); } } return result; From 4293ce2a0240cc2ea2e211224a0345ddae77ee65 Mon Sep 17 00:00:00 2001 From: Radon Rosborough Date: Mon, 9 Feb 2026 15:57:48 -0800 Subject: [PATCH 2/2] Fix logic error --- src/inject.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/inject.js b/src/inject.js index 80b5020a..edb18bf0 100644 --- a/src/inject.js +++ b/src/inject.js @@ -276,10 +276,10 @@ * @since 3.12.0 * * @param DOMElement parent Parent element to query - * @return array List of shadow root elements plus parent + * @return array List of shadow root elements */ function queryShadowRoots(parent) { - return [parent, ...parent.querySelectorAll("*")] + return [...parent.querySelectorAll("*")] .filter((e) => e.shadowRoot) .flatMap((e) => [e.shadowRoot, ...queryShadowRoots(e.shadowRoot)]); } @@ -296,7 +296,7 @@ */ function queryAllVisible(parent, field, form) { const result = []; - for (let root of queryShadowRoots(parent)) { + for (let root of [parent, ...queryShadowRoots(parent)]) { for (let i = 0; i < field.selectors.length; i++) { let elems = root.querySelectorAll(field.selectors[i]); for (let j = 0; j < elems.length; j++) {