diff --git a/src/main/browser/popup-origin-bar-window.test.ts b/src/main/browser/popup-origin-bar-window.test.ts index 1d8f791c7b9..fd92d8b71de 100644 --- a/src/main/browser/popup-origin-bar-window.test.ts +++ b/src/main/browser/popup-origin-bar-window.test.ts @@ -253,6 +253,23 @@ describe('openPopupWithOriginBar', () => { expect(lastWindow().setTitle).toHaveBeenCalledWith('http://phish.example.net') }) + it('shows the page title in the native title bar but resets to origin on navigation', () => { + const adopted = createFakeWebContents() + openPopupWithOriginBar( + { webContents: adopted as never }, + 'https://accounts.example.com/oauth?code=SECRET' + ) + // Until the page supplies a title, the window title is the origin. + expect(lastWindow().options.title).toBe('https://accounts.example.com') + + adopted.emit('page-title-updated', {}, 'Sign in to Example') + expect(lastWindow().setTitle).toHaveBeenLastCalledWith('Sign in to Example') + + // A stale title must not survive a cross-origin navigation. + adopted.emit('did-navigate', {}, 'https://evil.example.net/') + expect(lastWindow().setTitle).toHaveBeenLastCalledWith('https://evil.example.net') + }) + it('closes the window when the popup content is destroyed', () => { const adopted = createFakeWebContents() openPopupWithOriginBar({ webContents: adopted as never }, 'https://example.com/') diff --git a/src/main/browser/popup-origin-bar-window.ts b/src/main/browser/popup-origin-bar-window.ts index fb5873f63e1..03eb3c46c9d 100644 --- a/src/main/browser/popup-origin-bar-window.ts +++ b/src/main/browser/popup-origin-bar-window.ts @@ -139,6 +139,10 @@ export function openPopupWithOriginBar( let currentUrl = initialUrl const renderOrigin = (): void => { const { label, insecure } = describePopupOrigin(currentUrl) + // Why: origin is the title only until the page supplies one — the bar + // below stays the trust surface, so the native title bar can show the + // page title (Chrome popup behavior) instead of doubling the origin. + // Re-asserting on navigation stops a stale title outliving its origin. if (!window.isDestroyed()) { window.setTitle(label) } @@ -161,6 +165,12 @@ export function openPopupWithOriginBar( renderOrigin() } contentWebContents.on('did-navigate', handleDidNavigate) + const handlePageTitleUpdated = (_event: Electron.Event, title: string): void => { + if (!window.isDestroyed() && title) { + window.setTitle(title) + } + } + contentWebContents.on('page-title-updated', handlePageTitleUpdated) // Why: with no adopted contents there is no Chromium-driven navigation for // this popup, so load the target ourselves (opener handle is already gone). @@ -179,6 +189,7 @@ export function openPopupWithOriginBar( if (!contentWebContents.isDestroyed()) { contentWebContents.off('destroyed', handleContentDestroyed) contentWebContents.off('did-navigate', handleDidNavigate) + contentWebContents.off('page-title-updated', handlePageTitleUpdated) // Why: close() (not destroy) so the page's unload handlers run — OAuth // pages often notify the opener from unload. contentWebContents.close()