From ba661dcbb5dc94bd3e5a440617389d993099c728 Mon Sep 17 00:00:00 2001 From: L4XB Date: Wed, 16 Sep 2026 13:59:50 +0200 Subject: [PATCH] Don't open two browser tabs when clicking the pull request number 02ac4de1529 gave the pull request number links in the overview header and the sticky header an onClick that calls preventDefault and opens the item on GitHub, so that the link also works in the Agents window. The webview host opens any anchor with an href as well, and its click handler does not check defaultPrevented, so in a regular window the click is handled twice and the browser gets two tabs for the same URL. Stop the click from reaching the host, the way the code reference link handler in webviews/editorWebview/app.tsx already does. Fixes #8955 --- webviews/components/header.tsx | 3 ++ webviews/components/stickyHeader.tsx | 3 ++ webviews/editorWebview/test/overview.test.tsx | 39 +++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/webviews/components/header.tsx b/webviews/components/header.tsx index 9e04929ead..b532dd6bf4 100644 --- a/webviews/components/header.tsx +++ b/webviews/components/header.tsx @@ -136,7 +136,10 @@ function Title({ title, titleHTML, number, url, inEditMode, setEditMode, setCurr title={url} data-vscode-context={JSON.stringify(context)} onClick={event => { + // The webview host opens any anchor with an href and ignores defaultPrevented, + // so the click must not reach it or a second browser tab is opened. event.preventDefault(); + event.stopPropagation(); void openOnGitHub(); }} > diff --git a/webviews/components/stickyHeader.tsx b/webviews/components/stickyHeader.tsx index dc6500bdda..f9ea2c9fa6 100644 --- a/webviews/components/stickyHeader.tsx +++ b/webviews/components/stickyHeader.tsx @@ -65,7 +65,10 @@ export function StickyHeader({ pr, visible }: { pr: PullRequest; visible: boolea 'github:copyMenu': true, })} onClick={event => { + // The webview host opens any anchor with an href and ignores defaultPrevented, + // so the click must not reach it or a second browser tab is opened. event.preventDefault(); + event.stopPropagation(); void openOnGitHub(); }} > diff --git a/webviews/editorWebview/test/overview.test.tsx b/webviews/editorWebview/test/overview.test.tsx index 6a23cae97d..fe8d9a9c22 100644 --- a/webviews/editorWebview/test/overview.test.tsx +++ b/webviews/editorWebview/test/overview.test.tsx @@ -59,6 +59,45 @@ describe('Overview', function () { assert.strictEqual(openOnGitHub.callCount, 2); }); + it('opens a PR number link exactly once', function () { + const pr = new PullRequestBuilder().build(); + const context = new PRContext(pr); + const openOnGitHub = sinon.stub(context, 'openOnGitHub'); + + // Stands in for the webview host, which opens any anchor with an href that a click + // reaches, and does not check defaultPrevented. + const hostOpenedLinks: string[] = []; + const hostLinkHandler = (event: Event) => { + const anchor = (event.target as HTMLElement).closest('a[href]'); + if (anchor) { + hostOpenedLinks.push(anchor.getAttribute('href')!); + } + }; + window.addEventListener('click', hostLinkHandler); + + try { + const out = render( + + + , + ); + + const numberLinks = out.container.querySelectorAll('.overview-title a, .sticky-header-number'); + assert.strictEqual(numberLinks.length, 2); + numberLinks.forEach(link => { + openOnGitHub.resetHistory(); + hostOpenedLinks.length = 0; + + fireEvent.click(link); + + assert.strictEqual(openOnGitHub.callCount, 1); + assert.deepStrictEqual(hostOpenedLinks, []); + }); + } finally { + window.removeEventListener('click', hostLinkHandler); + } + }); + it('shows view changes in both headers', function () { const pr = new PullRequestBuilder().isAgentSessionsWorkspace(true).build(); const context = new PRContext(pr);