fix(extension): stop the video-state poll from restarting the target

getReadyTabVideoState() treated "no video found" as a broken injection and
forced a full reactivation. On a page that legitimately has no video yet — an
anime or Drive page before playback starts — that fired on every call, and the
dev panel polls it on a timer. The result was an endless teardown and
reinjection cycle: the target never settled, the popup showed "activating"
forever, and the panel reported "Target tab changed before content script
recovery completed" because each read raced the reactivation it had triggered.

Only an unreachable content script justifies recovery now, and that recovery no
longer reinjects unless the selected frame actually moved.

Audited against v3.1.2, which worked on these pages. The only unjustified
deviation left was the retry budget, which had been cut from eight passes to
three and shortened the window for a late-loading player; it is back at eight,
now bounded by a wall-clock deadline instead of being unbounded.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Timo
2026-08-18 01:05:03 +02:00
parent ac0093b043
commit f450584562
4 changed files with 60 additions and 9 deletions
+14 -5
View File
@@ -2059,17 +2059,26 @@ async function getReadyTabVideoState(tabId, expectedGeneration = targetActivatio
return { error: 'Target tab changed before video state could be read' };
}
let state = await getTabVideoState(tabId);
if (!state || state.error || state.found === false) {
const activation = await refreshCurrentMediaTarget(tabId);
if (activation?.status !== 'ok') {
// "No video" is a legitimate answer, not a broken injection: an anime or
// Drive page has no video element until the viewer starts playback. Forcing
// a reactivation for it made every poll of this function tear the content
// script down and reinject it, which kept the target permanently activating.
// Only an unreachable content script justifies recovery.
if (!state || state.error) {
const activation = await refreshCurrentMediaTarget(tabId, { onlyIfTargetMoved: true });
if (activation?.status !== 'ok' && activation?.status !== 'unchanged') {
return { error: 'Target tab changed before content script recovery completed' };
}
// An unchanged target reports no generation of its own.
const generation = Number.isInteger(activation.generation)
? activation.generation
: targetActivationGeneration;
await new Promise(resolve => setTimeout(resolve, 250));
if (!isCurrentTargetIdentity(tabId, activation.generation)) {
if (!isCurrentTargetIdentity(tabId, generation)) {
return { error: 'Target tab changed before video state could be read' };
}
state = await getTabVideoState(tabId);
if (!isCurrentTargetIdentity(tabId, activation.generation)) {
if (!isCurrentTargetIdentity(tabId, generation)) {
return { error: 'Target tab changed while video state was being read' };
}
}