Peer usernames are remote-controlled and were interpolated into an
innerHTML string in updateLobbyUI. The server only truncates them to 30
chars, so a peer could inject markup into everyone else's popup: enough
to load a remote image (leaking viewer IPs) or spoof readiness badges.
Inline handlers were already blocked by the MV3 default CSP.
Build the peer items with the DOM API, matching the pattern the sibling
peer list already uses.
Add the two checks that would have caught this before upload:
- eslint no-unsanitized, which reproduces the AMO warning at lint time
- addons-linter on the built XPI in verify-release, with
--warnings-as-errors since it exits 0 on warnings and AMO rejects them
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The test still required 4 foreground film birds, but commit 16cb138
deliberately removed them all; now it guards against remnants instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The v2.5.2 Disney+ page-API integration leaked blob-relative <video>
time into force sync, seeks, and heartbeats when the page-API bridge
had no fresh data, so force sync on Disney+ appeared broken.
- getSyncCurrentTime/getSyncDuration now refuse native values on Disney+
(return null/0) so stale bridge data degrades to a clean no-op instead
of broadcasting garbage to peers. The get_current_time handler and
episode/lobby/hcmIsLive paths are routed through the same accessor.
- Validate FORCE_SYNC_PREPARE/SEEK payloads as finite before relaying;
the internal coercion no longer treats '' as 0.
- Stop double-routing FORCE_SYNC_PREPARE from the popup path (the
generic popup route now covers only play/pause/seek).
- popup force-sync: exclude null/empty peer times from the jump-to-others
median (Number(null)===0 was dragging the target to 0), guard against
NaN end-to-end, clear the dangling reset timer on failure, and retry
without re-injecting when the content script responds but the Disney+
bridge has not yet delivered a finite time.
- hcmIsLive skips the native-duration live signal on Disney+ only,
preserving YouTube/Twitch Infinity-duration live detection.
Disney-specific logic remains strictly gated to disneyplus.com; no
Netflix/YouTube/Twitch/generic path is affected.
Disney+'s <video> is blob-relative (unusable as an absolute clock) and its
scrubber aria-value freezes during playback, so DOM scraping lagged and the
+/-10s button seek could neither reach far targets nor land precisely. The
real player hangs off the <disney-web-player> custom element as
`.mediaPlayer`, exposing seek(ms) and timeline.info (playhead/duration ms).
Since the isolated content world can't read that page object, route it
through the existing MAIN-world page-API bridge (as Netflix already does):
- page-api-seek-overrides.js: register a 'disney' provider.
- background.js installPageApiSeekBridge: seek Disney via mediaPlayer.seek(),
and post the exact playhead/duration (seconds) to the content world every
250ms. Both are gated on provider === 'disney'; Netflix path unchanged.
- content.js: cache the pushed playhead, prefer it in getDisneyPlusTimeline
(DOM scraping stays as fallback), and check the page-API seek first in
seekVideo. Outcome is identical for Netflix and generic sites.
Verified live on Disney+: reported time matches the player exactly and
seek lands within ~1s of the target.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When the control overlay (and its live time readout) is briefly absent,
the old fallback recomputed the timeline from the raw blob-relative
video.currentTime via a cached scale/start. Disney recreates the <video>
element on some play/pause transitions, which resets currentTime, so that
math produced a wildly wrong time ("loses the time" after a pause).
The native clock advances 1:1 with real playback, so instead anchor the
last live UI position to video.currentTime and extrapolate by the elapsed
native delta. If the delta is negative or implausibly large (element
recreated / seek), freeze at the last known position rather than emitting
a garbage time.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- server: SET_PEER_ROLE now uses the same per-room 500ms debounce as
SET_CONTROL_MODE (M-4), preventing a buggy/malicious host from
thrashing the room's UI with rapid promote/demote bursts.
- server: PEER_STATUS relay treats explicit null as 'clear' for
tabTitle/mediaTitle/currentTime, so the tab-close heartbeat actually
zeroes out stale state on other peers instead of being silently
preserved by the clamp fallback.
- background: _persistLastSeq is now trailing-debounced (500ms),
cutting storage.session IPC writes from one-per-relayed-event to
one-per-quiet-window in active rooms.
- locales: add missing TOAST_ID_REGENERATED to all 14 non-English
locales (was blocking locale coverage test on main).
- test: H-1 force-sync demote test now waits out the debounce window
to reflect real-world UI timing (a host cannot promote, run a
force-sync, and demote inside 500ms).