The evidence for the frame-observation fixes was a reimplementation of the
logic measured in a browser console, not the shipped code. Two extension
specs now cover the observable behaviour end to end: a player frame that
swaps its document, and the same one level deeper.
The nested case fails against the pre-fix content.js and passes now. The
top-level case already passed before the fix, so that fix removed dead
observer registrations without changing what a user could see; recorded
here so the distinction is not lost.
Also adds bench-finder.mjs, which measures the extracted shipped finder
instead of a transcription of it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The delta model was only covered at module level; the popup wiring around
it was not exercised at all. Seven specs now drive the real settings UI in
the packed extension, including the migration path: a pre-v3.1.0 snapshot
is converted on open, the legacy key is removed, and a default missing
from that snapshot is delivered again.
Also from the audit pass:
- await the blacklist read in init instead of firing a floating promise
- unify the debug report on the finder's own candidate list, which the
separate traversal missed shadow-DOM videos from
- assert that a single candidate is always returned regardless of its
ranking signals, so no scoring signal can regress a single-player site
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The weighted score summed incomparable units, so size could outvote traits
that disqualify an element outright. Measured on a real page: a display:none
preload reports its full 1080p intrinsic size and scored 2073600, beating a
visible unmuted player at 509920.
Selection now compares an ordered list of signals, highest priority first:
has a source, is rendered, is not a silent background loop, rendered size
bucket, is playing, has controls, duration. Rendered size replaces intrinsic
resolution, and mute state is gone from the ranking entirely: it is a viewer
preference, not evidence about which element is the player.
It stays a ranking rather than a filter, so a page of only bad candidates
still yields one and findVideo never returns null where a video exists.
The new tests/e2e suite runs the shipped finder against real fixture pages
and drives the packed extension for injection, reinjection and remote
play/pause/seek into a first-party frame. All five scoring scenarios fail
against the previous implementation.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>