diff --git a/Sources/Panels/BrowserPanel.swift b/Sources/Panels/BrowserPanel.swift index 07ce2d538d79..00ccf669bfa7 100644 --- a/Sources/Panels/BrowserPanel.swift +++ b/Sources/Panels/BrowserPanel.swift @@ -2296,6 +2296,7 @@ private enum BrowserInsecureHTTPNavigationIntent { nonisolated enum BrowserWebViewLifecycleState: String { case newTab = "new_tab" + case deferredURL = "deferred_url" case liveVisible = "live_visible" case liveHidden = "live_hidden" case discarded @@ -2748,9 +2749,9 @@ final class BrowserPanel: Panel, ObservableObject { private var isWebViewVisibleInUI: Bool = false private var isClosingWebViewLifecycle: Bool = false - /// True when the browser is showing the internal empty new-tab page (no WKWebView attached yet). + /// True when the browser is showing the internal empty new-tab page. var isShowingNewTabPage: Bool { - !shouldRenderWebView + !shouldRenderWebView && preferredURLStringForOmnibar() == nil } /// Published page title @@ -3008,7 +3009,7 @@ final class BrowserPanel: Panel, ObservableObject { } else if hiddenWebViewDiscardManager.isDiscardedForMemory { nextState = .discarded } else if !shouldRenderWebView { - nextState = .newTab + nextState = preferredURLStringForOmnibar() == nil ? .newTab : .deferredURL } else if isWebViewVisibleInUI { nextState = .liveVisible } else { diff --git a/Sources/Panels/BrowserPanelView.swift b/Sources/Panels/BrowserPanelView.swift index d80f5a7b652a..2befd1a8eb28 100644 --- a/Sources/Panels/BrowserPanelView.swift +++ b/Sources/Panels/BrowserPanelView.swift @@ -855,10 +855,10 @@ struct BrowserPanelView: View { if addressBarFocused, !panel.shouldSuppressWebViewFocus(), addressWasEmpty, - !isWebViewBlank() { + !isBrowserContentBlankForOmnibar() { setAddressBarFocused(false, reason: "panel.currentURL.loaded") } - if isWebViewBlank() { + if panel.isShowingNewTabPage { refreshEmptyStateImportBrowsers() } panel.resetReactGrabState( @@ -1947,7 +1947,7 @@ struct BrowserPanelView: View { } private var shouldShowEmptyStateImportOverlay: Bool { - !panel.shouldRenderWebView && isWebViewBlank() + panel.isShowingNewTabPage } private func presentImportDialogFromHint() { @@ -1983,10 +1983,9 @@ struct BrowserPanelView: View { isBrowserImportHintPopoverPresented = false } - /// Treat a WebView with no URL (or about:blank) as "blank" for UX purposes. - private func isWebViewBlank() -> Bool { - guard let url = panel.webView.url else { return true } - return url.absoluteString == "about:blank" + /// Treat content as blank only if neither WebKit nor the panel model has a nonblank URL. + private func isBrowserContentBlankForOmnibar() -> Bool { + panel.preferredURLStringForOmnibar() == nil } private func autoFocusOmnibarIfBlank() { @@ -2022,7 +2021,7 @@ struct BrowserPanelView: View { #endif return } - guard isWebViewBlank() else { + guard isBrowserContentBlankForOmnibar() else { #if DEBUG logBrowserFocusState(event: "addressBarFocus.autoFocus.skip", detail: "reason=webview_not_blank") #endif diff --git a/cmuxTests/BrowserConfigTests.swift b/cmuxTests/BrowserConfigTests.swift index c00288a42c7b..f749c5c7e7d0 100644 --- a/cmuxTests/BrowserConfigTests.swift +++ b/cmuxTests/BrowserConfigTests.swift @@ -1877,6 +1877,20 @@ final class BrowserDeveloperToolsConfigurationTests: XCTestCase { XCTAssertFalse(panel.isShowingNewTabPage) } + func testBrowserPanelWithDeferredInitialURLIsNotNewTabPage() throws { + let url = try XCTUnwrap(URL(string: "https://example.com/restored")) + let panel = BrowserPanel( + workspaceId: UUID(), + initialURL: url, + renderInitialNavigation: false + ) + + XCTAssertFalse(panel.shouldRenderWebView) + XCTAssertEqual(panel.currentURL, url) + XCTAssertFalse(panel.isShowingNewTabPage) + XCTAssertEqual(panel.webViewLifecycleState, .deferredURL) + } + func testBrowserPanelThemeModeUpdatesWebViewAppearance() { let panel = BrowserPanel(workspaceId: UUID()) diff --git a/cmuxTests/GhosttyConfigTests.swift b/cmuxTests/GhosttyConfigTests.swift index c3527229da1b..0fc320fae3a5 100644 --- a/cmuxTests/GhosttyConfigTests.swift +++ b/cmuxTests/GhosttyConfigTests.swift @@ -1527,7 +1527,7 @@ final class BrowserPanelWebViewLifecycleTests: XCTestCase { } } - func testLifecycleStartsAsNewTabUntilRenderable() { + func testLifecycleDistinguishesDeferredURLFromNewTab() { let panel = BrowserPanel( workspaceId: UUID(), initialURL: URL(string: "https://example.test/")!, @@ -1536,11 +1536,11 @@ final class BrowserPanelWebViewLifecycleTests: XCTestCase { ) defer { panel.close() } - XCTAssertEqual(panel.webViewLifecycleState, .newTab) + XCTAssertEqual(panel.webViewLifecycleState, .deferredURL) panel.noteWebViewVisibility(true, reason: "test.visible") - XCTAssertEqual(panel.webViewLifecycleState, .newTab) + XCTAssertEqual(panel.webViewLifecycleState, .deferredURL) } func testBackgroundInitialNavigationOwnsHeadlessWebKitHostBeforeViewAppears() {