Skip to content

Commit c8c5bfc

Browse files
committed
Release claim after final visibility loss
1 parent 9d398ed commit c8c5bfc

3 files changed

Lines changed: 55 additions & 20 deletions

File tree

src/chrome/src/ui/sidepanel.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7104,6 +7104,20 @@ async function sendMessage(extraChatParams = {}) {
71047104
return false;
71057105
}
71067106

7107+
let renewedContextMenuClaim = false;
7108+
const releaseRenewedContextMenuClaim = async () => {
7109+
if (!renewedContextMenuClaim) return;
7110+
renewedContextMenuClaim = false;
7111+
try {
7112+
await sendToBackground('release_context_menu_prompt_claim', {
7113+
tabId,
7114+
promptId: contextMenuClaim.promptId,
7115+
claimantId: contextMenuClaim.claimantId,
7116+
});
7117+
} catch { /* the durable lease still expires if release fails */ }
7118+
onContextMenuClaimRejected?.({ reason: 'panel-hidden', retryAfterMs: 250 });
7119+
};
7120+
71077121
if (contextMenuClaim?.promptId && contextMenuClaim?.claimantId) {
71087122
let renewedClaim = null;
71097123
try {
@@ -7115,18 +7129,12 @@ async function sendMessage(extraChatParams = {}) {
71157129
} catch {
71167130
renewedClaim = { claimed: false, reason: 'connection', retryAfterMs: 1_000 };
71177131
}
7132+
renewedContextMenuClaim = renewedClaim?.claimed === true;
71187133
const claimStillVisible = document.visibilityState !== 'hidden'
71197134
&& sameTabId(currentTabId, tabId)
71207135
&& sameTabId(renderedTabId, tabId);
7121-
if (renewedClaim?.claimed && !claimStillVisible) {
7122-
try {
7123-
await sendToBackground('release_context_menu_prompt_claim', {
7124-
tabId,
7125-
promptId: contextMenuClaim.promptId,
7126-
claimantId: contextMenuClaim.claimantId,
7127-
});
7128-
} catch { /* the durable lease still expires if release fails */ }
7129-
onContextMenuClaimRejected?.({ reason: 'panel-hidden', retryAfterMs: 250 });
7136+
if (renewedContextMenuClaim && !claimStillVisible) {
7137+
await releaseRenewedContextMenuClaim();
71307138
setTabProcessing(tabId, false);
71317139
setTabAbortRequested(tabId, false);
71327140
if (sameTabId(currentTabId, tabId)) syncSendButtonState();
@@ -7148,6 +7156,7 @@ async function sendMessage(extraChatParams = {}) {
71487156
&& sameTabId(currentTabId, tabId)
71497157
&& sameTabId(renderedTabId, tabId);
71507158
if (!renderToCurrentTab) {
7159+
await releaseRenewedContextMenuClaim();
71517160
if (text) saveInputDraftForTab(tabId, text);
71527161
setTabProcessing(tabId, false);
71537162
setTabAbortRequested(tabId, false);

src/firefox/src/ui/sidepanel.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6835,6 +6835,20 @@ async function sendMessage(extraChatParams = {}) {
68356835
return false;
68366836
}
68376837

6838+
let renewedContextMenuClaim = false;
6839+
const releaseRenewedContextMenuClaim = async () => {
6840+
if (!renewedContextMenuClaim) return;
6841+
renewedContextMenuClaim = false;
6842+
try {
6843+
await sendToBackground('release_context_menu_prompt_claim', {
6844+
tabId,
6845+
promptId: contextMenuClaim.promptId,
6846+
claimantId: contextMenuClaim.claimantId,
6847+
});
6848+
} catch { /* the durable lease still expires if release fails */ }
6849+
onContextMenuClaimRejected?.({ reason: 'panel-hidden', retryAfterMs: 250 });
6850+
};
6851+
68386852
if (contextMenuClaim?.promptId && contextMenuClaim?.claimantId) {
68396853
let renewedClaim = null;
68406854
try {
@@ -6846,18 +6860,12 @@ async function sendMessage(extraChatParams = {}) {
68466860
} catch {
68476861
renewedClaim = { claimed: false, reason: 'connection', retryAfterMs: 1_000 };
68486862
}
6863+
renewedContextMenuClaim = renewedClaim?.claimed === true;
68496864
const claimStillVisible = document.visibilityState !== 'hidden'
68506865
&& sameTabId(currentTabId, tabId)
68516866
&& sameTabId(renderedTabId, tabId);
6852-
if (renewedClaim?.claimed && !claimStillVisible) {
6853-
try {
6854-
await sendToBackground('release_context_menu_prompt_claim', {
6855-
tabId,
6856-
promptId: contextMenuClaim.promptId,
6857-
claimantId: contextMenuClaim.claimantId,
6858-
});
6859-
} catch { /* the durable lease still expires if release fails */ }
6860-
onContextMenuClaimRejected?.({ reason: 'panel-hidden', retryAfterMs: 250 });
6867+
if (renewedContextMenuClaim && !claimStillVisible) {
6868+
await releaseRenewedContextMenuClaim();
68616869
setTabProcessing(tabId, false);
68626870
setTabAbortRequested(tabId, false);
68636871
if (sameTabId(currentTabId, tabId)) syncSendButtonState();
@@ -6879,6 +6887,7 @@ async function sendMessage(extraChatParams = {}) {
68796887
&& sameTabId(currentTabId, tabId)
68806888
&& sameTabId(renderedTabId, tabId);
68816889
if (!renderToCurrentTab) {
6890+
await releaseRenewedContextMenuClaim();
68826891
if (text) saveInputDraftForTab(tabId, text);
68836892
setTabProcessing(tabId, false);
68846893
setTabAbortRequested(tabId, false);

test/run.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21869,8 +21869,25 @@ test('context-menu ownership and stale-panel persistence guards are wired in bot
2186921869
);
2187021870
assert.match(
2187121871
panel,
21872-
/await prepareChatHistoryForTurn\(tabId, modeForSend\);[\s\S]*?renderToCurrentTab = document\.visibilityState !== 'hidden'[\s\S]*?sendToBackground\('claim_context_menu_prompt',[\s\S]*?const claimStillVisible = document\.visibilityState !== 'hidden'[\s\S]*?release_context_menu_prompt_claim[\s\S]*?let userEl = null;/,
21873-
`${label}: context-menu ownership should require visibility around renewal and be released before a hidden panel can start`,
21872+
/const releaseRenewedContextMenuClaim = async \(\) => \{[\s\S]*?renewedContextMenuClaim = false;[\s\S]*?release_context_menu_prompt_claim[\s\S]*?onContextMenuClaimRejected\?\.\(\{ reason: 'panel-hidden', retryAfterMs: 250 \}\);[\s\S]*?renewedContextMenuClaim = renewedClaim\?\.claimed === true;/,
21873+
`${label}: renewed ownership should have one idempotent release-and-retry path`,
21874+
);
21875+
const sendMessageStart = panel.indexOf('async function sendMessage(extraChatParams = {}) {');
21876+
const sendMessageEnd = panel.indexOf(
21877+
label === 'chrome' ? '\nfunction formatRecordTimer(' : '\nfunction ensureCurrentRunAssistant(',
21878+
sendMessageStart,
21879+
);
21880+
assert.notEqual(sendMessageEnd, -1, `${label}: sendMessage boundary should remain inspectable`);
21881+
const sendMessageBody = panel.slice(sendMessageStart, sendMessageEnd);
21882+
assert.equal(
21883+
(sendMessageBody.match(/await releaseRenewedContextMenuClaim\(\);/g) || []).length,
21884+
2,
21885+
`${label}: both visibility checks after renewal should release the claim and schedule retry`,
21886+
);
21887+
assert.match(
21888+
sendMessageBody,
21889+
/renewedContextMenuClaim = renewedClaim\?\.claimed === true;[\s\S]*?const claimStillVisible[\s\S]*?if \(renewedContextMenuClaim && !claimStillVisible\) \{[\s\S]*?await releaseRenewedContextMenuClaim\(\);[\s\S]*?renderToCurrentTab = document\.visibilityState !== 'hidden'[\s\S]*?if \(!renderToCurrentTab\) \{\s*await releaseRenewedContextMenuClaim\(\);/,
21890+
`${label}: the final post-renewal visibility failure should relinquish ownership before returning`,
2187421891
);
2187521892
}
2187621893
});

0 commit comments

Comments
 (0)