Fix browser pane video fullscreen

This commit is contained in:
austinpower1258 2026-03-21 12:17:55 -07:00
parent 6ff81579d9
commit e935eb57d3
4 changed files with 97 additions and 6 deletions

View file

@ -2759,6 +2759,8 @@ final class WindowBrowserPortal: NSObject {
let webViewId = ObjectIdentifier(webView)
let anchorId = ObjectIdentifier(anchorView)
let previousEntry = entriesByWebViewId[webViewId]
let shouldPreserveExternalFullscreenHost =
webView.cmuxIsManagedByExternalFullscreenWindow(relativeTo: window)
let containerView = ensureContainerView(
for: previousEntry ?? Entry(
webView: nil,
@ -2833,7 +2835,16 @@ final class WindowBrowserPortal: NSObject {
}
#endif
if webView.superview !== containerView {
if shouldPreserveExternalFullscreenHost {
#if DEBUG
dlog(
"browser.portal.reparent.skip web=\(browserPortalDebugToken(webView)) " +
"reason=fullscreenExternalHost super=\(browserPortalDebugToken(webView.superview)) " +
"container=\(browserPortalDebugToken(containerView)) " +
"state=\(String(describing: webView.fullscreenState))"
)
#endif
} else if webView.superview !== containerView {
#if DEBUG
dlog(
"browser.portal.reparent web=\(browserPortalDebugToken(webView)) " +
@ -3097,10 +3108,22 @@ final class WindowBrowserPortal: NSObject {
hostView.addSubview(containerView, positioned: .above, relativeTo: nil)
refreshReasons.append("syncAttachContainer")
}
let shouldPreserveExternalFullscreenHost =
webView.cmuxIsManagedByExternalFullscreenWindow(relativeTo: window)
let shouldPreserveExternalHostForHiddenEntry =
!shouldPreserveExternalFullscreenHost &&
!entry.visibleInUI &&
webView.superview !== containerView
if shouldPreserveExternalHostForHiddenEntry {
if shouldPreserveExternalFullscreenHost {
#if DEBUG
dlog(
"browser.portal.reparent.skip web=\(browserPortalDebugToken(webView)) " +
"reason=fullscreenExternalHost super=\(browserPortalDebugToken(webView.superview)) " +
"container=\(browserPortalDebugToken(containerView)) " +
"state=\(String(describing: webView.fullscreenState))"
)
#endif
} else if shouldPreserveExternalHostForHiddenEntry {
#if DEBUG
dlog(
"browser.portal.reparent.skip web=\(browserPortalDebugToken(webView)) " +

View file

@ -2192,6 +2192,7 @@ final class BrowserPanel: Panel, ObservableObject {
}
}
}
@Published private(set) var isElementFullscreenActive: Bool = false
private var searchNeedleCancellable: AnyCancellable?
let portalAnchorView = BrowserPortalAnchorView(frame: .zero)
private struct PortalHostLease {
@ -2478,6 +2479,7 @@ final class BrowserPanel: Panel, ObservableObject {
// Enable developer extras (DevTools)
configuration.preferences.setValue(true, forKey: "developerExtrasEnabled")
configuration.preferences.isElementFullscreenEnabled = true
// Enable JavaScript
configuration.defaultWebpagePreferences.allowsContentJavaScript = true
@ -3064,6 +3066,27 @@ final class BrowserPanel: Panel, ObservableObject {
}
webViewObservers.append(progressObserver)
let fullscreenObserver = webView.observe(\.fullscreenState, options: [.initial, .new]) { [weak self] webView, _ in
let isElementFullscreenActive = webView.cmuxIsElementFullscreenActiveOrTransitioning
let fullscreenState = webView.fullscreenState
Task { @MainActor in
guard let self, self.isCurrentWebView(webView, instanceID: observedWebViewInstanceID) else { return }
self.isElementFullscreenActive = isElementFullscreenActive
BrowserWindowPortalRegistry.refresh(
webView: webView,
reason: "fullscreenStateChanged"
)
#if DEBUG
dlog(
"browser.fullscreen.state panel=\(self.id.uuidString.prefix(5)) " +
"web=\(ObjectIdentifier(webView)) state=\(String(describing: fullscreenState)) " +
"active=\(isElementFullscreenActive ? 1 : 0)"
)
#endif
}
}
webViewObservers.append(fullscreenObserver)
NotificationCenter.default.publisher(for: .ghosttyDefaultBackgroundDidChange)
.sink { [weak self] notification in
guard let self else { return }

View file

@ -5774,6 +5774,13 @@ struct WebViewRepresentable: NSViewRepresentable {
host.clearLocalInlineCallbacks()
}
private static func shouldPreserveExternalFullscreenHost(
for webView: WKWebView,
relativeTo expectedWindow: NSWindow?
) -> Bool {
webView.cmuxIsManagedByExternalFullscreenWindow(relativeTo: expectedWindow)
}
private static func localInlineTransferRoot(for webView: WKWebView) -> NSView? {
var current = webView.superview
var last: NSView?
@ -5874,7 +5881,12 @@ struct WebViewRepresentable: NSViewRepresentable {
guard let host = nsView as? HostContainerView else { return false }
let slotView = host.ensureLocalInlineSlotView()
let isAlreadyInLocalHost = host.containsManagedLocalInlineContent(webView)
let didAttachWebViewToLocalHost = !isAlreadyInLocalHost
let shouldPreserveExternalFullscreenHost = Self.shouldPreserveExternalFullscreenHost(
for: webView,
relativeTo: host.window
)
let didAttachWebViewToLocalHost =
!isAlreadyInLocalHost && !shouldPreserveExternalFullscreenHost
let coordinator = context.coordinator
coordinator.desiredPortalVisibleInUI = false
@ -5919,6 +5931,16 @@ struct WebViewRepresentable: NSViewRepresentable {
return false
}
#if DEBUG
if shouldPreserveExternalFullscreenHost {
dlog(
"browser.localHost.reparent.skip web=\(Self.objectID(webView)) " +
"reason=fullscreenExternalHost host=\(Self.objectID(host)) " +
"slot=\(Self.objectID(slotView)) state=\(String(describing: webView.fullscreenState))"
)
}
#endif
let preferredAttachedWidthState = panel.preferredAttachedDeveloperToolsWidthState()
host.setPreferredHostedInspectorWidth(
width: preferredAttachedWidthState.width,
@ -5971,7 +5993,7 @@ struct WebViewRepresentable: NSViewRepresentable {
host.setHostedInspectorFrontendWebView(webView.cmuxInspectorFrontendWebView())
host.scheduleHostedInspectorDockConfigurationSync(reason: "localInline.update.async")
}
} else {
} else if !shouldPreserveExternalFullscreenHost {
panel.consumeAttachedDeveloperToolsManualCloseIfNeeded()
host.scheduleHostedInspectorDockConfigurationSync(reason: "localInline.update")
}
@ -5985,7 +6007,7 @@ struct WebViewRepresentable: NSViewRepresentable {
details: Self.attachContext(webView: webView, host: host)
)
#endif
return true
return !shouldPreserveExternalFullscreenHost
}
private func updateUsingWindowPortal(_ nsView: NSView, context: Context, webView: WKWebView) -> Bool {
@ -5993,6 +6015,10 @@ struct WebViewRepresentable: NSViewRepresentable {
host.prepareForWindowPortalHosting()
host.setLocalInlineSlotHidden(true)
host.releaseHostedWebViewConstraints()
let shouldPreserveExternalFullscreenHost = Self.shouldPreserveExternalFullscreenHost(
for: webView,
relativeTo: host.window
)
let coordinator = context.coordinator
let paneDropContext = currentPaneDropContext()
@ -6188,7 +6214,7 @@ struct WebViewRepresentable: NSViewRepresentable {
details: Self.attachContext(webView: webView, host: host)
)
#endif
return portalHostAccepted
return portalHostAccepted && !shouldPreserveExternalFullscreenHost
}
func updateNSView(_ nsView: NSView, context: Context) {

View file

@ -4,6 +4,25 @@ import ObjectiveC
import UniformTypeIdentifiers
import WebKit
extension WKWebView {
var cmuxIsElementFullscreenActiveOrTransitioning: Bool {
switch fullscreenState {
case .notInFullscreen:
return false
case .enteringFullscreen, .inFullscreen, .exitingFullscreen:
return true
@unknown default:
return true
}
}
func cmuxIsManagedByExternalFullscreenWindow(relativeTo expectedWindow: NSWindow?) -> Bool {
guard cmuxIsElementFullscreenActiveOrTransitioning else { return false }
guard let expectedWindow else { return true }
return window !== expectedWindow
}
}
struct BrowserImageCopyPasteboardPayload {
let imageData: Data
let mimeType: String?