From 13680132d7b5434accc36a39952267d452e6fd0b Mon Sep 17 00:00:00 2001 From: Paul Kinlan Date: Sun, 20 Sep 2026 06:12:43 +0000 Subject: [PATCH 1/6] v154: reference for Add options bag to WebSocket constructor Generated from spec: whatwg/websockets PR #76 (verbatim IDL), issue #42, Chromium websocket.idl/websocket_init.idl/dom_websocket.cc, CLs 8197225 (merged) and 8256918 (open promote-to-stable), WPT option-bag.any.js. Overview + two child references (constructor overload, WebSocketInit), implementation-sufficient contract, conformance suite, critique, and mobile+desktop matrix (screenshots read). Honestly discloses that the milestone-154 'Enabled by default' listing is ahead of the trunk flag state (WebSocketOptionBag still experimental). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_017hFhoS24xY2u3ctcHNkeNR --- goals.json | 38 +- reports/conformance/index.html | 6 +- reports/conformance/responsive.json | 10 +- reports/conformance/results.json | 30 +- responsive-support.json | 14 +- .../_questions.json | 121 ++++++ .../conformance.json | 222 ++++++++++ .../index.html | 217 ++++++++++ .../reference-contract.json | 405 ++++++++++++++++++ .../websocket-constructor/index.html | 155 +++++++ .../websocketinit/index.html | 164 +++++++ 11 files changed, 1354 insertions(+), 28 deletions(-) create mode 100644 v154/add-options-bag-to-websocket-constructor/_questions.json create mode 100644 v154/add-options-bag-to-websocket-constructor/conformance.json create mode 100644 v154/add-options-bag-to-websocket-constructor/index.html create mode 100644 v154/add-options-bag-to-websocket-constructor/reference-contract.json create mode 100644 v154/add-options-bag-to-websocket-constructor/websocket-constructor/index.html create mode 100644 v154/add-options-bag-to-websocket-constructor/websocketinit/index.html diff --git a/goals.json b/goals.json index b543c7d..260c277 100644 --- a/goals.json +++ b/goals.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "generatedAt": "2026-08-02T06:39:53.375Z", + "generatedAt": "2026-09-20T06:11:08.198Z", "goals": [ { "id": "v147-css-border-shape-121qq4j", @@ -209,6 +209,24 @@ "status": "open", "createdAt": "2026-07-29" }, + { + "id": "v154-add-options-bag-to-websocket-constructor-15jpp36", + "sourcePage": "v154/add-options-bag-to-websocket-constructor", + "goal": "When CL 8256918 (promote WebSocketOptionBag to stable) merges, update the warn-blocks, baseline banner, and compat tables from 'behind an experimental flag' to the shipped milestone, and re-check the chrome://flags guidance.", + "kind": "targeted-fix", + "priority": "high", + "status": "open", + "createdAt": "2026-09-20" + }, + { + "id": "v154-add-options-bag-to-websocket-constructor-tr44bs", + "sourcePage": "v154/add-options-bag-to-websocket-constructor", + "goal": "When whatwg/websockets PR #76 merges (or changes), re-verify the verbatim IDL quotes and the two constructor routing steps against the merged living-standard text and update the specifications table on all three pages.", + "kind": "targeted-fix", + "priority": "high", + "status": "open", + "createdAt": "2026-09-20" + }, { "id": "v147-css-border-shape-h47dz4", "sourcePage": "v147/css-border-shape", @@ -805,6 +823,24 @@ "status": "open", "createdAt": "2026-07-29" }, + { + "id": "v154-add-options-bag-to-websocket-constructor-1j1w8r1", + "sourcePage": "v154/add-options-bag-to-websocket-constructor", + "goal": "Execute the WPT option-bag tests (or the examples) in a flagged Chromium build and confirm the TypeError-on-bare-string and silent-unknown-member behaviors match the Web IDL derivation documented on the WebSocketInit page.", + "kind": "targeted-fix", + "priority": "medium", + "status": "open", + "createdAt": "2026-09-20" + }, + { + "id": "v154-add-options-bag-to-websocket-constructor-fah0qn", + "sourcePage": "v154/add-options-bag-to-websocket-constructor", + "goal": "When BCD adds a WebSocket constructor options-bag subfeature (and webstatus.dev records a web-feature), replace the interim compat tables with BCD-anchored per-version rows.", + "kind": "targeted-fix", + "priority": "medium", + "status": "open", + "createdAt": "2026-09-20" + }, { "id": "v149-css-gap-decorations-fr1umo", "sourcePage": "v149/css-gap-decorations", diff --git a/reports/conformance/index.html b/reports/conformance/index.html index 6680ae3..67f82d5 100644 --- a/reports/conformance/index.html +++ b/reports/conformance/index.html @@ -22,12 +22,12 @@

Deterministic headless-Chrome run of every reference page's immutable conformance suite. Blocked = manual-evidenced (needs agent review) or genuinely unavailable — never a pass.

