pub(crate) const MAX_WALK_NODES: usize = 20_000;
pub(crate) const MAX_ROOTS: usize = 500;
pub(crate) fn deep_helpers_js() -> String {
format!(
r#"
const __OC_MAX_NODES = {MAX_WALK_NODES};
const __OC_MAX_ROOTS = {MAX_ROOTS};
// Breadth-first collection of the document plus every OPEN shadow
// root reachable from it. `querySelectorAll('*')` on a root does
// NOT descend into nested shadow trees, so each node is scanned
// exactly once across the whole pass — linear, not quadratic.
// `seen` is the cycle guard: a root can never be queued twice.
const __ocRoots = () => {{
const roots = [document];
const seen = new Set(roots);
for (let i = 0; i < roots.length; i++) {{
if (roots.length >= __OC_MAX_ROOTS) break;
const hosts = roots[i].querySelectorAll('*');
for (const h of hosts) {{
const sr = h.shadowRoot;
if (sr && !seen.has(sr)) {{ seen.add(sr); roots.push(sr); }}
if (roots.length >= __OC_MAX_ROOTS) break;
}}
}}
return roots;
}};
// Deep querySelectorAll. An invalid selector is allowed to throw
// exactly as `document.querySelectorAll` would, so callers that
// report "invalid selector" keep reporting it.
const __ocQueryAll = (sel, limit) => {{
const out = [];
for (const r of __ocRoots()) {{
for (const el of r.querySelectorAll(sel)) {{
out.push(el);
if (out.length >= limit) return out;
}}
}}
return out;
}};
const __ocQueryOne = (sel) => {{
const r = __ocQueryAll(sel, 1);
return r.length ? r[0] : null;
}};
// Every Element in the composed tree. Ordered document-first then
// per shadow root; within a root it is exact document order.
const __ocWalk = () => {{
const out = [];
for (const r of __ocRoots()) {{
const base = r === document ? document.body : r;
if (!base) continue;
const w = document.createTreeWalker(base, NodeFilter.SHOW_ELEMENT);
let n;
while ((n = w.nextNode())) {{
out.push(n);
if (out.length >= __OC_MAX_NODES) return out;
}}
}}
return out;
}};
// `Node.contains` stops at a shadow boundary, so climb the
// composed tree by hand: parentNode inside a tree, then the
// root's `host` to hop out of it.
const __ocComposedContains = (a, b) => {{
let n = b;
let guard = 0;
while (n && guard++ < __OC_MAX_ROOTS) {{
if (n === a) return true;
n = n.parentNode
|| (n.getRootNode ? n.getRootNode().host : null)
|| null;
}}
return false;
}};
// `document.elementFromPoint` RETARGETS to the shadow host, so a
// shadow-inner element never equals its own hit result and would
// be dropped as occluded. `ShadowRoot` implements
// `DocumentOrShadowRoot`, so hit-test inside the element's own
// root and the comparison is apples to apples.
const __ocHitTest = (el, x, y) => {{
const root = el.getRootNode();
const from = (root && root.elementFromPoint) ? root : document;
return from.elementFromPoint(x, y);
}};
const __ocClearStamps = () => {{
for (const r of __ocRoots()) {{
r.querySelectorAll('[data-opencrabs-match]').forEach(
el => el.removeAttribute('data-opencrabs-match'));
}}
}};
const __ocInShadow = (el) => {{
const r = el.getRootNode ? el.getRootNode() : document;
return !!r && r !== document;
}};
"#
)
}
pub(crate) fn with_deep_helpers(body: &str) -> String {
let helpers = deep_helpers_js();
format!("(() => {{{helpers}\n{body}\n}})()")
}