Merge pull request #1173 from manaflow-ai/task-browser-pane-devtools-resize-disappear
Preserve browser devtools when resizing pane
This commit is contained in:
commit
bcb712fde4
3 changed files with 344 additions and 13 deletions
|
|
@ -1672,12 +1672,15 @@ 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] ||
|
||||
!Self.rectApproximatelyEqual(webView.frame, bounds)
|
||||
webView.autoresizingMask != [.width, .height]
|
||||
guard needsFrameHosting else {
|
||||
needsLayout = true
|
||||
layoutSubtreeIfNeeded()
|
||||
|
|
@ -1688,7 +1691,8 @@ final class WindowBrowserSlotView: NSView {
|
|||
hostedWebViewConstraints = []
|
||||
hostedWebView = webView
|
||||
// Attached Web Inspector mutates the moved WKWebView's frame directly.
|
||||
// Edge constraints fight side-docked resizing and cause visible churn.
|
||||
// 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
|
||||
|
|
@ -2752,7 +2778,18 @@ final class WindowBrowserPortal: NSObject {
|
|||
hostView.addSubview(containerView, positioned: .above, relativeTo: nil)
|
||||
refreshReasons.append("syncAttachContainer")
|
||||
}
|
||||
if webView.superview !== containerView {
|
||||
let shouldPreserveExternalHostForHiddenEntry =
|
||||
!entry.visibleInUI &&
|
||||
webView.superview !== containerView
|
||||
if shouldPreserveExternalHostForHiddenEntry {
|
||||
#if DEBUG
|
||||
dlog(
|
||||
"browser.portal.reparent.skip web=\(browserPortalDebugToken(webView)) " +
|
||||
"reason=hiddenEntryExternalHost super=\(browserPortalDebugToken(webView.superview)) " +
|
||||
"container=\(browserPortalDebugToken(containerView))"
|
||||
)
|
||||
#endif
|
||||
} else if webView.superview !== containerView {
|
||||
#if DEBUG
|
||||
dlog(
|
||||
"browser.portal.reparent web=\(browserPortalDebugToken(webView)) " +
|
||||
|
|
@ -2943,15 +2980,16 @@ final class WindowBrowserPortal: NSObject {
|
|||
refreshReasons.append("bounds")
|
||||
}
|
||||
|
||||
let containerOwnsWebView = webView.superview === containerView
|
||||
let containerBounds = containerView.bounds
|
||||
let preNormalizeWebFrame = webView.frame
|
||||
let preNormalizeWebFrame = containerOwnsWebView ? webView.frame : .zero
|
||||
let inspectorHeightFromInsets = max(0, containerBounds.height - preNormalizeWebFrame.height)
|
||||
let inspectorHeightFromOverflow = max(0, preNormalizeWebFrame.maxY - containerBounds.maxY)
|
||||
let inspectorHeightApprox = max(inspectorHeightFromInsets, inspectorHeightFromOverflow)
|
||||
#if DEBUG
|
||||
let inspectorSubviews = Self.inspectorSubviewCount(in: containerView)
|
||||
#endif
|
||||
if Self.frameExtendsOutsideBounds(preNormalizeWebFrame, bounds: containerBounds) {
|
||||
if containerOwnsWebView && Self.frameExtendsOutsideBounds(preNormalizeWebFrame, bounds: containerBounds) {
|
||||
let oldWebFrame = preNormalizeWebFrame
|
||||
CATransaction.begin()
|
||||
CATransaction.setDisableActions(true)
|
||||
|
|
@ -3010,14 +3048,16 @@ final class WindowBrowserPortal: NSObject {
|
|||
if transientRecoveryReason == nil {
|
||||
resetTransientRecoveryRetryIfNeeded(forWebViewId: webViewId, entry: &entry)
|
||||
}
|
||||
if !shouldHide, !refreshReasons.isEmpty {
|
||||
if !shouldHide, containerOwnsWebView, !refreshReasons.isEmpty {
|
||||
refreshHostedWebViewPresentation(
|
||||
webView,
|
||||
in: containerView,
|
||||
reason: "\(source):" + refreshReasons.joined(separator: ",")
|
||||
)
|
||||
}
|
||||
hostView.reapplyHostedInspectorDividerIfNeeded(in: containerView, reason: "portal.sync")
|
||||
if containerOwnsWebView {
|
||||
hostView.reapplyHostedInspectorDividerIfNeeded(in: containerView, reason: "portal.sync")
|
||||
}
|
||||
#if DEBUG
|
||||
dlog(
|
||||
"browser.portal.sync.result web=\(browserPortalDebugToken(webView)) source=\(source) " +
|
||||
|
|
@ -3027,6 +3067,7 @@ final class WindowBrowserPortal: NSObject {
|
|||
"old=\(browserPortalDebugFrame(oldFrame)) raw=\(browserPortalDebugFrame(frameInHost)) " +
|
||||
"target=\(browserPortalDebugFrame(targetFrame)) hide=\(shouldHide ? 1 : 0) " +
|
||||
"entryVisible=\(entry.visibleInUI ? 1 : 0) " +
|
||||
"containerOwnsWeb=\(containerOwnsWebView ? 1 : 0) " +
|
||||
"containerHidden=\(containerView.isHidden ? 1 : 0) webHidden=\(webView.isHidden ? 1 : 0) " +
|
||||
"containerBounds=\(browserPortalDebugFrame(containerView.bounds)) " +
|
||||
"preWebFrame=\(browserPortalDebugFrame(preNormalizeWebFrame)) " +
|
||||
|
|
|
|||
|
|
@ -3786,8 +3786,7 @@ struct WebViewRepresentable: NSViewRepresentable {
|
|||
hostedWebView !== webView ||
|
||||
!hostedWebViewConstraints.isEmpty ||
|
||||
!webView.translatesAutoresizingMaskIntoConstraints ||
|
||||
webView.autoresizingMask != [.width, .height] ||
|
||||
webView.frame != container.bounds
|
||||
webView.autoresizingMask != [.width, .height]
|
||||
guard needsFrameHosting else {
|
||||
needsLayout = true
|
||||
layoutSubtreeIfNeeded()
|
||||
|
|
@ -3799,8 +3798,8 @@ struct WebViewRepresentable: NSViewRepresentable {
|
|||
hostedWebView = webView
|
||||
|
||||
// WebKit's attached inspector does not reliably dock into a constraint-managed
|
||||
// WKWebView hierarchy on macOS. Host the moved webview with autoresizing so
|
||||
// the inspector can resize the content view in place.
|
||||
// WKWebView hierarchy on macOS. Host the moved webview with autoresizing and
|
||||
// keep WebKit-owned page frames intact when DevTools is side-docked.
|
||||
webView.translatesAutoresizingMaskIntoConstraints = true
|
||||
webView.autoresizingMask = [.width, .height]
|
||||
webView.frame = container.bounds
|
||||
|
|
@ -4487,6 +4486,34 @@ struct WebViewRepresentable: NSViewRepresentable {
|
|||
)
|
||||
}
|
||||
|
||||
let shouldPreserveExistingExternalLocalHost =
|
||||
host.window == nil &&
|
||||
webView.superview != nil &&
|
||||
webView.superview !== slotView
|
||||
if shouldPreserveExistingExternalLocalHost {
|
||||
// Split zoom can instantiate a replacement local host before it joins a window.
|
||||
// Never let that off-window host steal the live page + inspector hierarchy away
|
||||
// from the currently visible local host.
|
||||
host.setLocalInlineSlotHidden(true)
|
||||
coordinator.lastPortalHostId = nil
|
||||
coordinator.lastSynchronizedHostGeometryRevision = 0
|
||||
#if DEBUG
|
||||
dlog(
|
||||
"browser.localHost.reparent.skip web=\(Self.objectID(webView)) " +
|
||||
"reason=offWindowReplacementHost super=\(Self.objectID(webView.superview)) " +
|
||||
"host=\(Self.objectID(host)) slot=\(Self.objectID(slotView))"
|
||||
)
|
||||
Self.logDevToolsState(
|
||||
panel,
|
||||
event: "localHost.skip",
|
||||
generation: coordinator.attachGeneration,
|
||||
retryCount: 0,
|
||||
details: Self.attachContext(webView: webView, host: host)
|
||||
)
|
||||
#endif
|
||||
return false
|
||||
}
|
||||
|
||||
if webView.superview !== slotView {
|
||||
if let sourceSuperview = webView.superview {
|
||||
Self.moveWebKitRelatedSubviewsIntoHostIfNeeded(
|
||||
|
|
|
|||
|
|
@ -2481,6 +2481,18 @@ final class BrowserDeveloperToolsVisibilityPersistenceTests: XCTestCase {
|
|||
return (panel, inspector)
|
||||
}
|
||||
|
||||
private func findHostContainerView(in root: NSView) -> WebViewRepresentable.HostContainerView? {
|
||||
if let host = root as? WebViewRepresentable.HostContainerView {
|
||||
return host
|
||||
}
|
||||
for subview in root.subviews {
|
||||
if let host = findHostContainerView(in: subview) {
|
||||
return host
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func testRestoreReopensInspectorAfterAttachWhenPreferredVisible() {
|
||||
let (panel, inspector) = makePanelWithInspector()
|
||||
|
||||
|
|
@ -2691,6 +2703,89 @@ final class BrowserDeveloperToolsVisibilityPersistenceTests: XCTestCase {
|
|||
|
||||
XCTAssertTrue(panel.shouldPreserveWebViewAttachmentDuringTransientHide())
|
||||
}
|
||||
|
||||
func testOffWindowReplacementLocalHostDoesNotStealVisibleDevToolsWebView() {
|
||||
let (panel, _) = makePanelWithInspector()
|
||||
XCTAssertTrue(panel.showDeveloperTools())
|
||||
|
||||
let paneId = PaneID(id: UUID())
|
||||
let representable = WebViewRepresentable(
|
||||
panel: panel,
|
||||
paneId: paneId,
|
||||
shouldAttachWebView: false,
|
||||
useLocalInlineHosting: true,
|
||||
shouldFocusWebView: false,
|
||||
isPanelFocused: true,
|
||||
portalZPriority: 0,
|
||||
paneDropZone: nil,
|
||||
searchOverlay: nil,
|
||||
paneTopChromeHeight: 0
|
||||
)
|
||||
|
||||
let window = NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 360, height: 240),
|
||||
styleMask: [.titled, .closable],
|
||||
backing: .buffered,
|
||||
defer: false
|
||||
)
|
||||
defer { window.orderOut(nil) }
|
||||
guard let contentView = window.contentView else {
|
||||
XCTFail("Expected content view")
|
||||
return
|
||||
}
|
||||
|
||||
let visibleHosting = NSHostingView(rootView: representable)
|
||||
visibleHosting.frame = contentView.bounds
|
||||
visibleHosting.autoresizingMask = [.width, .height]
|
||||
contentView.addSubview(visibleHosting)
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
window.displayIfNeeded()
|
||||
contentView.layoutSubtreeIfNeeded()
|
||||
visibleHosting.layoutSubtreeIfNeeded()
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.05))
|
||||
|
||||
guard let visibleHost = findHostContainerView(in: visibleHosting) else {
|
||||
XCTFail("Expected visible local host")
|
||||
return
|
||||
}
|
||||
guard let visibleSlot = panel.webView.superview as? WindowBrowserSlotView else {
|
||||
XCTFail("Expected visible local inline slot")
|
||||
return
|
||||
}
|
||||
|
||||
let inspectorView = WKInspectorProbeView(
|
||||
frame: NSRect(x: 0, y: 0, width: visibleSlot.bounds.width, height: 72)
|
||||
)
|
||||
inspectorView.autoresizingMask = [.width]
|
||||
visibleSlot.addSubview(inspectorView)
|
||||
panel.webView.frame = NSRect(
|
||||
x: 0,
|
||||
y: inspectorView.frame.maxY,
|
||||
width: visibleSlot.bounds.width,
|
||||
height: visibleSlot.bounds.height - inspectorView.frame.height
|
||||
)
|
||||
visibleSlot.layoutSubtreeIfNeeded()
|
||||
|
||||
let detachedRoot = NSView(frame: visibleHosting.frame)
|
||||
let offWindowHosting = NSHostingView(rootView: representable)
|
||||
offWindowHosting.frame = detachedRoot.bounds
|
||||
offWindowHosting.autoresizingMask = [.width, .height]
|
||||
detachedRoot.addSubview(offWindowHosting)
|
||||
detachedRoot.layoutSubtreeIfNeeded()
|
||||
offWindowHosting.layoutSubtreeIfNeeded()
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.05))
|
||||
|
||||
XCTAssertNotNil(findHostContainerView(in: offWindowHosting), "Expected off-window replacement host")
|
||||
XCTAssertTrue(visibleHost.window === window)
|
||||
XCTAssertTrue(
|
||||
panel.webView.superview === visibleSlot,
|
||||
"An off-window replacement host should not steal a visible DevTools-hosted web view during split zoom churn"
|
||||
)
|
||||
XCTAssertTrue(
|
||||
inspectorView.superview === visibleSlot,
|
||||
"An off-window replacement host should leave DevTools companion views in the visible local host"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
final class WorkspaceShortcutMapperTests: XCTestCase {
|
||||
|
|
@ -9370,6 +9465,33 @@ final class BrowserPanelHostContainerViewTests: XCTestCase {
|
|||
XCTAssertEqual(webView.autoresizingMask, [.width, .height])
|
||||
XCTAssertEqual(webView.frame, slot.bounds)
|
||||
}
|
||||
|
||||
func testWindowBrowserSlotReattachesPlainWebViewAtFullBoundsAfterHiddenHostResize() {
|
||||
let slot = WindowBrowserSlotView(frame: NSRect(x: 0, y: 0, width: 400, height: 180))
|
||||
let webView = WKWebView(frame: .zero)
|
||||
slot.addSubview(webView)
|
||||
slot.pinHostedWebView(webView)
|
||||
XCTAssertEqual(webView.frame, slot.bounds)
|
||||
|
||||
let externalHost = NSView(frame: NSRect(x: 0, y: 0, width: 300, height: 180))
|
||||
webView.removeFromSuperview()
|
||||
externalHost.addSubview(webView)
|
||||
webView.frame = externalHost.bounds
|
||||
webView.translatesAutoresizingMaskIntoConstraints = true
|
||||
webView.autoresizingMask = [.width, .height]
|
||||
|
||||
slot.addSubview(webView)
|
||||
slot.pinHostedWebView(webView)
|
||||
|
||||
slot.frame = NSRect(x: 0, y: 0, width: 300, height: 180)
|
||||
slot.layoutSubtreeIfNeeded()
|
||||
|
||||
XCTAssertEqual(
|
||||
webView.frame,
|
||||
slot.bounds,
|
||||
"Reattaching a plain web view should restore full-bounds hosting instead of preserving a stale inset frame from a hidden host"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
|
|
@ -11010,6 +11132,8 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private final class WKInspectorProbeView: NSView {}
|
||||
|
||||
private func realizeWindowLayout(_ window: NSWindow) {
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
window.displayIfNeeded()
|
||||
|
|
@ -11274,6 +11398,145 @@ final class BrowserWindowPortalLifecycleTests: XCTestCase {
|
|||
XCTAssertEqual(webView.frame.size.height, slot.bounds.size.height, accuracy: 0.5)
|
||||
}
|
||||
|
||||
func testPortalResizePreservesSideDockedInspectorManagedWebViewFrame() {
|
||||
let window = NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 520, height: 320),
|
||||
styleMask: [.titled, .closable],
|
||||
backing: .buffered,
|
||||
defer: false
|
||||
)
|
||||
defer { window.orderOut(nil) }
|
||||
realizeWindowLayout(window)
|
||||
let portal = WindowBrowserPortal(window: window)
|
||||
guard let contentView = window.contentView else {
|
||||
XCTFail("Expected content view")
|
||||
return
|
||||
}
|
||||
|
||||
let anchor = NSView(frame: NSRect(x: 40, y: 24, width: 260, height: 180))
|
||||
contentView.addSubview(anchor)
|
||||
|
||||
let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration())
|
||||
portal.bind(webView: webView, to: anchor, visibleInUI: true)
|
||||
contentView.layoutSubtreeIfNeeded()
|
||||
portal.synchronizeWebViewForAnchor(anchor)
|
||||
|
||||
guard let slot = webView.superview as? WindowBrowserSlotView else {
|
||||
XCTFail("Expected browser slot")
|
||||
return
|
||||
}
|
||||
|
||||
let initialInspectorWidth: CGFloat = 110
|
||||
let inspectorContainer = NSView(
|
||||
frame: NSRect(
|
||||
x: slot.bounds.width - initialInspectorWidth,
|
||||
y: 0,
|
||||
width: initialInspectorWidth,
|
||||
height: slot.bounds.height
|
||||
)
|
||||
)
|
||||
inspectorContainer.autoresizingMask = [.minXMargin, .height]
|
||||
let inspectorView = WKInspectorProbeView(frame: inspectorContainer.bounds)
|
||||
inspectorView.autoresizingMask = [.width, .height]
|
||||
inspectorContainer.addSubview(inspectorView)
|
||||
slot.addSubview(inspectorContainer)
|
||||
|
||||
webView.frame = NSRect(
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: slot.bounds.width - initialInspectorWidth,
|
||||
height: slot.bounds.height
|
||||
)
|
||||
webView.autoresizingMask = [.width, .height]
|
||||
slot.layoutSubtreeIfNeeded()
|
||||
|
||||
anchor.frame = NSRect(x: 40, y: 24, width: 220, height: 180)
|
||||
contentView.layoutSubtreeIfNeeded()
|
||||
portal.synchronizeWebViewForAnchor(anchor)
|
||||
|
||||
XCTAssertFalse(slot.isHidden, "Resizing the browser pane should keep the hosted browser visible")
|
||||
XCTAssertEqual(
|
||||
webView.frame.maxX,
|
||||
inspectorContainer.frame.minX,
|
||||
accuracy: 0.5,
|
||||
"Portal sync should preserve the side-docked inspector split instead of stretching the page back over the inspector"
|
||||
)
|
||||
XCTAssertLessThan(
|
||||
webView.frame.width,
|
||||
slot.bounds.width,
|
||||
"Side-docked inspector should still own part of the slot after pane resize"
|
||||
)
|
||||
}
|
||||
|
||||
func testHiddenPortalSyncDoesNotStealLocallyHostedDevToolsWebViewDuringResize() {
|
||||
let window = NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 520, height: 320),
|
||||
styleMask: [.titled, .closable],
|
||||
backing: .buffered,
|
||||
defer: false
|
||||
)
|
||||
defer { window.orderOut(nil) }
|
||||
realizeWindowLayout(window)
|
||||
let portal = WindowBrowserPortal(window: window)
|
||||
guard let contentView = window.contentView else {
|
||||
XCTFail("Expected content view")
|
||||
return
|
||||
}
|
||||
|
||||
let anchor = NSView(frame: NSRect(x: 40, y: 24, width: 260, height: 180))
|
||||
contentView.addSubview(anchor)
|
||||
|
||||
let webView = CmuxWebView(frame: .zero, configuration: WKWebViewConfiguration())
|
||||
portal.bind(webView: webView, to: anchor, visibleInUI: true)
|
||||
contentView.layoutSubtreeIfNeeded()
|
||||
portal.synchronizeWebViewForAnchor(anchor)
|
||||
advanceAnimations()
|
||||
|
||||
guard let hiddenPortalSlot = webView.superview as? WindowBrowserSlotView else {
|
||||
XCTFail("Expected browser slot")
|
||||
return
|
||||
}
|
||||
|
||||
portal.updateEntryVisibility(forWebViewId: ObjectIdentifier(webView), visibleInUI: false, zPriority: 0)
|
||||
portal.synchronizeWebViewForAnchor(anchor)
|
||||
advanceAnimations()
|
||||
XCTAssertTrue(hiddenPortalSlot.isHidden, "Hidden portal entry should keep its slot hidden")
|
||||
|
||||
let localInlineSlot = WindowBrowserSlotView(frame: anchor.frame)
|
||||
contentView.addSubview(localInlineSlot)
|
||||
|
||||
let inspectorView = WKInspectorProbeView(
|
||||
frame: NSRect(x: 0, y: 0, width: localInlineSlot.bounds.width, height: 72)
|
||||
)
|
||||
inspectorView.autoresizingMask = [.width]
|
||||
localInlineSlot.addSubview(inspectorView)
|
||||
|
||||
localInlineSlot.addSubview(webView)
|
||||
webView.frame = NSRect(
|
||||
x: 0,
|
||||
y: inspectorView.frame.maxY,
|
||||
width: localInlineSlot.bounds.width,
|
||||
height: localInlineSlot.bounds.height - inspectorView.frame.height
|
||||
)
|
||||
localInlineSlot.layoutSubtreeIfNeeded()
|
||||
|
||||
anchor.frame = NSRect(x: 40, y: 24, width: 220, height: 180)
|
||||
localInlineSlot.frame = anchor.frame
|
||||
contentView.layoutSubtreeIfNeeded()
|
||||
localInlineSlot.layoutSubtreeIfNeeded()
|
||||
portal.synchronizeWebViewForAnchor(anchor)
|
||||
|
||||
XCTAssertTrue(
|
||||
webView.superview === localInlineSlot,
|
||||
"Hidden portal sync should not steal a DevTools-hosted web view back out of local inline hosting during pane resize"
|
||||
)
|
||||
XCTAssertTrue(
|
||||
inspectorView.superview === localInlineSlot,
|
||||
"Hidden portal sync should leave local DevTools companion views in the local inline host"
|
||||
)
|
||||
XCTAssertTrue(hiddenPortalSlot.isHidden, "The retiring hidden portal slot should stay hidden during local inline hosting")
|
||||
}
|
||||
|
||||
func testPortalHostBoundsBecomeReadyAfterBindingInFrameDrivenHierarchy() {
|
||||
let window = NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 500, height: 320),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue