Base red: the app no longer crashes at launch on macOS 14 (WebKit navigation with no source frame)

Merged
LA
1d ago
+150-5

Summary

Base red: the cmux-next app ends at launch on macOS 14.8.7, its oldest supported OS. 15.7.7 and 26.6.2 pass the same smoke.

  • feat tip 66339d310c0, fleet build 83e04e0d8b97c62c796fc5aa, Tart smoke step 48cb9eceb51bb6143325b239: SMOKE FAIL os=14.8.7 ... crashes=1. The app died after the chromium step; every later step found no socket.
  • Crash report, main thread, EXC_BREAKPOINT (SIGTRAP):
    Foundation  static URLRequest._unconditionallyBridgeFromObjectiveC(_:)
    cmux DEV    @objc closure #1 in PageWebView.webView(_:decidePolicyFor:)
    ...         libswift_Concurrency completeTaskWithClosure
    

Cause

PageWebView.webView(_:decidePolicyFor:) reads action.sourceFrame.request.url. #26286 (1e2705700cf5) added this line. WebKit declares sourceFrame nonnull, but macOS 14's WebKit gives nil for some navigations. In Swift, the nil frame then gives a nil NSURLRequest, and the bridge to URLRequest traps. The trapping frame is exactly that bridge, called directly from the delegate closure. The iOS app already hit the same WebKit behavior (BrowserTab.swift: "sourceFrame is nil for some navigations despite its Swift type").

AgentPaneNavigation reads the same property the same way, so it has the same hazard. The browser's WKNavigationAction.sourcePage used sourceFrame as WKFrameInfo?. That cast does not prove safety, because the optimizer may fold the nonnull value's .some.

Fix

The new CmuxNextDesign/WKNavigationAction+Frames.swift adds sourceFrameIfPresent, sourceFrameURL, targetFrameURL and WKFrameInfo.requestURLIfPresent. They call the Objective-C getters through perform(_:), which returns an optional. Thus no code assumes that the frame or its request is nonnull. All three call sites now use them: PageWebView, AgentPaneNavigation and sourcePage. When the frame exists, the URL is the same as before. When there is no frame, the policy treats the source as unknown (nil), which is a path it already handles. A nil source never grants the artifact or srcdoc allowances, so it fails closed.

Other WebKit delegate reads in the app are safe: targetFrame is declared optional, WKScriptMessage.frameInfo and the initiatedByFrame: parameters are not navigation actions, and no other code reads sourceFrame.

Regression test

FakeNavigationAction (CmuxNextPagesTestSupport) is a WKNavigationAction subclass. Its sourceFrame getter returns nil through the Objective-C runtime, as on macOS 14. The tests call the real delegate methods with it:

  • PageNavigationNoSourceFrameTests: PageWebView allows its own document, opens a clicked outside link externally, and cancels an unclicked one.
  • AgentPaneNavigationNoSourceFrameTests: AgentPaneNavigation allows the pane's document and cancels another main-frame document.

Command: cmux-ci run --class light --script scripts/ci/package-test-lane.sh --ref <sha> --arg=suite --arg=Packages/macOS/CmuxNext --arg=PageNavigationNoSourceFrameTests,AgentPaneNavigationNoSourceFrameTests

  • Red, at 1b35c936338 (tests only): step 065f1feb855193591217c6b6: both tests start, then the test process ends with unexpected signal code 5 (SIGTRAP, the trap in the crash report).
  • Green, at b53b9f860b7 (fix): step c65b70d79776acd6f5b697ee (Blacksmith overflow, run manaflow-ai/cmuxterm-hq actions 41987489687): ✔ aNavigationWithNoSourceFrameGetsThePolicyNotATrap() and ✔ aNavigationWithNoSourceFrameGetsTheDecisionNotATrap() pass.

macOS matrix (Tart, scripts/cmux-next/os-matrix.sh --suite smoke)

Build of b53b9f860b7: fleet job 7f4ba96af2200a8fcfb1b297 (tag base-red-mac14-b53b9f860-v1). Smoke step 57a668a56b1a7651b1834b67:

macOSbase 66339d310c0 (step 48cb9eceb51bb6143325b239)this PR
14.8.7FAIL, crashes=1 (this trap)PASS, crashes=0, hangs=0, faults=0
15.7.7PASSPASS, crashes=0, hangs=0, faults=0

Observed / Not run

Observed:

  • The base crash report (main thread, the stack above, EXC_BREAKPOINT) and its smoke log: the app died after the chromium step.
  • The red and green fleet test steps above, at 1b35c936338 and b53b9f860b7.
  • The macOS 14 and 15 Tart smoke at b53b9f860b7: both PASS with no crash.
  • python3 scripts/cmux-next/prepush-gate.py: no new violations.

Not run:

  • macOS 26 smoke on this head. The base already passed on 26.6.2, and the fix does not change behavior when a frame is present.
  • The browser sourcePage change has no test of its own. It reads through the same helper that the two tests cover.
  • I did not find the exact WebKit navigation that has no source frame on macOS 14. The test uses an Objective-C double that returns nil, as the iOS code already notes for the same WebKit behavior.

Changelog

  • Fixed: cmux NEXT no longer crashes at launch on macOS 14.

No capture: there is no UI change. This fixes a crash in a WebKit navigation delegate.


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes a launch crash on macOS 14 where a WebKit navigation with no source frame trapped the app.

  • macOS 14's WebKit returns nil for WKNavigationAction.sourceFrame on some navigations even though it is declared nonnull; reading sourceFrame.request.url bridged a nil NSURLRequest to URLRequest and trapped, killing the app at launch on the oldest supported OS (14.8.7). macOS 15 and 26 passed the same smoke.
  • New CmuxNextDesign helpers (sourceFrameIfPresent, sourceFrameURL, targetFrameURL, requestURLIfPresent) read the frames through the Objective-C runtime so a missing frame becomes nil instead of a trap. PageWebView, AgentPaneNavigation, and the browser's sourcePage now use them; with a frame present the URL is unchanged, and with none the policy treats the source as unknown, which the existing paths already handle and which fails closed on artifact and srcdoc allowances.
  • Regression tests use a new FakeNavigationAction double that returns nil for sourceFrame through the runtime; PageNavigationNoSourceFrameTests and AgentPaneNavigationNoSourceFrameTests trap at the base commit and pass with this fix.

Tart smoke on macOS 14.8.7 goes from FAIL (crashes=1) to PASS (crashes=0, hangs=0, faults=0); 15.7.7 stays PASS.