Architecture: Custom Media Element Track Children Are Cloned, So Programmatic Track Use Silently Diverges From Native Video
needs discussionapipkg:coremedia
`CustomMediaElement` forwards `<track>` (and `<source>`) host children to the inner native element by shallow `cloneNode()` (`#syncMediaChildren`, `packages/media/src/dom/custom-media-element/custom-media-element.ts`). The `TextTrack` that joins the live `textTracks` list is the **clone's**; the host child's own `.track` object is inert. Only attributes are synced (one-way, via `MutationObserver`).
That implicitly defines track children as declarative, src-bearing configuration. Everything the repo ships today stays inside that contract, so nothing is broken right now — but the contract is undocumented, and patterns that are idiomatic on a native `<video>` fail silently on the host:
- A srcless `<track>` child plus `trackEl.track.addCue(...)` — the cues land on the inert host-side track and never appear in `media.textTracks`. This is the likeliest real-world victim: building cues programmatically while attaching declaratively.
- `trackEl.track.mode = ...` on the child — no-op on the live track (mode is not an attribute).
- `load` / `error` on the child element and `cuechange` on its track — never fire; the clone does the loading. The store's own `load` listeners on host-level tracks (`packages/core/src/dom/store/features/text-track.ts`) are dead code for custom media elements; the feature works only via the shadow-root query that finds the clones.
- `trackEl.readyState` stays `NONE` on the child.
The gap is already half-known: `#enableDefaultTrack` exists to patch one specific clone divergence (browsers ignore `default` on JS-inserted tracks) and covers nothing else. Programmatic consumers do have a working path — `host.addTextTrack()` and `host.textTracks` are prototype-delegated to the native element — but nothing steers them to it.
## Why now
- #1268 (programmatic chapters) will pick an authoring surface; if it lands on "write cues into a track child", it hits this head-on.
- The planned Shaka in-manifest chapters/thumbnails bridge synthesizes cue-populated tracks and had to discover this by inspection; the next feature shouldn't have to.
## Acceptance criteria
- The supported contract for track children is decided and documented: declarative and src-bearing only (with `host.textTracks` / `host.addTextTrack()` as the programmatic surface), or a defined level of native parity.
- If the contract stays declarative-only, the unsupported pattern fails loudly: a `__DEV__` warning when a srcless `<track>` child is slotted.
Related: #1268, #1843.
0 条评论