Capture: send open shadow roots with the page #30

Open
opened 2026-10-10 16:48:40 +00:00 by tiagoagueda · 0 comments
Owner

The capture sends document.documentElement.outerHTML (src/lib/browser.js:66). outerHTML leaves out every shadow root, so a board that draws its offer inside a web component arrives empty: the server-side sources read the same bare page a pasted URL gives.

France Travail is one: on candidat.francetravail.fr/offres/recherche/detail/<ID> the whole offer is inside the open shadow root of <descriptif-offre>. The page's light DOM holds only the <title>. Checked on 2026-10-10 with two live offers: outerHTML was 58,890 characters with the component empty, and its shadow root held another 36,464.

What would change

  • Serialise with document.documentElement.getHTML({ serializableShadowRoots: true, shadowRoots: [...open roots] }) (Chrome 125+, Firefox 128+). Each open root becomes a declarative <template shadowrootmode="open"> in place, which an HTML parser on the server reads as ordinary markup.
  • Collect the open roots with a walk over querySelectorAll('*'). el.shadowRoot is null for closed roots, so they stay out, as they should.
  • Keep outerHTML as the fallback where getHTML is missing.
  • The 2 MB cap applies to the result as before.

Why it matters beyond one board

Web components are spreading on job boards. Postulo's own sources (JSON-LD, the readability fallback) gain the text too.

Found while building tiagoagueda/postulo-francetravail, an independent plugin and not one of Postulo's; its source reads the serialised shadow root. As usual, firefox follows chromium.

The capture sends `document.documentElement.outerHTML` (`src/lib/browser.js:66`). `outerHTML` leaves out every shadow root, so a board that draws its offer inside a web component arrives empty: the server-side sources read the same bare page a pasted URL gives. France Travail is one: on `candidat.francetravail.fr/offres/recherche/detail/<ID>` the whole offer is inside the open shadow root of `<descriptif-offre>`. The page's light DOM holds only the `<title>`. Checked on 2026-10-10 with two live offers: `outerHTML` was 58,890 characters with the component empty, and its shadow root held another 36,464. ## What would change - Serialise with `document.documentElement.getHTML({ serializableShadowRoots: true, shadowRoots: [...open roots] })` (Chrome 125+, Firefox 128+). Each open root becomes a declarative `<template shadowrootmode="open">` in place, which an HTML parser on the server reads as ordinary markup. - Collect the open roots with a walk over `querySelectorAll('*')`. `el.shadowRoot` is `null` for closed roots, so they stay out, as they should. - Keep `outerHTML` as the fallback where `getHTML` is missing. - The 2 MB cap applies to the result as before. ## Why it matters beyond one board Web components are spreading on job boards. Postulo's own sources (JSON-LD, the readability fallback) gain the text too. Found while building tiagoagueda/postulo-francetravail, an independent plugin and not one of Postulo's; its source reads the serialised shadow root. As usual, firefox follows chromium.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo-chromium#30
No description provided.