Fix desync after Cmd-N: a late Chromium page gets the keys through the focus model; G1 counts cmux pages

Merged
LA
5d ago
+121-18

Summary

The action fuzzer (#29071) recorded three debug.desync findings after Cmd-N (STATUS 1002, 1022). This PR classifies each one and fixes it at its cause.

W1 and W4: a real desync. At the first Cmd-N of a launch, no New Tab spare exists and Chromium is not started, so the New Tab page (an agentPage) shows before its Chromium page exists. The focus applier makes the page's WebKit stand-in first responder. When the Chromium page attaches, PageWebView.attachChromium granted Chromium focus by itself, behind the focus model. The stand-in stayed first responder, and the applier never recorded the focused page. When the app is active, the page window's key change (childWindowDidBecomeKey) makes the applier apply the model again. When the app is not active (the fuzzer's capture slot, or a person who switched away before the page attached), nothing applies it again. The world check then keeps W1 appkit content:pane_* (PageWebView) != model agentPage and W4 chromium page nil != model tab_*. The window becoming key again heals it.

Fix: when a page's Chromium page attaches, the page asks its Chromium host (PageChromiumHosting.focusViewChanged, default: no focus model). The app's host finds the pane whose own Chromium page (TabContent.childWindowTab) this is and sends contentPresented. The applier then applies the model as it does for any newly presented content: no responder in the cmux window, the page recorded and granted, in one key change. Any other page (the docked Home's Channels page, a page that no pane shows, a parked spare) keeps its own grant, as on the base. A fall back to WebKit is unchanged.

G1: a false finding of the check. ChildPageGeometry.sample counted only browser tabs as hosts. The page window of every shown cmux page in Chromium (the New Tab page, an agent chat) was then reported as page window (618, 268, 892, 692) covers no pane. Hosts now come from TabContent.childWindowTab, the rule that the focus applier and the input observation already use. The same sample drives WindowOverlayLayer.pageWindowDidChangeFrame, which forced a page update on each frame change of such a page window. ChildPageGeometry.check had no caller and is removed.

Testing

Red first: PageWebViewFocusViewChangeTests (CmuxNextPagesTests), added at a346e23e41b.

Observed / Not run

Observed (journey, cmux19, exclusive capture lease, diagnostic mode):

  • v2.2.11-opt (78ca62c225bb0943594e3cd2), app inactive: RED, W1 + W4 + G1 13 s after Cmd-N; debug.focus: first responder PageWebView, model agentPage.
  • feat-tip-fixes-v2 (f2f3083bac80ab71101d03ac, feat acfd69382dc), app active: RED, G1 only (twice).
  • This PR at 976b215bed7 (build 568ad9fbd033e4ba0ac995fb), app active: PASS (no G1, W1 or W4).
  • Correction (coordinator, 11:4xZ): build 2db9e5949fd59e393aa1bab7 (5ceaf206d2b) did NOT run this journey. It ran a later dropped "-inactive" variant, which FAILED on its own app_active == false precondition. Do not count it.
  • The reviewed journey (#29274), whole host with the app active, through the mini scheduler (spec desync-g1w1w4-journeys), 12:11-12:19Z:
  • Home dock Channels, first open (the review's FIX case at 976b215), journey found-desync-channels-type: the SAME state on the feat base f2f3083b (cmux19), on 976b215 (build 568ad9fbd033e4ba0ac995fb, cmux19) and on this head (build ed24b7b1, cmux20): Channels in Chromium never gets the keyboard; the key window is ShellWindow and the first responder is the hidden PageWKWebView. This is a pre-existing bug on feat, not caused by this PR, and this PR does not fix it. The journey cannot tell 976b215 from the head; the identity match is shown by the code reading and the review only.

Not run, or read only:

  • W1/W4 on a feat build with the app inactive. In the capture slot, feat builds become active after Cmd-N, so the journey cannot make that state there. The inactive path on feat is shown by the Swift test and by the code reading above, not by a journey.
  • No app-level test of the pane lookup closure in AppDelegate (the arch ratchet refused the App lines for a testable extension). The review accepted this.

Changelog

none

Proof

No capture: there is no visible change. The fix changes which view holds the keyboard after a cold New Tab page attaches while cmux is in the background, and what a debug-only check reports.