From baee7c6e85c8b080da72d9704ae261e49573c433 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:06:04 -0800 Subject: [PATCH] Harden Sentry crash guards and reduce noisy events --- Sources/WindowDragHandleView.swift | 6 ++-- cmuxTests/CmuxWebViewKeyEquivalentTests.swift | 32 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Sources/WindowDragHandleView.swift b/Sources/WindowDragHandleView.swift index 8786dae8..d0ffd48b 100644 --- a/Sources/WindowDragHandleView.swift +++ b/Sources/WindowDragHandleView.swift @@ -254,11 +254,13 @@ func windowDragHandleShouldCaptureHit(_ point: NSPoint, in dragHandleView: NSVie } } + let siblingSnapshot = Array(superview.subviews.reversed()) + #if DEBUG - let siblingCount = superview.subviews.count + let siblingCount = siblingSnapshot.count #endif - for sibling in superview.subviews.reversed() { + for sibling in siblingSnapshot { guard sibling !== dragHandleView else { continue } guard !sibling.isHidden, sibling.alphaValue > 0 else { continue } diff --git a/cmuxTests/CmuxWebViewKeyEquivalentTests.swift b/cmuxTests/CmuxWebViewKeyEquivalentTests.swift index 8f8afdf6..63f78cde 100644 --- a/cmuxTests/CmuxWebViewKeyEquivalentTests.swift +++ b/cmuxTests/CmuxWebViewKeyEquivalentTests.swift @@ -145,7 +145,6 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { set {} } } - func testCmdNRoutesToMainMenuWhenWebViewIsFirstResponder() { let spy = ActionSpy() installMenu(spy: spy, key: "n", modifiers: [.command]) @@ -529,7 +528,6 @@ final class CmuxWebViewKeyEquivalentTests: XCTestCase { } XCTAssertTrue(window.makeFirstResponder(responder)) } - private func installMenu(spy: ActionSpy, key: String, modifiers: NSEvent.ModifierFlags) { let mainMenu = NSMenu() @@ -5899,6 +5897,21 @@ final class WindowDragHandleHitTests: XCTestCase { } } + private final class MutatingSiblingView: NSView { + weak var container: NSView? + private var didMutate = false + + override func hitTest(_ point: NSPoint) -> NSView? { + guard bounds.contains(point) else { return nil } + guard !didMutate, let container else { return nil } + didMutate = true + let transient = NSView(frame: .zero) + container.addSubview(transient) + transient.removeFromSuperview() + return nil + } + } + func testDragHandleCapturesHitWhenNoSiblingClaimsPoint() { let container = NSView(frame: NSRect(x: 0, y: 0, width: 220, height: 36)) let dragHandle = NSView(frame: container.bounds) @@ -6040,6 +6053,21 @@ final class WindowDragHandleHitTests: XCTestCase { "Top-hit recursion in one window must not disable top-hit resolution in another window" ) } + + func testDragHandleRemainsStableWhenSiblingMutatesSubviewsDuringHitTest() { + let container = NSView(frame: NSRect(x: 0, y: 0, width: 220, height: 36)) + let dragHandle = NSView(frame: container.bounds) + container.addSubview(dragHandle) + + let mutatingSibling = MutatingSiblingView(frame: container.bounds) + mutatingSibling.container = container + container.addSubview(mutatingSibling) + + XCTAssertTrue( + windowDragHandleShouldCaptureHit(NSPoint(x: 180, y: 18), in: dragHandle), + "Subview mutations during hit testing should not crash or break drag-handle capture" + ) + } } @MainActor