diff --git a/Sources/BrowserWindowPortal.swift b/Sources/BrowserWindowPortal.swift index 915467e2..10e0b8b0 100644 --- a/Sources/BrowserWindowPortal.swift +++ b/Sources/BrowserWindowPortal.swift @@ -1672,9 +1672,13 @@ final class WindowBrowserSlotView: NSView { func pinHostedWebView(_ webView: WKWebView) { guard webView.superview === self else { return } + let needsPlainWebViewFrameReset = + !Self.hasWebKitCompanionSubview(in: self, primaryWebView: webView) && + Self.frameDiffersFromBounds(webView.frame, bounds: bounds) let needsFrameHosting = hostedWebView !== webView || !hostedWebViewConstraints.isEmpty || + needsPlainWebViewFrameReset || !webView.translatesAutoresizingMaskIntoConstraints || webView.autoresizingMask != [.width, .height] guard needsFrameHosting else { @@ -1687,8 +1691,8 @@ final class WindowBrowserSlotView: NSView { hostedWebViewConstraints = [] hostedWebView = webView // Attached Web Inspector mutates the moved WKWebView's frame directly. - // Re-pin only when hosting mode changes, not when WebKit resizes the page - // inside the slot for side-docked DevTools. + // Re-pin plain web views after cross-host reattach, but preserve the + // WebKit-managed split frame when docked DevTools siblings are present. webView.translatesAutoresizingMaskIntoConstraints = true webView.autoresizingMask = [.width, .height] webView.frame = bounds @@ -1696,6 +1700,27 @@ final class WindowBrowserSlotView: NSView { layoutSubtreeIfNeeded() } + private static func frameDiffersFromBounds(_ frame: NSRect, bounds: NSRect, epsilon: CGFloat = 0.5) -> Bool { + abs(frame.minX - bounds.minX) > epsilon || + abs(frame.minY - bounds.minY) > epsilon || + abs(frame.width - bounds.width) > epsilon || + abs(frame.height - bounds.height) > epsilon + } + + private static func hasWebKitCompanionSubview(in host: NSView, primaryWebView: WKWebView) -> Bool { + var stack = host.subviews.filter { $0 !== primaryWebView } + while let current = stack.popLast() { + if current.isDescendant(of: primaryWebView) { + continue + } + if String(describing: type(of: current)).contains("WK") { + return true + } + stack.append(contentsOf: current.subviews) + } + return false + } + func effectivePaneTopChromeHeight() -> CGFloat { paneTopChromeHeight } @@ -1975,6 +2000,7 @@ final class WindowBrowserPortal: NSObject { guard let webView = entry.webView, let containerView = entry.containerView, !containerView.isHidden else { continue } + guard webView.superview === containerView else { continue } refreshHostedWebViewPresentation( webView, in: containerView, @@ -2650,7 +2676,7 @@ final class WindowBrowserPortal: NSObject { containerView.setPaneTopChromeHeight(0) containerView.setSearchOverlay(nil) containerView.setDropZoneOverlay(zone: nil) - if !containerView.isHidden { + if !containerView.isHidden, webView.superview === containerView { webView.browserPortalNotifyHidden(reason: reason) } containerView.isHidden = true