Share browser context with OAuth popups (#1600)

* Add popup browser context regression tests

* Share browser context with OAuth popups

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
This commit is contained in:
Lawrence Chen 2026-03-20 03:57:36 -07:00 committed by GitHub
parent 886de2a7c9
commit cc0fc557cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 93 additions and 29 deletions

View file

@ -2447,33 +2447,9 @@ final class BrowserPanel: Panel, ObservableObject {
websiteDataStore: WKWebsiteDataStore? = nil
) -> CmuxWebView {
let config = WKWebViewConfiguration()
config.processPool = BrowserPanel.sharedProcessPool
config.mediaTypesRequiringUserActionForPlayback = []
// Ensure browser cookies/storage persist across navigations and launches.
// This reduces repeated consent/bot-challenge flows on sites like Google.
config.websiteDataStore = websiteDataStore ?? BrowserProfileStore.shared.websiteDataStore(for: profileID)
// Enable developer extras (DevTools)
config.preferences.setValue(true, forKey: "developerExtrasEnabled")
// Enable JavaScript
config.defaultWebpagePreferences.allowsContentJavaScript = true
// Keep browser console/error/dialog telemetry active from document start on every navigation.
config.userContentController.addUserScript(
WKUserScript(
source: Self.telemetryHookBootstrapScriptSource,
injectionTime: .atDocumentStart,
forMainFrameOnly: false
)
)
// Track the last editable focused element continuously so omnibar exit can
// restore page input focus even if capture runs after first-responder handoff.
config.userContentController.addUserScript(
WKUserScript(
source: Self.addressBarFocusTrackingBootstrapScript,
injectionTime: .atDocumentStart,
forMainFrameOnly: false
)
configureWebViewConfiguration(
config,
websiteDataStore: websiteDataStore ?? BrowserProfileStore.shared.websiteDataStore(for: profileID)
)
let webView = CmuxWebView(frame: .zero, configuration: config)
@ -2489,6 +2465,41 @@ final class BrowserPanel: Panel, ObservableObject {
return webView
}
static func configureWebViewConfiguration(
_ configuration: WKWebViewConfiguration,
websiteDataStore: WKWebsiteDataStore,
processPool: WKProcessPool = BrowserPanel.sharedProcessPool
) {
configuration.processPool = processPool
configuration.mediaTypesRequiringUserActionForPlayback = []
// Ensure browser cookies/storage persist across navigations and launches.
// This reduces repeated consent/bot-challenge flows on sites like Google.
configuration.websiteDataStore = websiteDataStore
// Enable developer extras (DevTools)
configuration.preferences.setValue(true, forKey: "developerExtrasEnabled")
// Enable JavaScript
configuration.defaultWebpagePreferences.allowsContentJavaScript = true
// Keep browser console/error/dialog telemetry active from document start on every navigation.
configuration.userContentController.addUserScript(
WKUserScript(
source: Self.telemetryHookBootstrapScriptSource,
injectionTime: .atDocumentStart,
forMainFrameOnly: false
)
)
// Track the last editable focused element continuously so omnibar exit can
// restore page input focus even if capture runs after first-responder handoff.
configuration.userContentController.addUserScript(
WKUserScript(
source: Self.addressBarFocusTrackingBootstrapScript,
injectionTime: .atDocumentStart,
forMainFrameOnly: false
)
)
}
private func bindWebView(_ webView: CmuxWebView) {
webView.onContextMenuDownloadStateChanged = { [weak self] downloading in
if downloading {

View file

@ -92,13 +92,24 @@ final class BrowserPopupWindowController: NSObject, NSWindowDelegate {
self.parentPopupController = parentPopupController
self.nestingDepth = nestingDepth
// Create popup web view with WebKit's supplied configuration (preserves
// internal browsing-context state for opener linkage / postMessage).
let browserContextSource = parentPopupController?.webView.configuration ?? openerPanel?.webView.configuration
if let browserContextSource {
BrowserPanel.configureWebViewConfiguration(
configuration,
websiteDataStore: browserContextSource.websiteDataStore,
processPool: browserContextSource.processPool
)
}
// Create popup web view with WebKit's supplied configuration after
// overlaying the opener's browser context so OAuth popups keep cmux's
// shared cookie/storage scope and opener linkage.
let webView = CmuxWebView(frame: .zero, configuration: configuration)
webView.allowsBackForwardNavigationGestures = true
if #available(macOS 13.3, *) {
webView.isInspectable = true
}
webView.underPageBackgroundColor = GhosttyBackgroundTheme.currentColor()
webView.customUserAgent = BrowserUserAgentSettings.safariUserAgent
self.webView = webView

View file

@ -1003,6 +1003,48 @@ final class GhosttyTerminalStartupEnvironmentTests: XCTestCase {
}
}
@MainActor
final class BrowserPanelPopupContextTests: XCTestCase {
func testFloatingPopupInheritsOpenerBrowserContext() throws {
let panel = BrowserPanel(workspaceId: UUID(), isRemoteWorkspace: false)
let popupWebView = try XCTUnwrap(
panel.createFloatingPopup(
configuration: WKWebViewConfiguration(),
windowFeatures: WKWindowFeatures()
)
)
defer { popupWebView.window?.close() }
XCTAssertTrue(
popupWebView.configuration.processPool === panel.webView.configuration.processPool
)
XCTAssertTrue(
popupWebView.configuration.websiteDataStore === panel.webView.configuration.websiteDataStore
)
}
func testFloatingPopupInheritsRemoteWorkspaceWebsiteDataStore() throws {
let remoteWorkspaceId = UUID()
let panel = BrowserPanel(
workspaceId: remoteWorkspaceId,
isRemoteWorkspace: true,
remoteWebsiteDataStoreIdentifier: remoteWorkspaceId
)
let popupWebView = try XCTUnwrap(
panel.createFloatingPopup(
configuration: WKWebViewConfiguration(),
windowFeatures: WKWindowFeatures()
)
)
defer { popupWebView.window?.close() }
XCTAssertTrue(
popupWebView.configuration.websiteDataStore === panel.webView.configuration.websiteDataStore
)
XCTAssertFalse(popupWebView.configuration.websiteDataStore === WKWebsiteDataStore.default())
}
}
@MainActor
final class BrowserPanelRemoteStoreTests: XCTestCase {
func testRemoteWorkspacePanelsShareWorkspaceScopedWebsiteDataStore() {