/** * Flatten all open shadow DOM trees into the light DOM so that * page.content() / outerHTML can serialize the full composed view. * * Uses manual recursive serialization with proper slot resolution. * Resolves slots via the live DOM API (assignedNodes), skips only * shadow-scoped styles, and produces clean HTML with no regex hacks. * * Returns the full HTML string including shadow content. */ (() => { const VOID = new Set([ 'area','base','br','col','embed','hr','img','input', 'link','meta','param','source','track','wbr' ]); // Serialize a DOM node. When it has a shadow root, switch to // shadow-aware serialization that resolves elements. const serialize = (node) => { if (node.nodeType === Node.TEXT_NODE) return node.textContent; if (node.nodeType === Node.COMMENT_NODE) return ''; if (node.nodeType !== Node.ELEMENT_NODE) return ''; const tag = node.tagName.toLowerCase(); const attrs = serializeAttrs(node); let inner = ''; if (node.shadowRoot) { inner = serializeShadowRoot(node); } else { for (const child of node.childNodes) { inner += serialize(child); } } if (VOID.has(tag)) return `<${tag}${attrs}>`; return `<${tag}${attrs}>${inner}`; }; // Serialize a shadow root's children, resolving slots against // the host's light DOM children. const serializeShadowRoot = (host) => { let result = ''; for (const child of host.shadowRoot.childNodes) { result += serializeShadowChild(child, host); } return result; }; // Serialize a node that lives inside a shadow root. //