Harden file-drop routing for local tab drags

This commit is contained in:
Lawrence Chen 2026-02-20 16:27:12 -08:00
parent a5d724cf28
commit 146ac253af
4 changed files with 193 additions and 29 deletions

View file

@ -3,12 +3,12 @@
Regression test: file-drop overlay must not intercept bonsplit tab-transfer drags.
This test is socket-only (no System Events / Accessibility permissions required).
It validates the FileDropOverlayView hit-test gate logic:
It validates both FileDropOverlayView hit-test and drag-destination gate logic:
1) tabtransfer pasteboard type never captures hit-testing
2) sidebar reorder pasteboard type never captures hit-testing
3) fileURL pasteboard captures only drag-motion mouse events
4) stale/no-event contexts do not capture hit-testing
1) tabtransfer/sidebar-reorder payloads never capture
2) fileURL captures only valid external file-drop paths
3) local drags are never captured by file-drop destination routing
4) mixed payloads (fileURL + tabtransfer/sidebar) are never captured
"""
import os
@ -42,6 +42,8 @@ def wait_for_overlay_probe_ready(client: cmux, timeout_s: float = 8.0) -> None:
while time.time() - start < timeout_s:
try:
_ = client.overlay_hit_gate("none")
_ = client.overlay_drop_gate("external")
_ = client.overlay_drop_gate("local")
return
except Exception as e:
last_error = e
@ -57,6 +59,14 @@ def assert_gate(client: cmux, event_type: str, expected: bool, reason: str) -> N
)
def assert_drop_gate(client: cmux, source: str, expected: bool, reason: str) -> None:
got = client.overlay_drop_gate(source)
if got != expected:
raise cmuxError(
f"overlay_drop_gate({source}) expected {expected} got {got} ({reason})"
)
def main() -> int:
socket_path = cmux.default_socket_path()
if not os.path.exists(socket_path):
@ -79,22 +89,42 @@ def main() -> int:
client.clear_drag_pasteboard()
for event in DRAG_EVENTS + NON_DRAG_EVENTS + ["none"]:
assert_gate(client, event, expected=False, reason="empty drag pasteboard")
assert_drop_gate(client, "external", expected=False, reason="empty pasteboard")
assert_drop_gate(client, "local", expected=False, reason="empty pasteboard")
client.seed_drag_pasteboard_tabtransfer()
for event in DRAG_EVENTS + NON_DRAG_EVENTS + ["none"]:
assert_gate(client, event, expected=False, reason="tabtransfer drag must pass through")
assert_drop_gate(client, "external", expected=False, reason="tabtransfer drag must pass through")
assert_drop_gate(client, "local", expected=False, reason="tabtransfer drag must pass through")
client.seed_drag_pasteboard_sidebar_reorder()
for event in DRAG_EVENTS + NON_DRAG_EVENTS + ["none"]:
assert_gate(client, event, expected=False, reason="sidebar reorder drag must pass through")
assert_drop_gate(client, "external", expected=False, reason="sidebar reorder drag must pass through")
assert_drop_gate(client, "local", expected=False, reason="sidebar reorder drag must pass through")
client.seed_drag_pasteboard_fileurl()
for event in DRAG_EVENTS:
assert_gate(client, event, expected=True, reason="file URL drag should be captured")
for event in NON_DRAG_EVENTS + ["none"]:
assert_gate(client, event, expected=False, reason="non-drag events should pass through")
assert_drop_gate(client, "external", expected=True, reason="external file drags should be captured")
assert_drop_gate(client, "local", expected=False, reason="local drags must not be captured")
print("PASS: overlay hit-test gate preserves bonsplit tab drags and file-drop behavior")
client.seed_drag_pasteboard_types(["fileurl", "tabtransfer"])
for event in DRAG_EVENTS + NON_DRAG_EVENTS + ["none"]:
assert_gate(client, event, expected=False, reason="fileurl+tabtransfer must pass through")
assert_drop_gate(client, "external", expected=False, reason="fileurl+tabtransfer must pass through")
assert_drop_gate(client, "local", expected=False, reason="fileurl+tabtransfer must pass through")
client.seed_drag_pasteboard_types(["fileurl", "sidebarreorder"])
for event in DRAG_EVENTS + NON_DRAG_EVENTS + ["none"]:
assert_gate(client, event, expected=False, reason="fileurl+sidebarreorder must pass through")
assert_drop_gate(client, "external", expected=False, reason="fileurl+sidebarreorder must pass through")
assert_drop_gate(client, "local", expected=False, reason="fileurl+sidebarreorder must pass through")
print("PASS: overlay hit/drop gates preserve bonsplit drags and external file-drop behavior")
return 0
finally:
try: