From 44a80b6a2ba18b913b6484c83cd1e585db593207 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Mon, 6 Oct 2025 09:59:46 -0400 Subject: [PATCH] add queryDeepSelector to the DOM query service --- .../abstractions/dom-query.service.ts | 1 + .../autofill/services/dom-query.service.ts | 33 +++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/apps/browser/src/autofill/services/abstractions/dom-query.service.ts b/apps/browser/src/autofill/services/abstractions/dom-query.service.ts index 32809573223..dc43a488ca0 100644 --- a/apps/browser/src/autofill/services/abstractions/dom-query.service.ts +++ b/apps/browser/src/autofill/services/abstractions/dom-query.service.ts @@ -6,5 +6,6 @@ export interface DomQueryService { mutationObserver?: MutationObserver, forceDeepQueryAttempt?: boolean, ): T[]; + queryDeepSelector(root: Document | ShadowRoot | Element, selector: string): Element[]; checkPageContainsShadowDom(): boolean; } diff --git a/apps/browser/src/autofill/services/dom-query.service.ts b/apps/browser/src/autofill/services/dom-query.service.ts index b681e8e9fbb..f5c9843c2bf 100644 --- a/apps/browser/src/autofill/services/dom-query.service.ts +++ b/apps/browser/src/autofill/services/dom-query.service.ts @@ -141,6 +141,33 @@ export class DomQueryService implements DomQueryServiceInterface { return Array.from(root.querySelectorAll(queryString)) as T[]; } + /** + * Queries the DOM for elements based on the given CSS selector string; + * uses special `>>>` syntax to indicate needed shadowDOM traversal. + * + * @param root - The root element to start the query from + * @param selector - The CSS selector string to match elements against + */ + queryDeepSelector(root: Element, selector: string): Element[] { + const rootScopedSelectors = selector.split(">>>"); + let current: Element[] = [root]; + + for (const selector of rootScopedSelectors) { + const next: Element[] = []; + for (const element of current) { + next.push(...Array.from(element.querySelectorAll(selector))); + + const elementShadowRoot = this.getShadowRoot(element); + if (elementShadowRoot) { + next.push(...Array.from(elementShadowRoot.querySelectorAll(selector))); + } + } + current = next; + } + + return current; + } + /** * Recursively queries all shadow roots found within the given root element. * Will also set up a mutation observer on the shadow root if the @@ -217,13 +244,12 @@ export class DomQueryService implements DomQueryServiceInterface { if ((chrome as any).dom?.openOrClosedShadowRoot) { try { return (chrome as any).dom.openOrClosedShadowRoot(node); - // FIXME: Remove when updating file. Eslint update - // eslint-disable-next-line @typescript-eslint/no-unused-vars - } catch (error) { + } catch { return null; } } + // Firefox-specific implementation of `openOrClosedShadowRoot` return (node as any).openOrClosedShadowRoot; } @@ -284,6 +310,7 @@ export class DomQueryService implements DomQueryServiceInterface { } const nodeShadowRoot = this.getShadowRoot(currentNode); + // console.log('🚀 🚀 nodeShadowRoot:', nodeShadowRoot); if (nodeShadowRoot) { if (mutationObserver) { mutationObserver.observe(nodeShadowRoot, {