Repair bottom-docked devtools page frame

This commit is contained in:
Lawrence Chen 2026-03-11 18:11:18 -07:00
parent 94c5d621ec
commit fbd04ca09e

View file

@ -2229,6 +2229,71 @@ final class WindowBrowserPortal: NSObject {
frame.maxY > bounds.maxY + epsilon
}
private static func hasVisibleInspectorDescendant(in root: NSView) -> Bool {
var stack: [NSView] = [root]
while let current = stack.popLast() {
if current !== root {
let className = String(describing: type(of: current))
if className.contains("WKInspector"),
!current.isHidden,
current.alphaValue > 0,
current.frame.width > 1,
current.frame.height > 1 {
return true
}
}
stack.append(contentsOf: current.subviews)
}
return false
}
private static func inferredBottomDockedInspectorFrame(
in containerView: NSView,
primaryWebView: WKWebView,
epsilon: CGFloat = 1
) -> NSRect? {
let pageFrame = primaryWebView.frame
let containerBounds = containerView.bounds
let candidates = containerView.subviews.compactMap { candidate -> NSRect? in
guard candidate !== primaryWebView else { return nil }
guard hasVisibleInspectorDescendant(in: candidate) else { return nil }
let frame = candidate.frame
guard frame.width > 1, frame.height > 1 else { return nil }
let overlapWidth = min(pageFrame.maxX, frame.maxX) - max(pageFrame.minX, frame.minX)
guard overlapWidth > min(pageFrame.width, frame.width) * 0.7 else { return nil }
guard frame.minY <= containerBounds.minY + epsilon else { return nil }
guard frame.maxY <= pageFrame.minY + epsilon else { return nil }
return frame
}
return candidates.max(by: { $0.height < $1.height })
}
private static func repairedBottomDockedPageFrame(
in containerView: NSView,
primaryWebView: WKWebView,
epsilon: CGFloat = 0.5
) -> NSRect? {
let pageFrame = primaryWebView.frame
let containerBounds = containerView.bounds
guard frameExtendsOutsideBounds(pageFrame, bounds: containerBounds, epsilon: epsilon),
let inspectorFrame = inferredBottomDockedInspectorFrame(
in: containerView,
primaryWebView: primaryWebView
) else {
return nil
}
return NSRect(
x: containerBounds.minX,
y: inspectorFrame.maxY,
width: containerBounds.width,
height: max(0, containerBounds.maxY - inspectorFrame.maxY)
)
}
#if DEBUG
private static func inspectorSubviewCount(in root: NSView) -> Int {
var stack: [NSView] = [root]
@ -3073,7 +3138,30 @@ final class WindowBrowserPortal: NSObject {
#if DEBUG
let inspectorSubviews = Self.inspectorSubviewCount(in: containerView)
#endif
if containerOwnsWebView && Self.frameExtendsOutsideBounds(preNormalizeWebFrame, bounds: containerBounds) {
if containerOwnsWebView,
let repairedBottomDockFrame = Self.repairedBottomDockedPageFrame(
in: containerView,
primaryWebView: webView
) {
let oldWebFrame = preNormalizeWebFrame
CATransaction.begin()
CATransaction.setDisableActions(true)
webView.frame = repairedBottomDockFrame
CATransaction.commit()
#if DEBUG
dlog(
"browser.portal.webframe.bottomDockRepair web=\(browserPortalDebugToken(webView)) " +
"container=\(browserPortalDebugToken(containerView)) old=\(browserPortalDebugFrame(oldWebFrame)) " +
"new=\(browserPortalDebugFrame(repairedBottomDockFrame)) bounds=\(browserPortalDebugFrame(containerBounds)) " +
"inspectorHApprox=\(String(format: "%.1f", inspectorHeightApprox)) " +
"inspectorInsets=\(String(format: "%.1f", inspectorHeightFromInsets)) " +
"inspectorOverflow=\(String(format: "%.1f", inspectorHeightFromOverflow)) " +
"inspectorSubviews=\(inspectorSubviews) " +
"source=\(source)"
)
#endif
refreshReasons.append("webFrameBottomDock")
} else if containerOwnsWebView && Self.frameExtendsOutsideBounds(preNormalizeWebFrame, bounds: containerBounds) {
let oldWebFrame = preNormalizeWebFrame
CATransaction.begin()
CATransaction.setDisableActions(true)