fix(extension): cancel superseded frame monitors

This commit is contained in:
KoalaDev
2026-08-17 17:11:47 +02:00
parent 0701d347c7
commit a8fd79618f
2 changed files with 46 additions and 5 deletions
+39 -5
View File
@@ -2279,6 +2279,19 @@ function isMediaTargetNavigationError(error) {
|| message.includes('No document with ID');
}
function isTargetActivationSuperseded(tabId, activationGeneration) {
if (!Number.isInteger(activationGeneration)) return false;
return targetActivationGeneration !== activationGeneration
|| activeTargetActivation?.generation !== activationGeneration
|| normalizeTabId(activeTargetActivation?.tabId) !== normalizeTabId(tabId);
}
function createTargetActivationSupersededError() {
const error = new Error('Target activation was superseded');
error.code = 'target_activation_superseded';
return error;
}
async function injectMediaFrameMonitors(tabId, contentTarget) {
const targets = await listMediaFrameScriptTargets(chrome, tabId);
let injectedCount = 0;
@@ -2315,7 +2328,8 @@ async function injectMediaFrameMonitors(tabId, contentTarget) {
async function injectContentScript(tabId, {
requestHostAccess = true,
navigationRetries = 2
navigationRetries = 2,
activationGeneration = null
} = {}) {
const normalizedTabId = normalizeTabId(tabId);
if (normalizedTabId === null) throw new Error('Invalid tab ID');
@@ -2335,7 +2349,8 @@ async function injectContentScript(tabId, {
const url = access.url || '';
needsPageApiSeek = shouldUsePageApiSeek(url);
contentTarget = await resolveMediaContentTarget(chrome, tabId);
if (activeTargetActivation?.tabId === tabId) {
if (!isTargetActivationSuperseded(tabId, activationGeneration)
&& activeTargetActivation?.tabId === tabId) {
activeTargetActivation.frameId = contentTarget.frameId;
activeTargetActivation.documentId = contentTarget.documentId;
}
@@ -2358,7 +2373,17 @@ async function injectContentScript(tabId, {
const selectedDocumentId = contentTarget.documentId;
try {
if (isTargetActivationSuperseded(tabId, activationGeneration)) {
throw createTargetActivationSupersededError();
}
await injectMediaFrameMonitors(tabId, contentTarget);
if (isTargetActivationSuperseded(tabId, activationGeneration)) {
const replacementTabId = normalizeTabId(activeTargetActivation?.tabId);
if (replacementTabId !== tabId) {
await deactivateMediaFrameMonitors(tabId);
}
throw createTargetActivationSupersededError();
}
if (needsPageApiSeek) {
try {
await chrome.scripting.executeScript({
@@ -2401,16 +2426,22 @@ async function injectContentScript(tabId, {
contentTarget.documentId = typeof frameResult?.documentId === 'string'
? frameResult.documentId
: contentTarget.documentId;
if (activeTargetActivation?.tabId === tabId) {
if (!isTargetActivationSuperseded(tabId, activationGeneration)
&& activeTargetActivation?.tabId === tabId) {
activeTargetActivation.frameId = contentTarget.frameId;
activeTargetActivation.documentId = contentTarget.documentId;
}
return contentTarget;
} catch (error) {
if (error?.code === 'target_activation_superseded') {
try { error.contentTarget = contentTarget; } catch { /* immutable browser error */ }
throw error;
}
if (navigationRetries > 0 && isMediaTargetNavigationError(error)) {
return injectContentScript(tabId, {
requestHostAccess,
navigationRetries: navigationRetries - 1
navigationRetries: navigationRetries - 1,
activationGeneration
});
}
try { error.contentTarget = contentTarget; } catch { /* immutable browser error */ }
@@ -2610,7 +2641,10 @@ async function activateTargetTab(tabId, tabTitle, {
return { status: 'superseded' };
}
try {
injectedContentTarget = await injectContentScript(selectedTabId, { requestHostAccess });
injectedContentTarget = await injectContentScript(selectedTabId, {
requestHostAccess,
activationGeneration
});
} catch (error) {
if (activationGeneration !== targetActivationGeneration) {
if (normalizeTabId(currentTabId) !== selectedTabId) {
+7
View File
@@ -37,6 +37,13 @@ describe('target tab lifecycle', () => {
expect(overlaySource).toContain("message?.type === 'TARGET_DEACTIVATE'");
});
it('removes monitors injected by a superseded cross-tab activation', () => {
expect(backgroundSource).toContain('function isTargetActivationSuperseded(tabId, activationGeneration)');
expect(backgroundSource).toContain('activationGeneration\n });');
expect(backgroundSource).toMatch(/await injectMediaFrameMonitors\(tabId, contentTarget\);[\s\S]*if \(isTargetActivationSuperseded\(tabId, activationGeneration\)\)[\s\S]*await deactivateMediaFrameMonitors\(tabId\);/);
expect(backgroundSource).toContain("error.code = 'target_activation_superseded'");
});
it('binds cross-origin targets to an exact document and monitors every accessible frame', () => {
expect(backgroundSource).toContain("files: ['media-frame-monitor.js']");
expect(backgroundSource).toContain('const targets = await listMediaFrameScriptTargets(chrome, tabId)');