[2026-09-10] eagle: work/projects/cpm-web-extension-breakage-findings.md
This commit is contained in:
@@ -1162,7 +1162,27 @@ This **contradicts** the earlier claim that a dead P1 is cleaned up before any w
|
||||
- **A. Stale SW server state at re-registration.** UI process: `didClose` → `processDidTerminateOrFailedToLaunch` → `webViewWebContentProcessDidTerminate` → `unloadBackgroundWebView` (`WebProcessProxy.cpp:1565-1617`, `WebExtensionContextCocoa.mm:2950-2957`); the next queued content-script message immediately creates the new page in a prewarmed process and registers. Network process: cleanup runs from `NetworkConnectionToWebProcess::didClose` → `unregisterSWConnection` → `SWServer::removeConnection` → `~WebSWServerConnection` → unregister clients → `registration->clear()` (`NetworkConnectionToWebProcess.cpp:470-518`, `SWServer.cpp:1368-1378`, `WebSWServerConnection.cpp:96-105`). If P2's `Register` lands first → "directly reusable registration" against the dead worker (same outcome as the idle-unload race above), or "needs updating" against a registration whose page id is the dead page.
|
||||
- **B. New worker cannot start.** Old registration is gone, but the SW context connection for `webkit-extension://<uuid>` is still being torn down (`m_swContextConnection->stop()`), so the new worker's `createContextConnection` is deferred and the register job does not settle → `_loadServiceWorker` completion never fires → `m_backgroundContentIsLoaded=false`, actions queue, view retained until the 30 s timer → unload → next wake works.
|
||||
|
||||
**Log from the second run (2026-09-10 10:50, PID 54017) narrows it to a fast rejection, not a hang:**
|
||||
|
||||
```
|
||||
10:50:49.435 Loading background content
|
||||
10:50:49.436 [CPM Diagnostics] Background web view created #10 previousViewAlive=false previousWebProcessPID=8105 …
|
||||
10:50:49.446 Scheduled task for after background content loads (×2) ← content-script messages already queued
|
||||
10:50:49.446 Registered event listener for type 28 in content script world ← cpm.js onMessage, tab side
|
||||
10:50:49.490 Tab for page 2118 was not found (×3)
|
||||
10:50:49.498 Error recorded: WKWebExtensionContextErrorDomain Code=6 ← BackgroundContentFailedToLoad, 63 ms after load start
|
||||
10:50:49.540 [CPM Diagnostics] Background web view #10 webProcessPID=8415
|
||||
10:51:19.447 Unloading non-persistent background content ← 30 s timer; view deallocated; next wake works
|
||||
```
|
||||
|
||||
Not a single `WebContent[…] Called getter browser.*` line for the new worker. In a healthy load (other instance, PID 11621, 10:51:29) the first thing the worker does is `browser.runtime` / `runtime.id` (the polyfill's "am I in an extension" check), then registers `runtime.onConnect`, `runtime.onMessage`, `alarms.onAlarm`. So `_loadServiceWorker` failed **before the worker script touched any extension API** — `register()` was rejected (not B), and it was not the "reusable registration" resolve either (that returns success, not error). Neither A nor B as written; the hang variant B is ruled out.
|
||||
|
||||
Most consistent mechanism: **the worker was started in a process that does not host the new SW page's document**. Bindings are installed only from `ServiceWorkerGlobalScope::notifyServiceWorkerPageOfCreationIfNecessary` → `Page::serviceWorkerPage(m_contextData.serviceWorkerPageIdentifier)` → `Document::allDocumentsMap()` (**process-local**, `ServiceWorkerGlobalScope.cpp:118-140`, `Page.cpp:5208-5212`). No page in that process → no `chrome`/`browser` → polyfill throws at top level → `SWServer::scriptContextFailedToStart` → job rejected → `register()` rejects → `didFinishServiceWorkerPageRegistration(false)` → `BackgroundContentFailedToLoad`, view retained until the 30 s timer. Which process the worker lands in is decided by `SWServer::tryInstallContextData` (`SWServer.cpp:1068-1084`): an **existing** context connection for `webkit-extension://<uuid>` wins over the page's process; otherwise `createContextConnection` → UI `establishRemoteWorkerContextConnectionToNetworkProcess` picks the page's process by `serviceWorkerPageIdentifier` (`WebProcessPool.cpp:709-714`), falling back to the requesting process or any process with the same site (716-742), else a new one. Right after `kill -9`, the Network process may still hold P1's context connection (or a pending connection creation keyed by domain, `m_pendingConnectionDomains`) when P2's job arrives — the worker is then installed away from P2's page. Still a hypothesis: the ServiceWorker-category lines were not in the captured log (the predicate only matched `Extensions`).
|
||||
|
||||
Discriminating evidence to collect on the next run (kill → immediately reload a tab → within 10 s):
|
||||
0. Run the log stream with the **Networking process included**:
|
||||
`log stream --level debug --predicate '(subsystem == "com.apple.WebKit" AND category == "ServiceWorker") OR category == "Extensions" OR composedMessage CONTAINS "[CPM"'`
|
||||
Look for, in order: `runRegisterJob: …` (reusable / needs updating / constructing a new one), `establishRemoteWorkerContextConnectionToNetworkProcess reusing an existing web process (PID=…)` vs the view's `webProcessPID=`, and `SWServer::scriptContextFailedToStart: Failed to start SW … error: <JS error text>` or `fetch resulted in error`.
|
||||
1. "Print CPM Diagnostics Snapshot": `background_view_alive`, `background_view_create_count` (2 = new view was created → A or B; 1 = delegate never fired), `background_web_process_pid` (new PID vs 0).
|
||||
2. Our log: `Background web view created #2 … previousWebProcessAlive=false`, then `webProcessPID=<new>`; any `extension_context_errors` (`background_content_failed_to_load` → load actually failed).
|
||||
3. `log stream --predicate 'category == "ServiceWorker" || category == "Extensions"'` during the dead minute: "Found directly reusable registration" / "needs updating" / "No existing registration … constructing a new one", and whether the register job ever resolves.
|
||||
|
||||
Reference in New Issue
Block a user