1
suites
-
23
assertions
-
18
pass
+
22
assertions
+
17
pass
0
fail
5
blocked
-
pagestatustested/totalpassfailblocked
v147/css-border-shapebuilt18/231805
+ v154/css-text-decoration-insetstub17/221705
generated by scripts/conformance.mjs
\ No newline at end of file diff --git a/reports/conformance/responsive.json b/reports/conformance/responsive.json index a51bbf1..8d7b4fa 100644 --- a/reports/conformance/responsive.json +++ b/reports/conformance/responsive.json @@ -1,20 +1,20 @@ [ { - "id": "v147/css-border-shape", - "route": "/v147/css-border-shape/", + "id": "v154/css-text-decoration-inset", + "route": "/v154/css-text-decoration-inset/", "desktop": { "noOverflow": true, "controlsInView": true, "consoleClean": true, "networkClean": true, - "screenshot": "reports/conformance/shots/v147__css-border-shape.desktop.png" + "screenshot": "reports/conformance/shots/v154__css-text-decoration-inset.desktop.png" }, "mobile": { "noOverflow": true, - "controlsInView": false, + "controlsInView": true, "consoleClean": true, "networkClean": true, - "screenshot": "reports/conformance/shots/v147__css-border-shape.mobile.png" + "screenshot": "reports/conformance/shots/v154__css-text-decoration-inset.mobile.png" } } ] diff --git a/reports/conformance/results.json b/reports/conformance/results.json index c4dbf9d..ce5d8b9 100644 --- a/reports/conformance/results.json +++ b/reports/conformance/results.json @@ -1,18 +1,18 @@ { - "generatedAt": "2026-08-02T06:55:28.440Z", + "generatedAt": "2026-09-20T06:10:07.167Z", "agg": { - "total": 23, - "pass": 18, + "total": 22, + "pass": 17, "fail": 0, "blocked": 5 }, "suites": [ { - "id": "v147/css-border-shape", - "route": "/v147/css-border-shape/", - "status": "built", - "total": 23, - "pass": 18, + "id": "v154/css-text-decoration-inset", + "route": "/v154/css-text-decoration-inset/", + "status": "stub", + "total": 22, + "pass": 17, "fail": 0, "blocked": 5, "results": [ @@ -135,25 +135,19 @@ "reason": "manual-pending (needs agent screenshot/source review)" }, { - "id": "min-sections", + "id": "mdn-card-present", "category": "structure", "deviceClass": "both", "status": "pass" }, { - "id": "browser-support-section", - "category": "content", - "deviceClass": "both", - "status": "pass" - }, - { - "id": "example-surface-present", - "category": "content", + "id": "mdn-outbound-link", + "category": "links", "deviceClass": "both", "status": "pass" }, { - "id": "code-example-integrity", + "id": "mdn-coverage-accurate", "category": "content", "deviceClass": "n/a", "status": "blocked", diff --git a/responsive-support.json b/responsive-support.json index 66dd4ea..0ad7ab3 100644 --- a/responsive-support.json +++ b/responsive-support.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "updatedAt": "2026-08-02T06:15:54.189Z", + "updatedAt": "2026-09-20T06:10:30.282Z", "routes": { "/v147/auto-sizes-for-lazy-loaded-images-with-srcset/": { "desktop": "needs-review", @@ -1177,6 +1177,18 @@ "mobile": "ok", "method": "manual matrix review \u2014 headless-Chrome 360px-emulated mobile (DPR 3) + 1280px desktop screenshots read by writer (no overflow: scrollWidth==innerWidth==360 probed via CDP; tables scroll in .table-wrap; banner/warn-block render; console+network clean per auto-scan); auto-scan controlsInView=false = links inside intentionally-scrollable wide compat table, expected", "checkedAt": "2026-08-02" + }, + "/v154/add-options-bag-to-websocket-constructor/": { + "desktop": "ok", + "mobile": "ok", + "method": "manual matrix review \u2014 CDP mobile emulation (360px, DPR 3) + 1280px desktop screenshots read by writer: no horizontal overflow, banner/warn-block/tables wrap or scroll in .table-wrap, pre blocks scroll internally, legible type", + "checkedAt": "2026-09-20" + }, + "/v154/css-text-decoration-inset/": { + "desktop": "ok", + "mobile": "ok", + "method": "manual matrix review \u2014 CDP mobile emulation (360px, DPR 3) + 1280px desktop screenshots read by writer: no horizontal overflow, banner/warn-block/tables wrap or scroll in .table-wrap, pre blocks scroll internally, legible type", + "checkedAt": "2026-09-20" } } } diff --git a/v154/add-options-bag-to-websocket-constructor/_questions.json b/v154/add-options-bag-to-websocket-constructor/_questions.json new file mode 100644 index 0000000..6775abc --- /dev/null +++ b/v154/add-options-bag-to-websocket-constructor/_questions.json @@ -0,0 +1,121 @@ +{ + "schemaVersion": 1, + "id": "v154/add-options-bag-to-websocket-constructor", + "route": "/v154/add-options-bag-to-websocket-constructor/", + "identity": "5080055102439424", + "milestone": 154, + "status": "built", + "revision": 1, + "reviewedAt": "2026-09-20", + "reviewer": "writer self-review; independent integrator source review pending", + "frontendTouched": true, + "rubric": [ + { + "dimension": "factual-source-fidelity", + "score": 5, + "severity": "info", + "evidence": "Every load-bearing claim traced to a primary source fetched live 2026-09-20: whatwg/websockets PR #76 diff (verbatim WebSocketInit + constructor IDL + the two routing steps), issue #42 (motivation), Chromium websocket.idl (temporary `any` parameter + TODO crbug 542670554), websocket_init.idl (sequence protocols + flag-gated targetAddressSpace), dom_websocket.cc (WebSocketOptionBagEnabled branch, ParseConstructorOptionsWithBag), runtime_enabled_features.json5 (WebSocketOptionBag experimental), Gerrit CLs 8197225 (merged 2026-08-12), 8256918 (open promote-to-stable), 8294944 (abandoned revert), WPT option-bag.any.js (both tests read), intent thread YwkXWzPUJ7U, vendor positions WebKit#708 / mozilla#1444." + }, + { + "dimension": "milestone-mapping", + "score": 5, + "severity": "info", + "evidence": "Feature id 5080055102439424 confirmed in the milestone=154 listing under 'Enabled by default' (fetched 2026-09-20); slug add-options-bag-to-websocket-constructor derived from the exact listing name 'Add options bag to WebSocket constructor' per invariant #1. The listing bucket vs the trunk flag state (still experimental, promotion CL open) is documented explicitly in the warn-block rather than smoothed over." + }, + { + "dimension": "completeness", + "score": 5, + "severity": "info", + "evidence": "The surface is the constructor overload plus one dictionary with one spec member; both get child routes with nine-dimension coverage, the union-routing algorithm is inventoried against the overview, and Chromium's flag-gated targetAddressSpace extension is recorded with a pointer to its own tracked feature (4779920606756864) rather than duplicated. reference-contract.json claims implementation-sufficient with 4 inventory items and 23 sources." + }, + { + "dimension": "explanation-quality", + "score": 4, + "severity": "info", + "evidence": "The routing table makes the six input shapes and their outcomes explicit, including the two traps: a bare-string protocols member (TypeError per Web IDL sequence conversion — the ChromeStatus summary's own example doesn't match the IDL, called out directly) and the typo-silently-ignored dictionary member behavior." + }, + { + "dimension": "examples", + "score": 4, + "severity": "minor", + "evidence": "Both-forms, feature-detect/fallback, and negotiation examples; TypeError and silent-typo cases shown as commented-out lines. Array-form usage matches the WPT tests verbatim in spirit; examples are source-derived but not executed locally (no Chromium build with the flag in this environment)." + }, + { + "dimension": "browser-compat-fallbacks", + "score": 5, + "severity": "info", + "evidence": "No BCD key and no webstatus.dev web-feature for the overload — both stated plainly with the queries named; interim tables labelled non-BCD; host-constructor Baseline (widely available) given for orientation only; Safari 'Support' position linked to WebKit#708, Firefox 'No signal' to the ChromeStatus record and mozilla#1444." + }, + { + "dimension": "links-validity", + "score": 5, + "severity": "info", + "evidence": "Zero plain-text citations; every source label links to the canonical original (spec PR + files view, issue, Gerrit CLs, googlesource IDL/cc/flag files, WPT blob, Groups intent thread, standards-position issues). CPS feature route and all three concept routes HEAD-checked 200 on 2026-09-20." + }, + { + "dimension": "accessibility", + "score": 5, + "severity": "info", + "evidence": "Single h1 per page, sequential h2 outline, th scope=row/col on all tables, baseline banner role=note with aria-label, iframe title + figcaption, soft hyphen in the long child-page h1 token." + }, + { + "dimension": "responsive-ux", + "score": 4, + "severity": "info", + "evidence": "overflow-wrap:anywhere on h1/lede/inline code, .table-wrap{overflow-x:auto} around every table, pre blocks scroll internally, iframe max-width:100%; verified via the repo responsive matrix (mobile 360px + desktop 1280px screenshots read) before push." + }, + { + "dimension": "showcase-demo-relationship", + "score": 5, + "severity": "info", + "evidence": "CPS feature route HEAD-200 with three concept routes (both-forms, negotiation, why-a-dictionary — all 200, 2026-09-20); both-forms embedded on the overview via lazy iframe; the other two linked from the figcaption; caption notes the demo needs the WebSocketOptionBag flag." + } + ], + "guidanceConsulted": [ + { + "id": "accessibility", + "query": "reference documentation page responsive tables code blocks accessibility", + "recommendation": "Semantic structure: single h1, ordered headings, labelled tables with th scope, descriptive titles for embedded frames, focus-visible interactive elements.", + "appliedOrException": "Applied: single h1 + sequential h2 outline on all three pages, th scope on every table, role=note baseline banner with aria-label, iframe title + figcaption." + }, + { + "id": "html", + "query": "reference documentation page responsive tables code blocks accessibility", + "recommendation": "Semantic document structure and native elements over ARIA re-implementation; prioritize readable, resilient markup.", + "appliedOrException": "Applied: plain sectioned HTML with native tables/figures/pre-code; no scripting required to read any content." + }, + { + "id": "defer-rendering-heavy-content", + "query": "embedding third party iframe lazy loading performance", + "recommendation": "Defer offscreen/heavy embeds so they don't compete with initial render.", + "appliedOrException": "Applied: the showcase demo iframe uses loading=\"lazy\" and sits below the examples fold; no other third-party content is loaded." + } + ], + "openQuestions": [ + "Whether WebSocketOptionBag actually ships enabled-by-default in Chrome 154 stable (2026-09-22) depends on CL 8256918 merging and possibly a Finch rollout (finch_name WebSocketOptionBag); the pages document the trunk flag state as of 2026-09-20.", + "PR #76's web-compat review (objects that today coerce to \"[object Object]\" and reject) may change the union shape or add mitigations; the routing table and errors sections would need re-anchoring if the merged text differs.", + "Whether protocols will also accept a bare DOMString inside the dictionary (as the ChromeStatus summary example implies) if reviewers change the member type from sequence to (DOMString or sequence) — currently documented strictly per the IDL as written." + ], + "followUpGoals": [ + { + "goal": "When whatwg/websockets PR #76 merges (or changes), re-verify the verbatim IDL quotes and the two constructor routing steps against the merged living-standard text and update the specifications table on all three pages.", + "kind": "targeted-fix", + "priority": "high" + }, + { + "goal": "When CL 8256918 (promote WebSocketOptionBag to stable) merges, update the warn-blocks, baseline banner, and compat tables from 'behind an experimental flag' to the shipped milestone, and re-check the chrome://flags guidance.", + "kind": "targeted-fix", + "priority": "high" + }, + { + "goal": "When BCD adds a WebSocket constructor options-bag subfeature (and webstatus.dev records a web-feature), replace the interim compat tables with BCD-anchored per-version rows.", + "kind": "targeted-fix", + "priority": "medium" + }, + { + "goal": "Execute the WPT option-bag tests (or the examples) in a flagged Chromium build and confirm the TypeError-on-bare-string and silent-unknown-member behaviors match the Web IDL derivation documented on the WebSocketInit page.", + "kind": "targeted-fix", + "priority": "medium" + } + ] +} diff --git a/v154/add-options-bag-to-websocket-constructor/conformance.json b/v154/add-options-bag-to-websocket-constructor/conformance.json new file mode 100644 index 0000000..8918417 --- /dev/null +++ b/v154/add-options-bag-to-websocket-constructor/conformance.json @@ -0,0 +1,222 @@ +{ + "schemaVersion": 1, + "id": "v154/add-options-bag-to-websocket-constructor", + "route": "/v154/add-options-bag-to-websocket-constructor/", + "identity": "5080055102439424", + "milestone": 154, + "status": "built", + "demo": "https://chrome-platform-showcase.paulkinlan-ea.deno.net/v154/add-options-bag-to-websocket-constructor/both-forms/", + "cpsFeature": { + "host": "chrome-platform-showcase.paulkinlan-ea.deno.net", + "route": "/v154/add-options-bag-to-websocket-constructor/both-forms/", + "conformanceRoute": "/v154/add-options-bag-to-websocket-constructor/both-forms/conformance", + "note": "The chrome-platform-showcase suite at conformanceRoute governs only the assertions it explicitly lists; it does not prove unasserted native, hardware, permission, backend, or other behavior. gendn references that listed contract without forking it." + }, + "immutable": true, + "suiteHash": "747f43e90965c72e8fbe4d4fbdbeedb000c0d8679296bc039b19848cde64d310", + "generatedAt": "2026-07-19T00:00:00Z", + "author": "gen-conformance/v1 (derived from page metadata)", + "assertions": [ + { + "id": "route-resolves", + "category": "route", + "describe": "The stable route /v154/add-options-bag-to-websocket-constructor/ responds 200 (durable-demo contract).", + "kind": "http-status", + "test": "/v154/add-options-bag-to-websocket-constructor/", + "expect": 200, + "deviceClass": "both" + }, + { + "id": "chromestatus-identity-link", + "category": "identity", + "describe": "Page carries its canonical chromestatus.com/feature/5080055102439424 link — the immutable feature identity (CLAUDE.md invariant #3) and the milestone-154 mapping's self-heal.", + "kind": "dom-query", + "selector": "a[href*=\"chromestatus.com/feature/5080055102439424\"]", + "deviceClass": "both" + }, + { + "id": "title-nonempty", + "category": "structure", + "describe": "Document has a non-empty for the reference page.", + "kind": "js-eval", + "test": "document.title.trim().length > 0", + "deviceClass": "both" + }, + { + "id": "single-h1", + "category": "structure", + "describe": "Exactly one <h1> — the API name (listing name), MDN-shaped article heading.", + "kind": "js-eval", + "test": "document.querySelectorAll('h1').length === 1", + "deviceClass": "both" + }, + { + "id": "main-landmark", + "category": "a11y", + "describe": "A single <main> landmark wraps the article content.", + "kind": "js-eval", + "test": "document.querySelectorAll('main').length === 1", + "deviceClass": "both" + }, + { + "id": "stylesheet-linked", + "category": "structure", + "describe": "Shared editorial design system /public/styles.css is linked.", + "kind": "dom-query", + "selector": "link[rel=\"stylesheet\"][href=\"/public/styles.css\"]", + "deviceClass": "both" + }, + { + "id": "viewport-meta", + "category": "responsive", + "describe": "Responsive viewport meta present (width=device-width) for mobile+desktop parity.", + "kind": "dom-query", + "selector": "meta[name=\"viewport\"]", + "deviceClass": "both" + }, + { + "id": "crumb-to-release", + "category": "structure", + "describe": "Breadcrumb links back to the /v154/ release index.", + "kind": "dom-query", + "selector": "a[href=\"/v154/\"]", + "deviceClass": "both" + }, + { + "id": "byline-footer", + "category": "structure", + "describe": "Byline footer present (article provenance).", + "kind": "dom-query", + "selector": ".byline", + "deviceClass": "both" + }, + { + "id": "external-links-rel-safe", + "category": "links", + "describe": "Every target=_blank link carries rel=noopener (no reverse-tabnabbing; MDN-style external link hygiene).", + "kind": "js-eval", + "test": "[...document.querySelectorAll('a[target=\"_blank\"]')].every(el => (el.rel||'').includes('noopener'))", + "deviceClass": "both" + }, + { + "id": "no-empty-anchors", + "category": "links", + "describe": "No anchor has an empty or placeholder (#) href.", + "kind": "js-eval", + "test": "[...document.querySelectorAll('a')].every(el => { const h = el.getAttribute('href'); return h && h !== '#'; })", + "deviceClass": "both" + }, + { + "id": "no-console-errors", + "category": "runtime", + "describe": "Page loads with no console errors.", + "kind": "no-console-errors", + "deviceClass": "both" + }, + { + "id": "no-failed-requests", + "category": "runtime", + "describe": "No failed (>=400 / net-error) network requests on load.", + "kind": "no-failed-requests", + "deviceClass": "both" + }, + { + "id": "no-horizontal-overflow-desktop", + "category": "responsive", + "describe": "No unintended horizontal overflow at desktop (1280x800).", + "kind": "js-eval", + "test": "document.documentElement.scrollWidth <= (window.innerWidth + 1)", + "deviceClass": "desktop" + }, + { + "id": "no-horizontal-overflow-mobile", + "category": "responsive", + "describe": "No unintended horizontal overflow at narrow mobile (360x740, DPR3).", + "kind": "js-eval", + "test": "document.documentElement.scrollWidth <= (window.innerWidth + 1)", + "deviceClass": "mobile" + }, + { + "id": "legible-desktop", + "category": "responsive", + "describe": "Desktop render is legible, contrast passes WCAG AA, controls/text not clipped (screenshot review).", + "kind": "manual-evidenced", + "deviceClass": "desktop" + }, + { + "id": "legible-mobile", + "category": "responsive", + "describe": "Mobile render is legible with adequate tap targets and no clipped text/controls (screenshot review).", + "kind": "manual-evidenced", + "deviceClass": "mobile" + }, + { + "id": "h1-matches-listing-name", + "category": "content", + "describe": "The <h1> is the chromestatus milestone-listing name for this feature (not the broader detail name) — factual/source fidelity.", + "kind": "manual-evidenced", + "deviceClass": "n/a" + }, + { + "id": "guidance-consulted", + "category": "guidance", + "describe": "Frontend implementation consulted modern-web-guidance for its UI/responsive/a11y topics and applied or justified them (recorded in _questions.json guidanceConsulted).", + "kind": "manual-evidenced", + "deviceClass": "n/a" + }, + { + "id": "min-sections", + "category": "structure", + "describe": "Built reference has at least three <h2> sections (MDN-shaped depth).", + "kind": "js-eval", + "test": "document.querySelectorAll('main h2').length >= 3", + "deviceClass": "both" + }, + { + "id": "browser-support-section", + "category": "content", + "describe": "Cross-browser support is documented (a support/compat/baseline section, or a reference table stating Baseline / Shipped-in / standards positions) — accurate messaging, not implied support.", + "kind": "js-eval", + "test": "[...document.querySelectorAll('main h2')].some(h => /support|compat|baseline|availab|standards? position/i.test(h.textContent)) || /baseline|shipped in|standards? position|cross-browser|browser compat/i.test(document.querySelector('main').textContent)", + "deviceClass": "both" + }, + { + "id": "example-surface-present", + "category": "content", + "describe": "Built reference includes a usable example surface (<pre> code and/or a live embed).", + "kind": "js-eval", + "test": "!!document.querySelector('main pre, main iframe')", + "deviceClass": "both" + }, + { + "id": "code-example-integrity", + "category": "content", + "describe": "Code/IDL examples are correctly escaped (no attribute/element injection, invariant #4) and trace to the cited spec/IDL — no invented members (source review).", + "kind": "manual-evidenced", + "deviceClass": "n/a" + }, + { + "id": "warn-block-experimental", + "category": "content", + "describe": "Experimental / origin-trial / behind-a-flag feature shows the mandatory .warn-block with enable steps (CLAUDE.md invariant #7).", + "kind": "dom-query", + "selector": ".warn-block", + "deviceClass": "both" + }, + { + "id": "demo-embed-own-feature", + "category": "demo-link", + "describe": "Embedded live example points at THIS feature's chrome-platform-showcase route (chrome-platform-showcase.paulkinlan-ea.deno.net/v154/add-options-bag-to-websocket-constructor...) — the canonical demo identity, never repointed.", + "kind": "js-eval", + "test": "[...document.querySelectorAll('iframe')].some(f => (f.src||'').includes('chrome-platform-showcase.paulkinlan-ea.deno.net/v154/add-options-bag-to-websocket-constructor'))", + "deviceClass": "both" + }, + { + "id": "demo-embed-loads", + "category": "interaction", + "describe": "The embedded chrome-platform-showcase demo actually renders (not a blank box or error frame); its platform behavior is governed by the CPS conformance contract referenced in cpsFeature, not re-asserted here.", + "kind": "manual-evidenced", + "deviceClass": "desktop" + } + ] +} diff --git a/v154/add-options-bag-to-websocket-constructor/index.html b/v154/add-options-bag-to-websocket-constructor/index.html new file mode 100644 index 0000000..fe23001 --- /dev/null +++ b/v154/add-options-bag-to-websocket-constructor/index.html @@ -0,0 +1,217 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Add options bag to WebSocket constructor — Chrome 154 — gendn + + + + +
+

← Chrome 154 reference

+ +
+

v154 · web api · networking

+

Add options bag to WebSocket constructor

+ +
+

Limited availability

+
    +
  • Chrome · implemented, behind an experimental flag (targeted at 154)
  • +
  • Edge · not separately reported
  • +
  • Firefox · no signal
  • +
  • Safari · positive standards position, not shipped
  • +
+

Not a Baseline feature: the option bag has no web-features entry on webstatus.dev (queried 2026-09-20; only the host websockets feature exists, Baseline widely available — that covers the classic constructor, not this overload). BCD has no subfeature key for it either (the constructor entry records only the host constructor and the relative-URL update, verified 2026-09-20).

+
+ +

An overload of the WebSocket() constructor: the second argument may now be a WebSocketInit options dictionary (new WebSocket(url, { protocols: ["soap"] })) instead of a bare string or array of subprotocol names. The dictionary initially carries a single member, protocols, and exists chiefly as an extension point — mirroring the Fetch API's option-bag style — so future connection options don't need new positional arguments.

+
+ +
+ Experimental — not yet enabled by default +

The milestone=154 listing files this feature under “Enabled by default”, but as of 2026-09-20 that is a target, not a shipped state: the runtime flag record for WebSocketOptionBag is still status: "experimental" at trunk, the CL that promotes it to stable (CL 8256918) is open and unmerged, the ChromeStatus record's own implementation status reads “Proposed”, and the defining spec change (whatwg/websockets PR #76) is open with active web-compat review. To test locally:

+
chrome --enable-blink-features=WebSocketOptionBag
+

or enable chrome://flags/#enable-experimental-web-platform-features. Feature-detect before relying on it — with the flag off, an options object is coerced to the string "[object Object]" and the constructor throws a SyntaxError:

+
function supportsWebSocketOptions() {
+  try {
+    // An empty bag is valid where the overload exists, and an invalid
+    // protocol string where it does not.
+    new WebSocket("wss://example.invalid/", {}).close();
+    return true;
+  } catch {
+    return false;
+  }
+}
+
+ +
+

at a glance

+
+ + + + + + + + +
What it isA third accepted type for the WebSocket() constructor's second argument: alongside DOMString and sequence<DOMString>, a WebSocketInit dictionary whose protocols member lists subprotocols (spec PR diff)
WhyExtensibility: an option bag mirrors fetch()'s RequestInit, so future per-connection options need no new positional parameters. Chromium's concrete motivation is a targetAddressSpace option for Local Network Access (issue #42)
Behavior changePassing a plain non-iterable object as the second argument today coerces to the string "[object Object]" and rejects with SyntaxError; with the overload it parses as an options dictionary. This is the web-compat question under measurement on the spec PR
Milestone listingChrome 154 — listed under “Enabled by default” in the milestone=154 listing (fetched 2026-09-20; the listing is authoritative for filing). See the warn-block for the actual flag state
Runtime featureWebSocketOptionBag (experimental) in runtime_enabled_features.json5; implemented by CL 8197225 (merged 2026-08-12); stable promotion pending in CL 8256918
WPTwebsockets/constructor/option-bag.any.js (upstreamed with the implementation CL): an empty bag is accepted; a protocols array negotiates the first supported subprotocol
ChromeStatus5080055102439424 — Add options bag to WebSocket constructor (blink component Blink>Network>WebSockets; tracking bug crbug.com/542670554)
+
+ Source: whatwg/websockets PR #76 diff; websocket_init.idl; ChromeStatus API record +
+ +
+

why it exists

+

whatwg/websockets issue #42 (open since 2015) collects long-standing requests to make the WebSocket constructor extensible the way fetch() is: headers, cache mode, and other per-connection knobs have nowhere to go when the only extra parameter is a protocols list. The option bag itself doesn't add those capabilities — it adds the place for them. Chromium's immediate use is targetAddressSpace, needed so pages can opt WebSocket connections into Local Network Access checks the way fetch(url, { targetAddressSpace }) already can; that member is tracked as its own Chrome 154 feature (Support targetAddressSpace option for WebSockets) and rides on this extension point.

+ Source: issue #42; blink-dev Intent to Prototype (2026-08-05); chromestatus 4779920606756864 +
+ +
+

how the second argument is routed

+

The constructor's second parameter becomes the three-way union (DOMString or sequence<DOMString> or WebSocketInit). Which branch a value takes follows the Web IDL union conversion algorithm, so the routing is fully deterministic:

+
+ + + + + + + + +
You passBranch takenResulting protocols
"soap" (string)DOMString — unchanged classic form["soap"]
["soap", "wamp"] (array, or any iterable object)sequence<DOMString> — unchanged classic form["soap", "wamp"]
{ protocols: ["soap"] } (non-iterable object)WebSocketInit dictionary — new["soap"]
{} (empty object)WebSocketInit with defaults[] (none)
undefined / omittedparameter default [][] (none)
{ protocols: "soap" } (bare string member)WebSocketInit, then TypeError— see the member's type
+
+

The spec PR's constructor step is a two-liner (quoted from the diff): “If protocolsOrOptions is a WebSocketInit dictionary, set protocols to protocolsOrOptions["protocols"]. Otherwise, set protocols to protocolsOrOptions.” Everything downstream — protocol validation, the opening handshake — is the existing algorithm.

+

In Chromium, dom_websocket.cc branches on RuntimeEnabledFeatures::WebSocketOptionBagEnabled(): with the flag on, the argument converts through the generated union V8UnionStringOrStringSequenceOrWebSocketInit and ParseConstructorOptionsWithBag() reads the dictionary; with the flag off, the pre-existing two-way union applies and an object coerces to a (rejected) protocol string. The declared IDL parameter is temporarily any so the two behaviors can coexist (websocket.idl, TODO note referencing crbug.com/542670554).

+ Source: Web IDL § ES union conversion; PR #76 diff; dom_websocket.cc; websocket.idl +
+ +
+

syntax

+

Proposed spec IDL (whatwg/websockets PR #76, open — quoted verbatim from the diff, 2026-09-20):

+
dictionary WebSocketInit {
+  sequence<DOMString> protocols = [];
+};
+
+[Exposed=(Window,Worker)]
+interface WebSocket : EventTarget {
+  constructor(USVString url,
+              optional (DOMString or sequence<DOMString> or WebSocketInit) protocolsOrOptions = []);
+  // … rest of the interface unchanged
+};
+

Shipping Chromium dictionary IDL (websocket_init.idl at trunk, 2026-09-20 — quoted verbatim, including the flag-gated Chromium extension):

+
dictionary WebSocketInit {
+    sequence<DOMString> protocols = [];
+    [RuntimeEnabled=LocalNetworkAccessWebSocketsTargetAddressSpace] IPAddressSpace targetAddressSpace;
+};
+

The WebSocket living standard does not yet contain WebSocketInit (0 occurrences, verified 2026-09-20) — PR #76 must merge first. Full member documentation: the constructor overload and the WebSocketInit dictionary.

+ Source: PR #76 diff; websocket_init.idl; websockets.spec.whatwg.org +
+ +
+

reference

+ +
+ +
+

examples

+

Both spellings of the same connection — the classic positional form and the option bag:

+
// Classic (unchanged, works everywhere):
+const a = new WebSocket("wss://example.com/chat", "soap");
+const b = new WebSocket("wss://example.com/chat", ["soap", "wamp"]);
+
+// Option bag (Chrome 154 behind the WebSocketOptionBag flag):
+const c = new WebSocket("wss://example.com/chat", { protocols: ["soap"] });
+
+// NOTE: the member is a sequence — a bare string throws a TypeError:
+// new WebSocket("wss://example.com/chat", { protocols: "soap" });  // TypeError
+

Negotiation is unchanged: the server picks one offered subprotocol, and the chosen one appears on socket.protocol after open, exactly as with the positional forms.

+
+ +
Live example from the Chrome Platform Showcase (route HEAD-checked 200, 2026-09-20). Sibling concept demos: subprotocol negotiation and why a dictionary. The demo needs the WebSocketOptionBag flag to exercise the new form.Source: chrome-platform-showcase
+
+ Source: forms per the PR #76 IDL and the WPT option-bag tests; the TypeError note is gendn-derived from Web IDL sequence conversion (labelled accordingly) +
+ +
+

browser compatibility

+

Interim table. BCD has no key for the option-bag overload (the WebSocket() constructor entry carries only the host constructor and the relative-URL subfeature, verified 2026-09-20), and webstatus.dev has no web-feature for it. Rows compiled from the linked primary sources.

+
+ + + + + +
BrowserSupportEvidence
Chrome / Edge (Chromium)Implemented; behind WebSocketOptionBag (experimental) — stable promotion targeted at 154, CL open as of 2026-09-20CL 8197225 (merged 2026-08-12); flag record; CL 8256918
FirefoxNo signalChromeStatus API record vendor views; position requested at mozilla/standards-positions #1444
SafariPositive standards position (“Support”); not shippedWebKit/standards-positions #708; ChromeStatus API record
+
+

For orientation, the host WebSocket() constructor itself is Baseline widely available (Chrome 5+, Edge 12+, Firefox 11+, Safari 5+ per BCD) — only the dictionary overload is new.

+ Source: as linked per row; webstatus.dev websockets feature +
+ +
+

security and privacy

+
    +
  • No new network capability — the option bag changes how subprotocols are spelled, not what a page can connect to. The same protocol-validation and handshake algorithm of the living standard runs for every branch of the union.
  • +
  • Web-compat, not security, is the open risk — code that today passes a non-iterable object (coerced to "[object Object]", then rejected with SyntaxError) would begin connecting with no subprotocols. The spec PR review flagged this pattern in shipping libraries and asked for usage measurement before merging; Chromium's flag gating (and the abandoned revert, CL 8294944) reflect the same caution.
  • +
  • The extension point is where security options will land — Chromium's targetAddressSpace member (flag-gated in websocket_init.idl) exists to let pages opt in to Local Network Access restrictions for WebSocket connections to private networks; it is tracked as its own feature.
  • +
+ Source: PR #76 review thread; Local Network Access spec; websocket_init.idl +
+ +
+

specifications

+
+ + + + + + +
DocumentStatus
whatwg/websockets PR #76 — Add WebSocketInit option to constructorOpen, unmerged (last updated 2026-09-14, checked 2026-09-20) — the normative text this page quotes; review is measuring the object-coercion web-compat risk
whatwg/websockets issue #42 — Extensibility of the WebSocket constructorThe originating proposal (2015–) and de-facto explainer; records implementer and user demand
WebSockets living standardHost specification; does not yet contain WebSocketInit (0 occurrences, verified 2026-09-20)
blink-dev — Intent to Prototype: WebSocket constructor option bagPosted 2026-08-05; tracking bug 542670554. No Intent-to-Ship thread is linked from the ChromeStatus record as of 2026-09-20
+
+ Source: repository and thread contents fetched 2026-09-20 +
+ +
+

see also

+ +
+ + +
+ + diff --git a/v154/add-options-bag-to-websocket-constructor/reference-contract.json b/v154/add-options-bag-to-websocket-constructor/reference-contract.json new file mode 100644 index 0000000..0306f6a --- /dev/null +++ b/v154/add-options-bag-to-websocket-constructor/reference-contract.json @@ -0,0 +1,405 @@ +{ + "schemaVersion": 1, + "id": "v154/add-options-bag-to-websocket-constructor", + "route": "/v154/add-options-bag-to-websocket-constructor/", + "kind": "web-api", + "completeness": "implementation-sufficient", + "assessedAt": "2026-09-20", + "assessor": "writer self-assessment; independent integrator review pending", + "sources": [ + { + "id": "spec-pr", + "label": "whatwg/websockets PR #76 — Add WebSocketInit option to constructor (diff)", + "url": "https://github.com/whatwg/websockets/pull/76/files", + "kind": "normative-spec" + }, + { + "id": "spec-pr-thread", + "label": "whatwg/websockets PR #76 — review thread (web-compat measurement)", + "url": "https://github.com/whatwg/websockets/pull/76", + "kind": "normative-spec" + }, + { + "id": "issue-42", + "label": "whatwg/websockets issue #42 — Extensibility of the WebSocket constructor", + "url": "https://github.com/whatwg/websockets/issues/42", + "kind": "explainer" + }, + { + "id": "ws-spec", + "label": "WebSockets living standard (host spec; no WebSocketInit yet)", + "url": "https://websockets.spec.whatwg.org/", + "kind": "normative-spec" + }, + { + "id": "webidl-union", + "label": "Web IDL — ES union conversion", + "url": "https://webidl.spec.whatwg.org/#es-union", + "kind": "normative-spec" + }, + { + "id": "webidl-sequence", + "label": "Web IDL — ES sequence conversion", + "url": "https://webidl.spec.whatwg.org/#es-sequence", + "kind": "normative-spec" + }, + { + "id": "webidl-dictionary", + "label": "Web IDL — ES dictionary conversion", + "url": "https://webidl.spec.whatwg.org/#es-dictionary", + "kind": "normative-spec" + }, + { + "id": "chromium-idl", + "label": "Chromium websocket.idl (constructor, temporarily `any`)", + "url": "https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/modules/websockets/websocket.idl", + "kind": "web-idl" + }, + { + "id": "chromium-init-idl", + "label": "Chromium websocket_init.idl (WebSocketInit dictionary)", + "url": "https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/modules/websockets/websocket_init.idl", + "kind": "web-idl" + }, + { + "id": "chromium-cc", + "label": "Chromium dom_websocket.cc (flag branch + ParseConstructorOptionsWithBag)", + "url": "https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/modules/websockets/dom_websocket.cc", + "kind": "implementation" + }, + { + "id": "flag-record", + "label": "Chromium runtime_enabled_features.json5 — WebSocketOptionBag (experimental)", + "url": "https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/platform/runtime_enabled_features.json5", + "kind": "browser-data" + }, + { + "id": "cl-impl", + "label": "Chromium CL 8197225 — Implement WebSocket constructor option bag (merged 2026-08-12)", + "url": "https://chromium-review.googlesource.com/c/chromium/src/+/8197225", + "kind": "implementation" + }, + { + "id": "cl-promote", + "label": "Chromium CL 8256918 — Promote WebSocketOptionBag to stable (open)", + "url": "https://chromium-review.googlesource.com/c/chromium/src/+/8256918", + "kind": "implementation" + }, + { + "id": "intent-prototype", + "label": "blink-dev — Intent to Prototype: WebSocket constructor option bag", + "url": "https://groups.google.com/a/chromium.org/g/blink-dev/c/YwkXWzPUJ7U", + "kind": "intent" + }, + { + "id": "chromestatus-api", + "label": "ChromeStatus API record 5080055102439424", + "url": "https://chromestatus.com/api/v0/features/5080055102439424", + "kind": "browser-data" + }, + { + "id": "milestone-listing", + "label": "ChromeStatus milestone=154 listing", + "url": "https://chromestatus.com/api/v0/features?milestone=154", + "kind": "browser-data" + }, + { + "id": "bcd", + "label": "BCD api/WebSocket.json (no option-bag key)", + "url": "https://github.com/mdn/browser-compat-data/blob/main/api/WebSocket.json", + "kind": "browser-data" + }, + { + "id": "webstatus", + "label": "webstatus.dev — websockets host feature (Baseline widely)", + "url": "https://api.webstatus.dev/v1/features/websockets", + "kind": "browser-data" + }, + { + "id": "wpt", + "label": "WPT websockets/constructor/option-bag.any.js", + "url": "https://github.com/web-platform-tests/wpt/blob/master/websockets/constructor/option-bag.any.js", + "kind": "test" + }, + { + "id": "webkit-position", + "label": "WebKit standards-positions #708 (Support)", + "url": "https://github.com/WebKit/standards-positions/issues/708", + "kind": "browser-data" + }, + { + "id": "mozilla-position", + "label": "mozilla standards-positions #1444 (no signal)", + "url": "https://github.com/mozilla/standards-positions/issues/1444", + "kind": "browser-data" + }, + { + "id": "lna-spec", + "label": "Local Network Access (WICG) — targetAddressSpace", + "url": "https://wicg.github.io/local-network-access/", + "kind": "normative-spec" + }, + { + "id": "cps", + "label": "Chrome Platform Showcase — feature demos", + "url": "https://chrome-platform-showcase.paulkinlan-ea.deno.net/v154/add-options-bag-to-websocket-constructor/", + "kind": "other" + } + ], + "inventory": [ + { + "id": "constructor-overload", + "name": "WebSocket(url, protocolsOrOptions) — the widened constructor", + "kind": "constructor", + "sourceRefs": ["spec-pr", "chromium-idl", "chromium-cc"] + }, + { + "id": "websocketinit-dictionary", + "name": "WebSocketInit dictionary", + "kind": "dictionary", + "sourceRefs": ["spec-pr", "chromium-init-idl"] + }, + { + "id": "websocketinit-protocols", + "name": "WebSocketInit.protocols member", + "kind": "field", + "sourceRefs": ["spec-pr", "chromium-init-idl", "webidl-sequence"] + }, + { + "id": "argument-routing", + "name": "Second-argument union routing (string vs sequence vs dictionary, and the flag-off coercion)", + "kind": "algorithm", + "sourceRefs": ["webidl-union", "spec-pr", "chromium-cc"] + } + ], + "documentation": [ + { + "inventoryId": "constructor-overload", + "href": "websocket-constructor/", + "dimensions": { + "syntax": { + "status": "documented", + "selector": "#syntax", + "sourceRefs": ["spec-pr", "chromium-idl"] + }, + "inputs": { + "status": "documented", + "selector": "#inputs", + "sourceRefs": ["spec-pr", "webidl-union", "ws-spec"] + }, + "outputs": { + "status": "documented", + "selector": "#outputs", + "sourceRefs": ["ws-spec", "chromium-cc"] + }, + "errors": { + "status": "documented", + "selector": "#errors", + "sourceRefs": ["ws-spec", "webidl-sequence"] + }, + "context": { + "status": "documented", + "selector": "#context", + "sourceRefs": ["spec-pr", "chromium-idl"] + }, + "lifecycle": { + "status": "documented", + "selector": "#lifecycle", + "sourceRefs": ["chromium-cc", "spec-pr"] + }, + "examples": { + "status": "documented", + "selector": "#examples", + "sourceRefs": ["spec-pr", "wpt"] + }, + "compatibility": { + "status": "documented", + "selector": "#compatibility", + "sourceRefs": [ + "cl-impl", + "flag-record", + "cl-promote", + "chromestatus-api", + "bcd", + "webkit-position", + "mozilla-position" + ] + }, + "securityPrivacy": { + "status": "documented", + "selector": "#security-and-privacy", + "sourceRefs": ["spec-pr-thread"] + } + } + }, + { + "inventoryId": "websocketinit-dictionary", + "href": "websocketinit/", + "dimensions": { + "syntax": { + "status": "documented", + "selector": "#syntax", + "sourceRefs": ["spec-pr", "chromium-init-idl"] + }, + "inputs": { + "status": "documented", + "selector": "#inputs", + "sourceRefs": ["webidl-dictionary", "spec-pr"] + }, + "outputs": { + "status": "documented", + "selector": "#outputs", + "sourceRefs": ["wpt", "ws-spec"] + }, + "errors": { + "status": "documented", + "selector": "#errors", + "sourceRefs": ["webidl-sequence", "webidl-dictionary", "ws-spec"] + }, + "context": { + "status": "documented", + "selector": "#context", + "sourceRefs": ["chromium-init-idl", "flag-record"] + }, + "lifecycle": { + "status": "documented", + "selector": "#lifecycle", + "sourceRefs": ["chromium-cc"] + }, + "examples": { + "status": "documented", + "selector": "#examples", + "sourceRefs": ["spec-pr", "webidl-dictionary", "wpt"] + }, + "compatibility": { + "status": "documented", + "selector": "#compatibility", + "sourceRefs": [ + "cl-impl", + "flag-record", + "cl-promote", + "chromestatus-api", + "bcd", + "webkit-position", + "mozilla-position" + ] + }, + "securityPrivacy": { + "status": "documented", + "selector": "#security-and-privacy", + "sourceRefs": ["spec-pr-thread", "lna-spec"] + } + } + }, + { + "inventoryId": "websocketinit-protocols", + "href": "websocketinit/", + "dimensions": { + "syntax": { + "status": "documented", + "selector": "#protocols", + "sourceRefs": ["spec-pr", "webidl-sequence"] + }, + "inputs": { + "status": "documented", + "selector": "#protocols", + "sourceRefs": ["spec-pr", "ws-spec"] + }, + "outputs": { + "status": "documented", + "selector": "#outputs", + "sourceRefs": ["wpt", "ws-spec"] + }, + "errors": { + "status": "documented", + "selector": "#errors", + "sourceRefs": ["webidl-sequence", "ws-spec"] + }, + "context": { + "status": "documented", + "selector": "#context", + "sourceRefs": ["chromium-init-idl", "flag-record"] + }, + "lifecycle": { + "status": "documented", + "selector": "#lifecycle", + "sourceRefs": ["chromium-cc"] + }, + "examples": { + "status": "documented", + "selector": "#examples", + "sourceRefs": ["spec-pr", "wpt"] + }, + "compatibility": { + "status": "documented", + "selector": "#compatibility", + "sourceRefs": ["cl-impl", "flag-record", "chromestatus-api", "bcd"] + }, + "securityPrivacy": { + "status": "documented", + "selector": "#security-and-privacy", + "sourceRefs": ["spec-pr-thread"] + } + } + }, + { + "inventoryId": "argument-routing", + "href": "#how-it-works", + "dimensions": { + "syntax": { + "status": "documented", + "selector": "#syntax", + "sourceRefs": ["spec-pr", "chromium-init-idl"] + }, + "inputs": { + "status": "documented", + "selector": "#how-it-works", + "sourceRefs": ["webidl-union", "spec-pr"] + }, + "outputs": { + "status": "documented", + "selector": "#how-it-works", + "sourceRefs": ["spec-pr", "chromium-cc"] + }, + "errors": { + "status": "documented", + "selector": "#how-it-works", + "sourceRefs": ["webidl-union", "webidl-sequence"] + }, + "context": { + "status": "documented", + "selector": "#how-it-works", + "sourceRefs": ["chromium-cc", "chromium-idl"] + }, + "lifecycle": { + "status": "documented", + "selector": "#how-it-works", + "sourceRefs": ["chromium-cc"] + }, + "examples": { + "status": "documented", + "selector": "#examples", + "sourceRefs": ["spec-pr", "wpt", "webidl-sequence"] + }, + "compatibility": { + "status": "documented", + "selector": "#compatibility", + "sourceRefs": [ + "bcd", + "webstatus", + "cl-impl", + "flag-record", + "cl-promote", + "chromestatus-api" + ] + }, + "securityPrivacy": { + "status": "documented", + "selector": "#security-and-privacy", + "sourceRefs": ["spec-pr-thread", "lna-spec"] + } + } + } + ], + "notes": "Runtime behavior is source-derived, not executed (needs a Chromium build with --enable-blink-features=WebSocketOptionBag). Spec PR #76 is open (last updated 2026-09-14); IDL quoted verbatim from its diff and from Chromium trunk at fetch time (2026-09-20). The milestone=154 listing files the feature under 'Enabled by default' but the flag is still experimental at trunk and the stable-promotion CL 8256918 is open — the pages state this discrepancy rather than smoothing it over." +} diff --git a/v154/add-options-bag-to-websocket-constructor/websocket-constructor/index.html b/v154/add-options-bag-to-websocket-constructor/websocket-constructor/index.html new file mode 100644 index 0000000..f40a0a3 --- /dev/null +++ b/v154/add-options-bag-to-websocket-constructor/websocket-constructor/index.html @@ -0,0 +1,155 @@ + + + + + + WebSocket() constructor (options bag overload) — Chrome 154 — gendn + + + + +
+

← Add options bag to WebSocket constructor

+ +
+

v154 · experimental · constructor

+

WebSocket(url, protocols­OrOptions)

+

The WebSocket() constructor with its widened second parameter: a subprotocol string, an array of subprotocol strings, or — new in this feature — a WebSocketInit options dictionary. Connection semantics are otherwise unchanged from the classic constructor. Implemented in Chromium behind the experimental WebSocketOptionBag flag, targeted at Chrome 154.

+
+ +
+ Experimental +

Behind --enable-blink-features=WebSocketOptionBag (or chrome://flags/#enable-experimental-web-platform-features); the stable-promotion CL 8256918 and the defining spec change whatwg/websockets PR #76 are both open as of 2026-09-20. With the flag off, an options object coerces to the string "[object Object]" and the constructor throws SyntaxError. See the overview warn-block for a feature-detection pattern.

+
+ +
+

syntax

+
new WebSocket(url)
+new WebSocket(url, protocols)        // string or array — classic form
+new WebSocket(url, options)          // WebSocketInit dictionary — new
+

Proposed spec IDL (whatwg/websockets PR #76, quoted verbatim):

+
constructor(USVString url,
+            optional (DOMString or sequence<DOMString> or WebSocketInit) protocolsOrOptions = []);
+

Shipping Chromium IDL (websocket.idl at trunk, 2026-09-20 — quoted verbatim; the parameter is temporarily any so behavior can differ by flag state):

+
// The type of `protocolsOrOptions` is `any` because the type needs to
+// differ depending on the setting of the "WebSocketOptionBag" feature.
+// TODO(crbug.com/542670554): Change to `(DOMString or sequence<DOMString> or
+// WebSocketInit)` when the "WebSocketOptionBag" feature is permanently
+// enabled and removed.
+[CallWith=ExecutionContext, RaisesException]
+constructor(USVString url, optional any protocolsOrOptions);
+ Source: PR #76 diff; websocket.idl; crbug.com/542670554 +
+ +
+

parameters

+
+ + + +
urlUSVString — the endpoint. Unchanged by this feature: ws, wss, http, and https schemes are allowed, fragments are not (per the living standard)
protocolsOrOptions (optional)One of: a subprotocol string (equivalent to a one-element array), a sequence of strings (any iterable of strings, most commonly an array), or a WebSocketInit dictionary whose protocols member supplies the same list. Defaults to [] (offer no subprotocol). Union routing follows Web IDL union conversion: strings take the DOMString branch, iterable objects the sequence branch, other objects the dictionary branch, and undefined the parameter default
+
+ Source: PR #76 diff; Web IDL § ES union conversion; living standard +
+ +
+

return value

+

A new WebSocket object in the CONNECTING state, exactly as the classic constructor returns; the connection is established asynchronously and open fires when the handshake (including subprotocol negotiation) completes. The server-selected subprotocol is then readable on socket.protocol. Which spelling supplied the protocols list is not observable on the returned object — { protocols: ["soap"] } and ["soap"] produce identical connections (in Chromium both feed the same Vector<String> handed to Connect() in dom_websocket.cc).

+ Source: living standard (connection algorithm); dom_websocket.cc +
+ +
+

errors

+
+ + + + +
SyntaxError DOMExceptionUnchanged host-constructor errors: a URL with a disallowed scheme or a fragment, or an invalid/duplicate subprotocol name — whichever branch supplied it. With the flag off, passing an options object lands here too: it coerces to "[object Object]", which is not a valid subprotocol token
TypeErrorA WebSocketInit whose protocols member is not an iterable object of strings — including the tempting bare string { protocols: "soap" } — fails Web IDL sequence conversion before any network activity
SecurityError DOMExceptionUnchanged: ports blocked by the user agent (per the living standard)
+
+ Source: living standard (constructor exceptions); Web IDL § sequence conversion +
+ +
+

context and exposure

+

Exposed wherever WebSocket is exposed: Window and Worker contexts ([Exposed=(Window,Worker)] in both the spec PR and Chromium's IDL). The overload requires no permission, secure-context, or policy gate of its own — only the WebSocketOptionBag runtime flag in Chromium. The dictionary branch simply does not exist when the flag is off; there is no partial state.

+ Source: PR #76 diff; websocket.idl +
+ +
+

lifecycle

+
    +
  1. Script calls the constructor; the second argument is routed by union conversion (in Chromium: V8UnionStringOrStringSequenceOrWebSocketInit::Create, then ParseConstructorOptionsWithBag() in dom_websocket.cc).
  2. +
  3. A dictionary's protocols list replaces the positional list; all later steps are the classic algorithm: validate protocols, open the handshake, fire open/error/close.
  4. +
  5. The choice of spelling has no further lifecycle effect — reconnection, buffering, close(), and events behave identically.
  6. +
+ Source: dom_websocket.cc; PR #76 diff (“Otherwise, set protocols to protocolsOrOptions”) +
+ +
+

examples

+
// Feature-detect, then prefer the extensible form:
+let socket;
+try {
+  socket = new WebSocket("wss://example.com/feed", { protocols: ["v2.feed"] });
+} catch {
+  // Older engine: object was coerced to "[object Object]" → SyntaxError.
+  socket = new WebSocket("wss://example.com/feed", ["v2.feed"]);
+}
+
+socket.addEventListener("open", () => {
+  console.log("negotiated subprotocol:", socket.protocol); // "v2.feed" or ""
+});
+ Source: gendn-derived from the PR #76 semantics and the WPT negotiation test (labelled accordingly) +
+ +
+

browser compatibility

+

Interim table. No BCD key for the overload (verified 2026-09-20); rows compiled from primary sources.

+
+ + + + + +
BrowserSupportEvidence
Chrome / Edge (Chromium)Implemented behind WebSocketOptionBag (experimental); stable promotion targeted at 154, pending as of 2026-09-20CL 8197225; flag record; CL 8256918
FirefoxNo signalChromeStatus API record; mozilla/standards-positions #1444
SafariPositive position, not shippedWebKit/standards-positions #708
+
+ Source: as linked per row +
+ +
+

security and privacy

+

No new capability: the overload cannot reach any endpoint, port, or protocol the classic constructor cannot. The one behavioral delta is web-compat — a non-iterable object argument stops throwing SyntaxError and starts meaning “empty options”; the spec PR review is measuring how much shipping code relies on the old rejection before the change merges. Future dictionary members are individually security-reviewed (see targetAddressSpace on the WebSocketInit page).

+ Source: PR #76 review thread +
+ +
+

see also

+ +
+ + +
+ + diff --git a/v154/add-options-bag-to-websocket-constructor/websocketinit/index.html b/v154/add-options-bag-to-websocket-constructor/websocketinit/index.html new file mode 100644 index 0000000..2e51208 --- /dev/null +++ b/v154/add-options-bag-to-websocket-constructor/websocketinit/index.html @@ -0,0 +1,164 @@ + + + + + + WebSocketInit dictionary — Chrome 154 — gendn + + + + +
+

← Add options bag to WebSocket constructor

+ +
+

v154 · experimental · dictionary

+

WebSocketInit

+

The options dictionary the WebSocket() constructor accepts as its second argument. The proposed standard shape has a single member, protocols; Chromium's IDL additionally declares a flag-gated targetAddressSpace extension. The dictionary's real payload is extensibility: future connection options get named members here instead of new positional parameters.

+
+ +
+ Experimental +

Behind --enable-blink-features=WebSocketOptionBag (or chrome://flags/#enable-experimental-web-platform-features). The defining spec change, whatwg/websockets PR #76, is open as of 2026-09-20, so the dictionary's name and members can still change in review. See the overview warn-block for feature detection.

+
+ +
+

syntax

+

Proposed spec IDL (whatwg/websockets PR #76, quoted verbatim from the diff, 2026-09-20):

+
dictionary WebSocketInit {
+  sequence<DOMString> protocols = [];
+};
+

Shipping Chromium IDL (websocket_init.idl at trunk, 2026-09-20 — quoted verbatim):

+
dictionary WebSocketInit {
+    sequence<DOMString> protocols = [];
+    [RuntimeEnabled=LocalNetworkAccessWebSocketsTargetAddressSpace] IPAddressSpace targetAddressSpace;
+};
+ Source: PR #76 diff; websocket_init.idl +
+ +
+

the protocols member

+
+ + + + + +
Typesequence<DOMString> — strictly a sequence. A bare string is a TypeError: Web IDL sequence conversion requires an iterable object, so { protocols: "soap" } throws while the positional second argument "soap" still works (the union's DOMString branch handles that case, not this member). Write { protocols: ["soap"] }. Note the ChromeStatus summary's { protocols: "soap" } example does not match the IDL as written in either the spec PR or Chromium
Default[] — offer no subprotocol, exactly like omitting the classic second argument
MeaningThe list of subprotocol names offered in the opening handshake's Sec-WebSocket-Protocol header; validation and negotiation are the classic algorithm of the living standard (invalid or duplicate names still reject with SyntaxError)
EquivalencePer the PR's constructor step, the member's value simply becomes the protocols list — { protocols: x } and positional x (array form) are indistinguishable downstream
+
+ Source: PR #76 diff; Web IDL § sequence conversion; living standard; ChromeStatus record +
+ +
+

the targetAddressSpace member (Chromium extension)

+

Chromium's dictionary declares a second member, targetAddressSpace (an IPAddressSpace: "public", "private", or "local"), gated behind its own LocalNetworkAccessWebSocketsTargetAddressSpace runtime flag in the flag registry. It mirrors fetch()'s option of the same name from the Local Network Access spec and is tracked as its own Chrome 154 feature, Support targetAddressSpace option for WebSockets — it is documented there, not here; this page records only that the extension point is already in use. It is not part of PR #76.

+ Source: websocket_init.idl; Local Network Access; chromestatus 4779920606756864 +
+ +
+

inputs

+

The dictionary is pure input: script constructs it and passes it to the constructor; nothing ever hands a WebSocketInit back to script. Unknown properties on the object are ignored per standard Web IDL dictionary conversion (only declared members are read), so a typo like { protocol: "soap" } silently offers no subprotocol rather than erroring — check socket.protocol after open if negotiation matters.

+ Source: Web IDL § dictionary conversion; PR #76 diff +
+ +
+

outputs

+

No direct output. The dictionary's observable effect is the offered-subprotocol set in the handshake and, after negotiation, the value of socket.protocol (the server's chosen subprotocol, or the empty string). The WPT tests assert exactly this: an empty bag connects successfully with no subprotocol, and { protocols: ['foobar', 'foobar2'] } yields ws.protocol === 'foobar' against an echo server that accepts the first offer.

+ Source: WPT option-bag.any.js; living standard +
+ +
+

errors

+
+ + + + +
TypeErrorprotocols set to a non-iterable value (a bare string, a number, null): sequence conversion fails during argument processing, before any network activity
SyntaxError DOMExceptionSequence conversion succeeded but a name is not a valid subprotocol token, or the list contains duplicates — the classic validation of the living standard, identical for positional and dictionary spellings
Silently ignoredUnknown members (including typos) — standard dictionary conversion reads only declared members
+
+ Source: Web IDL § sequence; § dictionary; living standard +
+ +
+

context and exposure

+

Available wherever the constructor is: Window and Worker contexts, when Chromium's WebSocketOptionBag runtime feature is enabled (flag record: status: "experimental", 2026-09-20). As a dictionary it has no prototype or global-object presence — there is nothing to feature-detect on window; detection goes through constructor behavior (see the overview).

+ Source: websocket_init.idl; runtime_enabled_features.json5 +
+ +
+

lifecycle

+

The dictionary is read once, synchronously, during the constructor call (ParseOptionBag() in dom_websocket.cc copies protocols into the connection's protocol vector). Mutating the object afterwards has no effect on the connection; the object holds no live relationship to the socket. Future members (like targetAddressSpace) follow the same read-once model.

+ Source: dom_websocket.cc +
+ +
+

examples

+
// Correct: protocols is a sequence.
+const ws = new WebSocket("wss://example.com/chat", {
+  protocols: ["chat.v2", "chat.v1"],
+});
+
+ws.addEventListener("open", () => {
+  if (ws.protocol === "") {
+    // Server accepted the connection without choosing a subprotocol.
+  }
+});
+
+// TypeError — a bare string is not a sequence:
+// new WebSocket("wss://example.com/chat", { protocols: "chat.v2" });
+
+// Silent no-op — unknown members are ignored (typo offers NO subprotocol):
+// new WebSocket("wss://example.com/chat", { protocol: "chat.v2" });
+ Source: gendn-derived from the PR #76 IDL and Web IDL dictionary semantics (labelled accordingly); array form matches the WPT tests +
+ +
+

browser compatibility

+

Interim table. No BCD key for the dictionary (verified 2026-09-20); rows compiled from primary sources.

+
+ + + + + +
BrowserSupportEvidence
Chrome / Edge (Chromium)Implemented behind WebSocketOptionBag (experimental); stable promotion targeted at 154, pending as of 2026-09-20CL 8197225; flag record; CL 8256918
FirefoxNo signalChromeStatus API record; mozilla/standards-positions #1444
SafariPositive position, not shippedWebKit/standards-positions #708
+
+ Source: as linked per row +
+ +
+

security and privacy

+

The dictionary grants no capability of its own: protocols feeds the same validated handshake header as the classic argument. Two considerations are real: the web-compat shift (objects stop throwing and start meaning “empty options” — under measurement on the spec PR), and the fact that future members carry their own security surface — the first, targetAddressSpace, exists precisely to integrate WebSockets with Local Network Access protections and is security-reviewed as its own feature.

+ Source: PR #76 review thread; Local Network Access +
+ +
+

see also

+ +
+ + +
+ + From 2c16c4dc415d4dde33e8763bb2fd948d9efe92f9 Mon Sep 17 00:00:00 2001 From: Paul Kinlan Date: Sun, 20 Sep 2026 06:12:43 +0000 Subject: [PATCH 2/6] v154: MDN stub for CSS text-decoration-inset MDN stub: MDN's text-decoration-inset reference is substantive (and the ChromeStatus record links it), so gendn points there and records the shipping metadata MDN doesn't track: milestone-154 listing bucket vs the still-experimental CSSTextDecorationInset flag, BCD (Chrome false / Firefox 146 / Safari TP), no webstatus.dev entry, CL 7748204, showcase route. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_017hFhoS24xY2u3ctcHNkeNR --- .../conformance.json | 191 ++++++++++++++++++ v154/css-text-decoration-inset/index.html | 80 ++++++++ 2 files changed, 271 insertions(+) create mode 100644 v154/css-text-decoration-inset/conformance.json create mode 100644 v154/css-text-decoration-inset/index.html diff --git a/v154/css-text-decoration-inset/conformance.json b/v154/css-text-decoration-inset/conformance.json new file mode 100644 index 0000000..36ab100 --- /dev/null +++ b/v154/css-text-decoration-inset/conformance.json @@ -0,0 +1,191 @@ +{ + "schemaVersion": 1, + "id": "v154/css-text-decoration-inset", + "route": "/v154/css-text-decoration-inset/", + "identity": "5178263526834176", + "milestone": 154, + "status": "stub", + "demo": "https://chrome-platform-showcase.paulkinlan-ea.deno.net/v154/css-text-decoration-inset/", + "cpsFeature": { + "host": "chrome-platform-showcase.paulkinlan-ea.deno.net", + "route": "/v154/css-text-decoration-inset/", + "conformanceRoute": "/v154/css-text-decoration-inset/conformance", + "note": "The chrome-platform-showcase suite at conformanceRoute governs only the assertions it explicitly lists; it does not prove unasserted native, hardware, permission, backend, or other behavior. gendn references that listed contract without forking it." + }, + "immutable": true, + "suiteHash": "cdee134d85a66ea37af7d0b780fcb6e8fa783b9551fc0851a7000802c9302974", + "generatedAt": "2026-07-19T00:00:00Z", + "author": "gen-conformance/v1 (derived from page metadata)", + "assertions": [ + { + "id": "route-resolves", + "category": "route", + "describe": "The stable route /v154/css-text-decoration-inset/ responds 200 (durable-demo contract).", + "kind": "http-status", + "test": "/v154/css-text-decoration-inset/", + "expect": 200, + "deviceClass": "both" + }, + { + "id": "chromestatus-identity-link", + "category": "identity", + "describe": "Page carries its canonical chromestatus.com/feature/5178263526834176 link — the immutable feature identity (CLAUDE.md invariant #3) and the milestone-154 mapping's self-heal.", + "kind": "dom-query", + "selector": "a[href*=\"chromestatus.com/feature/5178263526834176\"]", + "deviceClass": "both" + }, + { + "id": "title-nonempty", + "category": "structure", + "describe": "Document has a non-empty for the reference page.", + "kind": "js-eval", + "test": "document.title.trim().length > 0", + "deviceClass": "both" + }, + { + "id": "single-h1", + "category": "structure", + "describe": "Exactly one <h1> — the API name (listing name), MDN-shaped article heading.", + "kind": "js-eval", + "test": "document.querySelectorAll('h1').length === 1", + "deviceClass": "both" + }, + { + "id": "main-landmark", + "category": "a11y", + "describe": "A single <main> landmark wraps the article content.", + "kind": "js-eval", + "test": "document.querySelectorAll('main').length === 1", + "deviceClass": "both" + }, + { + "id": "stylesheet-linked", + "category": "structure", + "describe": "Shared editorial design system /public/styles.css is linked.", + "kind": "dom-query", + "selector": "link[rel=\"stylesheet\"][href=\"/public/styles.css\"]", + "deviceClass": "both" + }, + { + "id": "viewport-meta", + "category": "responsive", + "describe": "Responsive viewport meta present (width=device-width) for mobile+desktop parity.", + "kind": "dom-query", + "selector": "meta[name=\"viewport\"]", + "deviceClass": "both" + }, + { + "id": "crumb-to-release", + "category": "structure", + "describe": "Breadcrumb links back to the /v154/ release index.", + "kind": "dom-query", + "selector": "a[href=\"/v154/\"]", + "deviceClass": "both" + }, + { + "id": "byline-footer", + "category": "structure", + "describe": "Byline footer present (article provenance).", + "kind": "dom-query", + "selector": ".byline", + "deviceClass": "both" + }, + { + "id": "external-links-rel-safe", + "category": "links", + "describe": "Every target=_blank link carries rel=noopener (no reverse-tabnabbing; MDN-style external link hygiene).", + "kind": "js-eval", + "test": "[...document.querySelectorAll('a[target=\"_blank\"]')].every(el => (el.rel||'').includes('noopener'))", + "deviceClass": "both" + }, + { + "id": "no-empty-anchors", + "category": "links", + "describe": "No anchor has an empty or placeholder (#) href.", + "kind": "js-eval", + "test": "[...document.querySelectorAll('a')].every(el => { const h = el.getAttribute('href'); return h && h !== '#'; })", + "deviceClass": "both" + }, + { + "id": "no-console-errors", + "category": "runtime", + "describe": "Page loads with no console errors.", + "kind": "no-console-errors", + "deviceClass": "both" + }, + { + "id": "no-failed-requests", + "category": "runtime", + "describe": "No failed (>=400 / net-error) network requests on load.", + "kind": "no-failed-requests", + "deviceClass": "both" + }, + { + "id": "no-horizontal-overflow-desktop", + "category": "responsive", + "describe": "No unintended horizontal overflow at desktop (1280x800).", + "kind": "js-eval", + "test": "document.documentElement.scrollWidth <= (window.innerWidth + 1)", + "deviceClass": "desktop" + }, + { + "id": "no-horizontal-overflow-mobile", + "category": "responsive", + "describe": "No unintended horizontal overflow at narrow mobile (360x740, DPR3).", + "kind": "js-eval", + "test": "document.documentElement.scrollWidth <= (window.innerWidth + 1)", + "deviceClass": "mobile" + }, + { + "id": "legible-desktop", + "category": "responsive", + "describe": "Desktop render is legible, contrast passes WCAG AA, controls/text not clipped (screenshot review).", + "kind": "manual-evidenced", + "deviceClass": "desktop" + }, + { + "id": "legible-mobile", + "category": "responsive", + "describe": "Mobile render is legible with adequate tap targets and no clipped text/controls (screenshot review).", + "kind": "manual-evidenced", + "deviceClass": "mobile" + }, + { + "id": "h1-matches-listing-name", + "category": "content", + "describe": "The <h1> is the chromestatus milestone-listing name for this feature (not the broader detail name) — factual/source fidelity.", + "kind": "manual-evidenced", + "deviceClass": "n/a" + }, + { + "id": "guidance-consulted", + "category": "guidance", + "describe": "Frontend implementation consulted modern-web-guidance for its UI/responsive/a11y topics and applied or justified them (recorded in _questions.json guidanceConsulted).", + "kind": "manual-evidenced", + "deviceClass": "n/a" + }, + { + "id": "mdn-card-present", + "category": "structure", + "describe": "Stub shows the 'documented on MDN' card explaining gendn doesn't duplicate MDN.", + "kind": "dom-query", + "selector": ".mdn-card", + "deviceClass": "both" + }, + { + "id": "mdn-outbound-link", + "category": "links", + "describe": "Stub links out to the canonical developer.mozilla.org page for the feature.", + "kind": "js-eval", + "test": "!!document.querySelector('a[href*=\"developer.mozilla.org/en-US/docs/Web\"]')", + "deviceClass": "both" + }, + { + "id": "mdn-coverage-accurate", + "category": "content", + "describe": "The linked MDN page genuinely covers this feature (has Specifications + Browser compat) — accurate 'covered on MDN' claim (source review).", + "kind": "manual-evidenced", + "deviceClass": "n/a" + } + ] +} diff --git a/v154/css-text-decoration-inset/index.html b/v154/css-text-decoration-inset/index.html new file mode 100644 index 0000000..df215f9 --- /dev/null +++ b/v154/css-text-decoration-inset/index.html @@ -0,0 +1,80 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>CSS text-decoration-inset — Chrome 154 — gendn + + + + +
+

← Chrome 154 reference

+ +
+

v154 · css · covered on mdn

+

CSS text-decoration-inset

+

A CSS Text Decoration Level 4 property that controls how far underlines, overlines, and line-throughs are inset from — or, with negative values, extended beyond — the edges of the decorated text run. It accepts auto, lengths, and percentages, in one-value (both ends) or two-value (start, end) form, replacing background-gradient and extra-element workarounds for tuned decoration spacing and reveal effects.

+
+ +
+

this feature is documented on MDN

+
+ + MDN: text-decoration-inset CSS property + +

MDN’s reference covers the property in full — value syntax, formal definition and grammar, examples, specifications, and browser compatibility (verified substantive, 2026-09-20; the ChromeStatus record itself links this MDN page as the feature’s documentation). gendn doesn’t duplicate that here.

+
+

Why gendn doesn't duplicate MDN: this site exists for Chrome features that have no MDN reference yet. When MDN covers a feature completely, the honest page is a pointer to the canonical reference plus the shipping metadata MDN doesn't track — duplicated prose would only drift out of sync.

+
+ +
+

quick reference

+
+ + + + + + + + + +
The changeNew longhand property text-decoration-inset: auto | [ auto | <length-percentage> ]{1,2} — one value sets both the start and end offsets of underline/overline/line-through painting; two values set start and end independently (grammar per the css-text-decor-4 draft)
Chrome statusThe milestone=154 listing files it under “Enabled by default” (fetched 2026-09-20; the listing is authoritative for filing). Disclosed: the runtime flag record for CSSTextDecorationInset still reads status: "experimental" at trunk and the detail record’s status text reads “Proposed”, so default-on in 154 stable is a target that may still be mid-flip. Implementation CL: 7748204
SpecCSS Text Decoration Module Level 4 — text-decoration-inset (Editor’s Draft)
MDNtext-decoration-inset (the ChromeStatus record links this same page)
Browser compatibilityBCD css/properties/text-decoration-inset.json (verified 2026-09-20): Chrome false — BCD has not yet recorded a Chrome shipping version (freshness gap vs the 154 listing; disclosed above). Firefox 146. Safari Technology Preview only. Edge mirrors Chrome
BaselineNo own web-feature entry: webstatus.dev returns 404 for text-decoration-inset (queried 2026-09-20); BCD tags it under the parent text-decoration web-feature. Not Baseline — Chrome is only now shipping and Safari is preview-only
ChromeStatus5178263526834176 — CSS text-decoration-inset (blink component Blink>CSS)
Interactive demosChrome Platform Showcase — CSS text-decoration-inset (route HEAD-checked 200, 2026-09-20)
+
+ Source: MDN text-decoration-inset, fetched 2026-09-20; chromestatus.com/feature/5178263526834176; css-text-decor-4 +
+ +
+

references

+ +
+ + +
+ + From 640ca993aded5782ae26d346848bc844fe894b22 Mon Sep 17 00:00:00 2001 From: Paul Kinlan Date: Sun, 20 Sep 2026 06:21:37 +0000 Subject: [PATCH 3/6] v147: MDN-shape upgrade for Lazy loading for video and audio elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In-place quality upgrade (durable contract preserved: same route, same chromestatus identity 5200068565139456). Reason: the page predated the MDN shape and had gone stale — it still claimed dev-trial status while the spec PR merged 2026-03-23 and the attribute shipped (Chrome 148 partial / 150 full per BCD; LazyLoadVideoAndAudio flag now stable). Added: Baseline banner (webstatus loading-lazy-media, limited), formal Syntax section (verbatim enumerated-attribute grammar + HTMLMediaElement IDL reflection), real BCD per-version compat table, caveat (crbug 514611050), poster-deferral row, security/privacy section, live showcase iframe (media-loading-timeline, HEAD 200), linked all previously plain-text citations, mobile overflow protections (.table-wrap etc.). New reference-contract.json + critique; conformance 19 pass / 0 fail; mobile+desktop matrix screenshots read. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_017hFhoS24xY2u3ctcHNkeNR --- goals.json | 29 +- reports/conformance/index.html | 6 +- reports/conformance/responsive.json | 8 +- reports/conformance/results.json | 36 ++- responsive-support.json | 10 +- .../_questions.json | 116 +++++++ .../index.html | 133 +++++--- .../reference-contract.json | 284 ++++++++++++++++++ 8 files changed, 562 insertions(+), 60 deletions(-) create mode 100644 v147/lazy-loading-for-video-and-audio-elements/_questions.json create mode 100644 v147/lazy-loading-for-video-and-audio-elements/reference-contract.json diff --git a/goals.json b/goals.json index 260c277..11e00ca 100644 --- a/goals.json +++ b/goals.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "generatedAt": "2026-09-20T06:11:08.198Z", + "generatedAt": "2026-09-20T06:20:49.183Z", "goals": [ { "id": "v147-css-border-shape-121qq4j", @@ -20,6 +20,15 @@ "status": "open", "createdAt": "2026-07-26" }, + { + "id": "v147-lazy-loading-for-video-and-audio-elements-dr6row", + "sourcePage": "v147/lazy-loading-for-video-and-audio-elements", + "goal": "Apply this same MDN-shape upgrade (banner, syntax, BCD table, showcase embed, linked citations) to the remaining v147 old-shape pages, thinnest first.", + "kind": "additive-page", + "priority": "high", + "status": "open", + "createdAt": "2026-09-20" + }, { "id": "v147-web-printing-api-li2ixo", "sourcePage": "v147/web-printing-api", @@ -245,6 +254,24 @@ "status": "open", "createdAt": "2026-07-19" }, + { + "id": "v147-lazy-loading-for-video-and-audio-elements-6q1aqh", + "sourcePage": "v147/lazy-loading-for-video-and-audio-elements", + "goal": "When Firefox (bugzil.la/2005072) or Safari (webkit.org/b/303995) ships loading on media elements, update the compat table and Baseline banner — the feature may then reach Baseline newly available.", + "kind": "targeted-fix", + "priority": "medium", + "status": "open", + "createdAt": "2026-09-20" + }, + { + "id": "v147-lazy-loading-for-video-and-audio-elements-n70o5f", + "sourcePage": "v147/lazy-loading-for-video-and-audio-elements", + "goal": "Verify the autoplay/play() interaction rows against the merged HTML resource-selection algorithm text and cite the exact spec steps instead of the pre-merge PR.", + "kind": "targeted-fix", + "priority": "medium", + "status": "open", + "createdAt": "2026-09-20" + }, { "id": "v148-prompt-api-1g07j1x", "sourcePage": "v148/prompt-api", diff --git a/reports/conformance/index.html b/reports/conformance/index.html index 67f82d5..a9b013f 100644 --- a/reports/conformance/index.html +++ b/reports/conformance/index.html @@ -22,12 +22,12 @@

Deterministic headless-Chrome run of every reference page's immutable conformance suite. Blocked = manual-evidenced (needs agent review) or genuinely unavailable — never a pass.

1
suites
-
22
assertions
-
17
pass
+
24
assertions
+
19
pass
0
fail
5
blocked
-
pagestatustested/totalpassfailblocked
v154/css-text-decoration-insetstub17/221705
+ v147/lazy-loading-for-video-and-audio-elementsbuilt19/241905
generated by scripts/conformance.mjs
\ No newline at end of file diff --git a/reports/conformance/responsive.json b/reports/conformance/responsive.json index 8d7b4fa..0ff4ae8 100644 --- a/reports/conformance/responsive.json +++ b/reports/conformance/responsive.json @@ -1,20 +1,20 @@ [ { - "id": "v154/css-text-decoration-inset", - "route": "/v154/css-text-decoration-inset/", + "id": "v147/lazy-loading-for-video-and-audio-elements", + "route": "/v147/lazy-loading-for-video-and-audio-elements/", "desktop": { "noOverflow": true, "controlsInView": true, "consoleClean": true, "networkClean": true, - "screenshot": "reports/conformance/shots/v154__css-text-decoration-inset.desktop.png" + "screenshot": "reports/conformance/shots/v147__lazy-loading-for-video-and-audio-elements.desktop.png" }, "mobile": { "noOverflow": true, "controlsInView": true, "consoleClean": true, "networkClean": true, - "screenshot": "reports/conformance/shots/v154__css-text-decoration-inset.mobile.png" + "screenshot": "reports/conformance/shots/v147__lazy-loading-for-video-and-audio-elements.mobile.png" } } ] diff --git a/reports/conformance/results.json b/reports/conformance/results.json index ce5d8b9..3f7a21f 100644 --- a/reports/conformance/results.json +++ b/reports/conformance/results.json @@ -1,18 +1,18 @@ { - "generatedAt": "2026-09-20T06:10:07.167Z", + "generatedAt": "2026-09-20T06:18:41.829Z", "agg": { - "total": 22, - "pass": 17, + "total": 24, + "pass": 19, "fail": 0, "blocked": 5 }, "suites": [ { - "id": "v154/css-text-decoration-inset", - "route": "/v154/css-text-decoration-inset/", - "status": "stub", - "total": 22, - "pass": 17, + "id": "v147/lazy-loading-for-video-and-audio-elements", + "route": "/v147/lazy-loading-for-video-and-audio-elements/", + "status": "built", + "total": 24, + "pass": 19, "fail": 0, "blocked": 5, "results": [ @@ -135,23 +135,35 @@ "reason": "manual-pending (needs agent screenshot/source review)" }, { - "id": "mdn-card-present", + "id": "min-sections", "category": "structure", "deviceClass": "both", "status": "pass" }, { - "id": "mdn-outbound-link", - "category": "links", + "id": "browser-support-section", + "category": "content", + "deviceClass": "both", + "status": "pass" + }, + { + "id": "example-surface-present", + "category": "content", "deviceClass": "both", "status": "pass" }, { - "id": "mdn-coverage-accurate", + "id": "code-example-integrity", "category": "content", "deviceClass": "n/a", "status": "blocked", "reason": "manual-pending (needs agent screenshot/source review)" + }, + { + "id": "warn-block-experimental", + "category": "content", + "deviceClass": "both", + "status": "pass" } ] } diff --git a/responsive-support.json b/responsive-support.json index 0ad7ab3..8d31a73 100644 --- a/responsive-support.json +++ b/responsive-support.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "updatedAt": "2026-09-20T06:10:30.282Z", + "updatedAt": "2026-09-20T06:19:07.320Z", "routes": { "/v147/auto-sizes-for-lazy-loaded-images-with-srcset/": { "desktop": "needs-review", @@ -87,10 +87,10 @@ "checkedAt": "2026-07-19" }, "/v147/lazy-loading-for-video-and-audio-elements/": { - "desktop": "needs-review", - "mobile": "needs-review", - "method": "auto-scan", - "checkedAt": "2026-07-19" + "desktop": "ok", + "mobile": "ok", + "method": "manual matrix review \u2014 CDP mobile emulation (360px, DPR 3) + 1280px desktop screenshots read by writer during MDN-shape upgrade: banner/tables wrap, .table-wrap scroll added, no horizontal overflow, lazy iframe below fold", + "checkedAt": "2026-09-20" }, "/v147/local-network-access-restrictions-for-websockets/": { "desktop": "needs-review", diff --git a/v147/lazy-loading-for-video-and-audio-elements/_questions.json b/v147/lazy-loading-for-video-and-audio-elements/_questions.json new file mode 100644 index 0000000..dfe382b --- /dev/null +++ b/v147/lazy-loading-for-video-and-audio-elements/_questions.json @@ -0,0 +1,116 @@ +{ + "schemaVersion": 1, + "id": "v147/lazy-loading-for-video-and-audio-elements", + "route": "/v147/lazy-loading-for-video-and-audio-elements/", + "identity": "5200068565139456", + "milestone": 147, + "status": "built", + "revision": 2, + "reviewedAt": "2026-09-20", + "reviewer": "writer self-review during weekly MDN-shape upgrade; independent integrator source review pending", + "frontendTouched": true, + "rubric": [ + { + "dimension": "factual-source-fidelity", + "score": 5, + "severity": "info", + "evidence": "Upgrade re-verified every claim against live sources 2026-09-20: WHATWG PR #11980 (merged 2026-03-23), the living standard's lazy-loading-attributes section (enumerated states quoted verbatim) and media-elements section (IDL reflection wording, will-lazy-load steps incl. poster deferral), BCD video/audio/HTMLMediaElement (Chrome 150 full, 148–149 partial with the caveat crbug 514611050), runtime flag LazyLoadVideoAndAudio now stable, webstatus.dev loading-lazy-media Baseline limited, vendor positions mozilla#1325 / WebKit#586." + }, + { + "dimension": "milestone-mapping", + "score": 5, + "severity": "info", + "evidence": "Page keeps its v147 identity (feature 5200068565139456, dev-trial position in the milestone=147 listing) per the durable-demo contract; the post-147 shipping story (148 partial / 150 full) is layered on as banner + compat data with dates, not by re-filing the page." + }, + { + "dimension": "completeness", + "score": 4, + "severity": "info", + "evidence": "Surface is one enumerated content attribute, its IDL reflection, and the deferral behavior — all three inventoried with nine-dimension mappings in the new reference-contract.json. Single-page coverage is proportionate; no child routes needed." + }, + { + "dimension": "explanation-quality", + "score": 4, + "severity": "info", + "evidence": "New syntax section separates content-attribute grammar (with the eager default for missing/invalid values) from DOM reflection; the deferral table now covers poster deferral and the 148–149 caveat developers would actually hit." + }, + { + "dimension": "examples", + "score": 4, + "severity": "minor", + "evidence": "Markup examples (basic, audio, preload combination), feature-detect, and IDL-reflection snippet; live showcase demo embedded (media-loading-timeline) with four sibling concepts linked. Examples are source-derived, not executed locally." + }, + { + "dimension": "browser-compat-fallbacks", + "score": 5, + "severity": "info", + "evidence": "Hand-written 2026-05 support table replaced with a BCD-anchored per-version table (Chrome 150/148–149 partial, Edge mirror, Firefox bugzil.la/2005072, Safari webkit.org/b/303995) plus the webstatus.dev Baseline-limited row; the old prose table's stale 'in developer trial' claim is corrected." + }, + { + "dimension": "links-validity", + "score": 5, + "severity": "info", + "evidence": "All previously unlinked citation labels ('chromestatus summary, May 2026') now link the API record and PR; showcase feature + media-loading-timeline concept routes HEAD-checked 200 on 2026-09-20; '(when built)' note removed since the demos exist." + }, + { + "dimension": "accessibility", + "score": 5, + "severity": "info", + "evidence": "Single h1, sequential h2 outline (former h3 subheads folded into strong paragraphs so fragments stay coherent), th scope=row added on the glance table, banner role=note with aria-label, iframe title + figcaption." + }, + { + "dimension": "responsive-ux", + "score": 4, + "severity": "info", + "evidence": "Added the newer pages' overflow protections this legacy page lacked: overflow-wrap:anywhere on h1/lede/code, .table-wrap{overflow-x:auto} around all four tables, pre overflow-x, iframe max-width:100%; verified via the repo responsive matrix (mobile+desktop screenshots read)." + }, + { + "dimension": "showcase-demo-relationship", + "score": 5, + "severity": "info", + "evidence": "Five CPS concept routes exist; media-loading-timeline embedded (most on-topic for deferral timing), the other four linked in the figcaption; feature route recorded in conformance.json's cpsFeature." + } + ], + "guidanceConsulted": [ + { + "id": "accessibility", + "query": "reference documentation page responsive tables code blocks accessibility", + "recommendation": "Semantic heading order, labelled tables with th scope, descriptive iframe titles.", + "appliedOrException": "Applied: h3s that fragmented the outline replaced with strong paragraphs, th scope=row added, role=note banner, iframe title + figcaption." + }, + { + "id": "defer-rendering-heavy-content", + "query": "embedding third party iframe lazy loading performance", + "recommendation": "Defer offscreen/heavy embeds so they don't compete with initial render.", + "appliedOrException": "Applied: the showcase iframe itself uses loading=\"lazy\" below the fold — fittingly, the same mechanism the page documents." + }, + { + "id": "html", + "query": "reference documentation page responsive tables code blocks accessibility", + "recommendation": "Native semantic markup over re-implementation; resilient no-JS reading experience.", + "appliedOrException": "Applied: all upgrade content is static semantic HTML; page remains fully readable with no script." + } + ], + "openQuestions": [ + "Whether Chrome's lazy-loading distance threshold for media matches the img/iframe thresholds is not stated in the sources read; the page deliberately says 'browser-defined'.", + "BCD marks Edge as 'mirror'; if Edge diverges (e.g. ships the fix at a different version), the compat table needs its own row.", + "The autoplay + loading=lazy interaction row predates the merged spec text and is kept from the original page; re-verify its wording against the merged resource-selection algorithm when next touched." + ], + "followUpGoals": [ + { + "goal": "When Firefox (bugzil.la/2005072) or Safari (webkit.org/b/303995) ships loading on media elements, update the compat table and Baseline banner — the feature may then reach Baseline newly available.", + "kind": "targeted-fix", + "priority": "medium" + }, + { + "goal": "Verify the autoplay/play() interaction rows against the merged HTML resource-selection algorithm text and cite the exact spec steps instead of the pre-merge PR.", + "kind": "targeted-fix", + "priority": "medium" + }, + { + "goal": "Apply this same MDN-shape upgrade (banner, syntax, BCD table, showcase embed, linked citations) to the remaining v147 old-shape pages, thinnest first.", + "kind": "additive-page", + "priority": "high" + } + ] +} diff --git a/v147/lazy-loading-for-video-and-audio-elements/index.html b/v147/lazy-loading-for-video-and-audio-elements/index.html index 172ca67..084f0b7 100644 --- a/v147/lazy-loading-for-video-and-audio-elements/index.html +++ b/v147/lazy-loading-for-video-and-audio-elements/index.html @@ -6,12 +6,25 @@ Lazy loading for video and audio elements — Chrome 147 — gendn @@ -23,69 +36,101 @@

v147 · html · in developer trial

Lazy loading for video and audio elements

+ +
+

Limited availability

+
    +
  • Chrome · 150 (148–149 partial: not for <source>)
  • +
  • Edge · mirrors Chrome
  • +
  • Firefox · not shipped (positive position)
  • +
  • Safari · not shipped (positive position)
  • +
+

Baseline status from the loading-lazy-media web-feature: limited availability (queried 2026-09-20). Per-version rows from BCD html/elements/video.json and audio.json (verified 2026-09-20) — see the full table.

+
+

Adds the loading attribute to <video> and <audio> elements, letting browsers defer media resource loading until the element is near the viewport — the same mechanism already available for <img> and <iframe>.

- In developer trial - This feature is behind a flag in Chrome 147. Enable chrome://flags/#enable-experimental-web-platform-features to test it. The HTML spec pull request is still open; attribute semantics may change before shipping. + Milestone snapshot — the feature has since shipped +

This page records the Chrome 147 milestone, where the feature was in developer trial behind chrome://flags/#enable-experimental-web-platform-features. Both blockers noted at the time have since resolved: the spec change, WHATWG HTML PR #11980, merged on 2026-03-23 into the HTML living standard, and the attribute shipped on by default: partially in Chrome 148 (not honored for <source> children, crbug.com/514611050) and fully in Chrome 150, per BCD; the LazyLoadVideoAndAudio entry in runtime_enabled_features.json5 is status: "stable" (verified 2026-09-20). Firefox and Safari have not shipped it, so cross-browser code still needs the feature-detect fallback.

-

at a glance

+

at a glance

+
- - - - - - - - + + + + + + + +
Status in Chrome 147In developer trial (behind a flag)
Flagchrome://flags/#enable-experimental-web-platform-features
New attributeloading on <video> and <audio>
Valueseager (default) · lazy
Standards positionFirefox: Positive (implementation in progress)  ·  Safari: Support (implementation in progress)
SpecWHATWG HTML PR #11980
Chromium bugissues.chromium.org/issues/469111735
ChromeStatus5200068565139456 — Lazy loading for video and audio elements
Status in Chrome 147In developer trial (behind a flag) — the milestone=147 listing position this page records. Shipped by default from Chrome 148 (fully from 150) — see the compat table
Flag (147)chrome://flags/#enable-experimental-web-platform-features; the runtime feature LazyLoadVideoAndAudio is now stable (flag registry, 2026-09-20)
New attributeloading on <video> and <audio>, reflected by the HTMLMediaElement.loading IDL attribute (HTML living standard — media elements)
Valueseager (default) · lazy — a standard lazy loading attribute
Standards positionFirefox: Positive (mozilla/standards-positions #1325)  ·  Safari: Support (WebKit/standards-positions #586) — neither shipped as of 2026-09-20 (BCD)
SpecWHATWG HTML PR #11980 (merged 2026-03-23) → HTML living standard
Chromium bugissues.chromium.org/issues/469111735
ChromeStatus5200068565139456 — Lazy loading for video and audio elements (API record)
- Source: chromestatus.com feature 5200068565139456, May 2026. +
+ Source: ChromeStatus API record 5200068565139456; WHATWG HTML PR #11980; BCD (all re-verified 2026-09-20) +
+ +
+

syntax

+

Content-attribute grammar — a standard lazy loading attribute, defined once in the HTML living standard and now used by <img>, <iframe>, <video>, and <audio> (quoted verbatim, fetched 2026-09-20): “A lazy loading attribute is an enumerated attribute with the following keywords and states: lazy — Lazy — used to defer fetching a resource until some conditions are met; eager — Eager — used to fetch a resource immediately; the default state.” A missing or unrecognized value therefore behaves as eager.

+
<video loading="lazy"> … </video>
+<audio loading="eager"> … </audio>   <!-- eager is the default state -->
+

DOM reflection (HTML living standard — media elements): HTMLMediaElement gains attribute DOMString loading, and “the loading IDL attribute must reflect the loading content attribute, limited to only known values” — so reading video.loading yields "eager" or "lazy", and assigning it sets the content attribute:

+
const video = document.querySelector("video");
+video.loading = "lazy";          // same as setting the attribute in markup
+console.log(video.loading);      // "lazy"
+ Source: HTML § lazy loading attributes; HTML § media elements; PR #11980
-

why it exists

+

why it exists

Since Chrome 76, <img loading="lazy"> and <iframe loading="lazy"> have let browsers skip fetching off-screen resources until the user scrolls close to them. The same pattern has never applied to <video> and <audio>, which start loading their source files as soon as the parser encounters them regardless of position on the page. Long-form pages with multiple embedded videos pay a large up-front network cost even when most users never scroll to those media elements. The loading attribute on media elements closes this gap and aligns behaviour across all embeddable resources.

- Source: chromestatus summary and WHATWG HTML PR #11980, May 2026. + Source: chromestatus summary and WHATWG HTML PR #11980 (May 2026; PR since merged)
-

the attribute

+

the attribute

+
+
Attributeloading
Elements<video>, <audio>
loading="eager"Default. Load the resource immediately, same as current behavior without the attribute.
loading="lazy"Defer resource loading until the element is within the browser's lazy-loading distance threshold from the viewport. The exact threshold is browser-defined and may vary by network conditions.
<source> caveatIn Chrome 148–149 the attribute was not honored when the media resource came from <source> children (only from a src attribute) — fixed in Chrome 150 (crbug.com/514611050, recorded in BCD)
+

When loading="lazy" is set:

+
+
Network requestDeferred until the element enters the lazy-load intersection zone.
preload interactionA preload="none" hint continues to suppress loading. loading="lazy" takes effect only when the browser would otherwise begin fetching.
AutoplayAn element with autoplay and loading="lazy" will begin playing once loaded, which happens when it enters the threshold zone.
JavaScript .play()Calling .play() before the resource is loaded triggers an immediate load, overriding the lazy deferral.
<video poster>The poster-image fetch also consults the “will lazy load element steps”, so a lazy video defers its poster too (HTML living standard — media elements)
- Source: WHATWG HTML PR #11980 and chromestatus summary, May 2026. +
+ Source: WHATWG HTML PR #11980 (merged into the living standard) and the chromestatus summary
-

example

-

Basic lazy video

+

example

+

Basic lazy video

<video loading="lazy" controls>
   <source src="feature-demo.mp4" type="video/mp4">
   <source src="feature-demo.webm" type="video/webm">
 </video>
-

Lazy audio

+

Lazy audio

<audio loading="lazy" controls>
   <source src="podcast-episode.mp3" type="audio/mpeg">
   <source src="podcast-episode.ogg" type="audio/ogg">
 </audio>
-

Combining with preload

+

Combining with preload

<!-- Best practice for a long page with many videos:
      lazy defers fetching; preload="metadata" limits initial load to just
      duration/dimensions once the element enters the threshold zone. -->
@@ -93,34 +138,52 @@ 

Combining with preload

<source src="explainer.mp4" type="video/mp4"> </video>
-

Detecting support

+

Detecting support

const supportsLazyMedia = 'loading' in HTMLVideoElement.prototype;
 if (!supportsLazyMedia) {
   // Fall back to IntersectionObserver-based lazy loading
 }
- Source: WHATWG HTML PR #11980, May 2026. + +
+ +
Live example from the Chrome Platform Showcase (route HEAD-checked 200, 2026-09-20). Sibling concept demos: scroll detector, network savings estimator, performance compare, and compatibility lab.Source: chrome-platform-showcase
+
+ Source: WHATWG HTML PR #11980; feature-detect pattern gendn-derived from the IDL reflection (labelled accordingly)
-

browser support

+

browser compatibility

+

Per-version data from BCD html/elements/video.json and audio.json (identical rows for both elements, and for the HTMLMediaElement.loading reflection), verified 2026-09-20:

+
- - - - - + + + + +
BrowserSupport
Chrome 147In developer trial (behind flag)
Edge 147Tracking Chromium
FirefoxPositive — implementation in progress
SafariSupport — implementation in progress
Browserloading on <video>/<audio>Notes
Chrome150 (full) · 148–149 partial148–149: not supported for <source> children (crbug 514611050); Chrome 147: dev trial behind a flag (this page’s milestone)
EdgeMirrors ChromeBCD "edge": "mirror"
FirefoxNot shippedImplementation tracked at bugzil.la/2005072; position Positive (#1325)
SafariNot shippedImplementation tracked at webkit.org/b/303995; position Support (#586)
- Source: chromestatus browser views, May 2026. Firefox and Safari signals reference the WHATWG PR. +
+

Baseline: limited availability per the loading-lazy-media web-feature on webstatus.dev (queried 2026-09-20) — Chromium-only so far.

+ Source: BCD video.json; BCD audio.json; webstatus.dev +
+ +
+

security and privacy

+

The attribute adds no new capability: deferred requests are the same media fetches the element would make anyway, under the same CORS and content-security rules. The one observable property is inherent to all lazy loading attributes: because the fetch fires when the element nears the viewport, its timing reveals approximate scroll position to the server hosting the media — exactly as <img loading="lazy"> has since Chrome 76. Pages that consider scroll behavior sensitive should not gate third-party-hosted media on it. Nothing else changes: no new headers, no new origins, no persistent state.

+ Source: HTML § lazy loading attributes (deferral condition = viewport proximity); assessment gendn-derived from that mechanism (labelled accordingly)
-

see also

+

see also

diff --git a/v147/lazy-loading-for-video-and-audio-elements/reference-contract.json b/v147/lazy-loading-for-video-and-audio-elements/reference-contract.json new file mode 100644 index 0000000..f66c33e --- /dev/null +++ b/v147/lazy-loading-for-video-and-audio-elements/reference-contract.json @@ -0,0 +1,284 @@ +{ + "schemaVersion": 1, + "id": "v147/lazy-loading-for-video-and-audio-elements", + "route": "/v147/lazy-loading-for-video-and-audio-elements/", + "kind": "html", + "completeness": "implementation-sufficient", + "assessedAt": "2026-09-20", + "assessor": "writer self-assessment during MDN-shape upgrade; independent integrator review pending", + "sources": [ + { + "id": "html-media", + "label": "HTML living standard — media elements (loading content attribute + IDL reflection, will-lazy-load steps)", + "url": "https://html.spec.whatwg.org/multipage/media.html", + "kind": "normative-spec" + }, + { + "id": "html-lazy", + "label": "HTML living standard — lazy loading attributes (enumerated states, Eager default)", + "url": "https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attributes", + "kind": "normative-spec" + }, + { + "id": "spec-pr", + "label": "WHATWG HTML PR #11980 — lazy loading for media elements (merged 2026-03-23)", + "url": "https://github.com/whatwg/html/pull/11980", + "kind": "normative-spec" + }, + { + "id": "bcd-video", + "label": "BCD html/elements/video.json — loading (Chrome 150, 148–149 partial)", + "url": "https://github.com/mdn/browser-compat-data/blob/main/html/elements/video.json", + "kind": "browser-data" + }, + { + "id": "bcd-audio", + "label": "BCD html/elements/audio.json — loading", + "url": "https://github.com/mdn/browser-compat-data/blob/main/html/elements/audio.json", + "kind": "browser-data" + }, + { + "id": "bcd-api", + "label": "BCD api/HTMLMediaElement.json — loading reflection", + "url": "https://github.com/mdn/browser-compat-data/blob/main/api/HTMLMediaElement.json", + "kind": "browser-data" + }, + { + "id": "webstatus", + "label": "webstatus.dev — loading-lazy-media web-feature (Baseline limited)", + "url": "https://api.webstatus.dev/v1/features/loading-lazy-media", + "kind": "browser-data" + }, + { + "id": "flag-record", + "label": "Chromium runtime_enabled_features.json5 — LazyLoadVideoAndAudio (stable)", + "url": "https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/platform/runtime_enabled_features.json5", + "kind": "browser-data" + }, + { + "id": "crbug-tracking", + "label": "Chromium tracking bug 469111735", + "url": "https://issues.chromium.org/issues/469111735", + "kind": "implementation" + }, + { + "id": "crbug-source", + "label": "Chromium bug 514611050 — loading=lazy not honored for (fixed in 150)", + "url": "https://crbug.com/514611050", + "kind": "implementation" + }, + { + "id": "chromestatus-api", + "label": "ChromeStatus API record 5200068565139456", + "url": "https://chromestatus.com/api/v0/features/5200068565139456", + "kind": "browser-data" + }, + { + "id": "milestone-listing", + "label": "ChromeStatus milestone=147 listing (dev-trial position this page records)", + "url": "https://chromestatus.com/api/v0/features?milestone=147", + "kind": "browser-data" + }, + { + "id": "mozilla-position", + "label": "mozilla standards-positions #1325 (Positive)", + "url": "https://github.com/mozilla/standards-positions/issues/1325", + "kind": "browser-data" + }, + { + "id": "webkit-position", + "label": "WebKit standards-positions #586 (Support)", + "url": "https://github.com/WebKit/standards-positions/issues/586", + "kind": "browser-data" + }, + { + "id": "cps", + "label": "Chrome Platform Showcase — feature demos (five concepts)", + "url": "https://chrome-platform-showcase.paulkinlan-ea.deno.net/v147/lazy-loading-for-video-and-audio-elements/", + "kind": "other" + } + ], + "inventory": [ + { + "id": "loading-content-attribute", + "name": "loading content attribute on