perf(extension): cut target selection from ~20s to under 3s

Selecting an anime tab took long enough that it read as broken. Measured against
the two anime fixtures, the cost was three multiplying blocks, none of which was
doing useful work.

The visibility handshake ran on every page with more than one frame, including
pages where no frame had a video at all. Its only purpose is to rank and exclude
video candidates, so with nothing to rank it was several seconds of pure waiting
per attempt. It is now skipped unless a candidate exists.

Its pass count was fixed at four, the worst-case same-origin nesting depth. It
now scales to the depth actually observed, which is two on these players, and
each surplus pass was a full round trip across every frame.

The retry budget was spent waiting for a video that no frame had. Retrying
cannot conjure one, and the injected monitor promotes the real player within a
fraction of a second of it appearing, so the loop stops instead — and only
retries when the sweep itself came back thin, which is the case a second pass
can actually fix.

Both probe timeouts were also far too generous. inspectMediaFrame and the
monitor injection are synchronous DOM work: a live frame answers in tens of
milliseconds and anything slower is a frame being torn down, which is exactly
what an ad slot is. 2000ms down to 750ms; a frame dropped there is re-probed on
the next attempt and reports itself through its monitor anyway.

Measured, calm page then heavy ad churn:
  selection  2.5s / 2.6s   (was 2.5s / 7.2s, and ~12s before this series)
  promotion  0.34s / 2.7s  (was 0.35s / 6.4s, and ~15s before this series)

The remaining time is the injection chain itself, not discovery.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Timo
2026-08-18 20:03:40 +02:00
parent b54eefdb5a
commit e0c68650c5
3 changed files with 68 additions and 9 deletions
+2 -2
View File
@@ -2510,7 +2510,7 @@ async function injectMediaFrameMonitors(tabId, contentTarget) {
await executeScriptWithTimeout({
target,
files: ['media-frame-monitor.js']
}, 2000);
}, 750);
injectedCount++;
} catch {
// One denied widget frame must not block the selected player.
@@ -2528,7 +2528,7 @@ async function injectMediaFrameMonitors(tabId, contentTarget) {
await executeScriptWithTimeout({
target,
files: ['media-frame-monitor.js']
}, 2000);
}, 750);
injectedCount++;
} catch {
// Main injection below reports a real selected-target failure.
+34 -4
View File
@@ -4,7 +4,11 @@ export const MEDIA_FRAME_PROBE_TIMEOUT = 'media_frame_probe_timeout';
const MIN_PLAYER_FRAME_AREA = 320 * 180;
const MIN_PLAYER_ASPECT_RATIO = 1.15;
const MAX_PLAYER_ASPECT_RATIO = 2.6;
const DEFAULT_PROBE_TIMEOUT_MS = 2000;
// inspectMediaFrame is synchronous DOM work: a live frame answers in tens of
// milliseconds, and anything slower is a frame that is navigating or being torn
// down. Waiting seconds for those only delays the answer — a frame dropped here
// is re-probed on the next attempt and reports itself through its monitor.
const DEFAULT_PROBE_TIMEOUT_MS = 750;
function normalizeFrameId(value) {
return Number.isInteger(value) && value >= 0 ? value : 0;
@@ -627,7 +631,14 @@ export async function resolveMediaContentTarget(chromeApi, tabId, {
}
}
if (results.length > 1) {
const candidateCount = results.filter(
entry => entry?.result?.bestVideo?.rendered === true
).length;
// The visibility handshake only exists to rank and exclude video
// candidates. With no video on the page yet there is nothing to rank, and
// running it anyway cost several seconds on every attempt — the whole
// reason selecting an anime tab before playback felt broken.
if (results.length > 1 && candidateCount > 0) {
const token = `${tabId}:${attempt}:${Date.now()}:${Math.random()}`;
// Address each discovered frame on its own from here on. A single
// allFrames call is all-or-nothing: one player or ad frame that
@@ -653,8 +664,15 @@ export async function resolveMediaContentTarget(chromeApi, tabId, {
[token],
visibilityTimeoutMs
);
// Four passes match the maximum same-origin recursion depth.
for (let pass = 0; pass < 4; pass++) {
// One pass per nesting level actually present. Four was the
// worst case, not the common one; these players sit two levels
// down and each surplus pass is a full round trip.
const observedDepth = results.reduce((deepest, entry) => Math.max(
deepest,
...(entry?.result?.embeddedFrames || []).map(frame => frame.depth || 1)
), 1);
const passes = Math.min(4, Math.max(2, observedDepth));
for (let pass = 0; pass < passes; pass++) {
await executeInAccessibleFrames(
chromeApi,
frameTargets,
@@ -712,6 +730,18 @@ export async function resolveMediaContentTarget(chromeApi, tabId, {
if (Number.isFinite(deadlineMs) && deadlineMs > 0 && Date.now() - startedAt >= deadlineMs) {
break;
}
// Nothing on the page has a video element yet. Spending the retry budget
// cannot change that; the injected monitor reports the player the moment
// it is created, so return now and let selection be instant.
const anyVideo = results.some(entry => (entry?.result?.videoCount || 0) > 0);
if (!anyVideo) {
// Discovery worked and simply found no player yet, so stop: the
// monitor reports one within a fraction of a second once it exists.
// Retry only when the sweep itself came back thin, which is the case
// a second pass can actually fix.
if (results.length > 1) break;
if (attempt >= 1) break;
}
if (attempt < attempts - 1) {
await new Promise(resolve => setTimeout(resolve, retryDelayMs));
}
+32 -3
View File
@@ -192,12 +192,41 @@ describe('cross-origin media-frame targeting', () => {
const visibilityDispatches = executeScript.mock.calls.filter(([options]) => (
options.func?.name === 'dispatchParentFrameVisibilityProbe'
));
// Four passes across both discovered frames, each addressed on its own
// so a frame that never answers cannot cancel the others.
expect(visibilityDispatches).toHaveLength(8);
// Two passes — the floor — across both discovered frames, each addressed
// on its own so a frame that never answers cannot cancel the others.
// These fixtures report no nesting, so the depth-scaled pass count must
// not spend the worst-case four round trips here.
expect(visibilityDispatches).toHaveLength(4);
expect(visibilityDispatches.every(([options]) => options.target.allFrames !== true)).toBe(true);
});
it('skips the visibility handshake when no frame has a video yet', async () => {
// Selecting an anime tab before playback must be immediate: there is
// nothing to rank, so the handshake and the retry budget are pure delay.
const results = [
frame(0, { bestVideo: null, videoCount: 0 }),
frame(3, { bestVideo: null, videoCount: 0 }),
frame(4, { bestVideo: null, videoCount: 0 })
];
const executeScript = vi.fn().mockResolvedValue(results);
await expect(resolveMediaContentTarget(
{ scripting: { executeScript } },
42,
{ probeDelayMs: 0, retryDelayMs: 0 }
)).resolves.toMatchObject({ frameId: 0, hasVideo: false });
expect(executeScript.mock.calls.filter(([options]) => (
options.func?.name === 'dispatchParentFrameVisibilityProbe'
|| options.func?.name === 'installParentFrameVisibilityProbe'
))).toHaveLength(0);
// And it must not burn all eight attempts waiting for a video that no
// frame has: the injected monitor reports one the moment it appears.
const inspections = executeScript.mock.calls.filter(([options]) => (
options.func?.name === 'inspectMediaFrame'
));
expect(inspections.length).toBeLessThanOrEqual(4);
});
it('keeps the top target inactive when the only discovered video is hidden', async () => {
const results = [
frame(0, { bestVideo: video({ rendered: false }) }),