[2026-09-10] eagle: work/projects/cpm-web-extension-breakage-findings.md
This commit is contained in:
@@ -1214,6 +1214,14 @@ Symptom match: all tabs, new tabs too, survives reload, banners not handled, das
|
||||
|
||||
**Diagnostics added for this class (2026-09-10):** `CPMBackgroundWebViewDelegateProxy` — a forwarding `WKNavigationDelegate` installed in `didCreateBackgroundWebView` in front of WebKit's own `_WKWebExtensionContextDelegate` (which WebKit assigns before the callback and needs for its own unload). Forwards every selector the original answers (WebKit snapshots `respondsToSelector:` at assignment), retains the original for the view's life, and intercepts: `webViewWebContentProcessDidTerminate:` + private `_webView:webContentProcessDidTerminateWithReason:` (`_WKProcessTerminationReason`: memory_limit / cpu_limit / requested_by_client / crash / shared_crash_limit; WebKit calls only the private one when both exist, so the proxy re-dispatches to whichever the original implements), and WebKit's hang detection `_webViewWebProcessDidBecomeUnresponsive:` / `…Responsive:` (UI-side `ResponsivenessTimer`, 3 s per replied message; `BackgroundProcessResponsivenessTimer` for page-less processes: ping every 20 s doubling to 8 h, 90 s to answer — the same signals Safari's "not responding" uses). Recorder → pixel: `background_process_terminations`, `background_process_terminated` (age bucket), `background_process_termination_reason`, `background_process_unresponsive`, `background_process_unresponsive_count`, plus `background_web_process_responsive` from `WKWebView._webProcessIsResponsive` at snapshot time. Health-monitor state machine untouched; it receives these through the diagnostics provider.
|
||||
|
||||
**Observation (Alex, 2026-09-10):** with the delegate proxy installed (its lifetime tied to the background `WKWebView`), `kill -9` no longer breaks CPM; without the proxy, or with the proxy tied to the original delegate, it does. Reading: the outcome of step 4 depends on whether the dead P1 still exists as a `WebProcessProxy` when the Network process asks for the context connection — `processForIdentifier` is cleared only in `~WebProcessProxy` (`WebProcessProxy.cpp:488`), which is kept alive by the closed `WebPageProxy`, i.e. by the `WKWebView`. If P1 still resolves: `useProcessForRemoteWorkers(P1)` → send to a dead process → cancelled → Network's retry finds no clients → clean. If P1 is gone: fallback → stray process S → bug. The proxy shifts the view's dealloc by enough to win that race — accidental timing, not a fix.
|
||||
|
||||
**Graveyard experiment (implemented 2026-09-10):** `FeatureFlag.cpmBackgroundGraveyardExperiment` (subfeature `webExtensions.cpmBackgroundGraveyard`, cohorts `control`/`treatment`). Treatment: on the termination callback the recorder holds the dead background `WKWebView` strongly for `CPMMessagingDiagnosticsRecorder.graveyardHoldDuration` (3 s), so `processForIdentifier(P1)` keeps resolving while the Network process re-requests; control releases immediately. Pixel: `background_graveyard=control|treatment`. Prediction: treatment never shows `error_background_failed_to_load` after `died_*`; control does. Deterministic A/B: local-override the cohort, `kill -9`, watch `background_events`.
|
||||
|
||||
**Delegate proxy flag:** `FeatureFlag.cpmBackgroundDelegateProxy` (subfeature `webExtensions.cpmBackgroundDelegateProxy`, remote-releasable, on by default). The recorder observes `FeatureFlagger.updatesPublisher`; turning it off hands WebKit's original delegate back on the live view (`CPMBackgroundWebViewDelegateProxy.uninstall`), turning it on re-installs.
|
||||
|
||||
**Pixel timeline instead of counters:** `background_events` — ordered `token@-<seconds before pixel>` list (last 12, ≤255 chars): `load`, `view`, `dealloc`, `died_<reason>`, `unresponsive`, `responsive`, `error_<descriptor>`, `hold`/`release` (graveyard), `proxy_on`/`proxy_off`. Replaces the separate termination/unresponsive counters.
|
||||
|
||||
**Reproduction (DEBUG, 100 %):** "Print CPM Diagnostics Snapshot" → `kill -9 <background_web_process_pid>` → reload any tab with a cookie banner. Log with
|
||||
`log stream --level debug --predicate '(subsystem == "com.apple.WebKit" AND category == "ServiceWorker") OR category == "Extensions" OR composedMessage CONTAINS "[CPM"'`
|
||||
and look for "creating a new service worker process" followed by "Created service worker N in process PID <≠ webProcessPID of the view>" and "Job N failed with error Script error."
|
||||
|
||||
Reference in New Issue
Block a user