11 KiB
Executable File
title, date, status, tags, updated
| title | date | status | tags | updated | |||
|---|---|---|---|---|---|---|---|
| CPM Web Extension failures — reproduction and fixes | 2026-10-08 | historical-summary |
|
2026-10-20 |
CPM Web Extension failures: reproduction and fixes
Current context — checked 20 October 2026. This is the historical October 8 reproduction summary. The “all code local / none in review” and original phase-1 sections below are superseded; historical results are retained.
Current implementations: #7379 P1/P2/P4, #7459 Network recovery with an applied daily+count pixel, #7493 restricted stable-origin P3 policy, and #7473 shared-pinned window close (merged 20 October into
release/macos/1.213.0, commit9bcce0264e; rollout unverified). Exact heads, bases, failure annotations and gates: cpm-status-2026-10-15#2. PR и quality gates; sources: cpm-web-extension-code-guide-2026-10-09#0. Текущая карта worktree и точек входа.#7376 now contains one terminal episode report with four-value
stuckand two-phasebg_events, daily+count; it is closed without merge. #7378/#7384 are also closed without merge, while #7385 and extension #3727 remain open. Full current telemetry contract: cpm-pr0-episode-telemetry-summary-2026-10-16. No page ACK or full explanation of production Others is provided.
Historical context — recorded 15 October 2026
Current status — 2026-10-15: cpm-status-2026-10-15; current code/worktree map: cpm-web-extension-code-guide-2026-10-09 (§0). The October 8 summary below is retained as history. Its “all code local / none in review” statement is no longer current: harness #7378, A #7379, B #7381 and telemetry #7376/#7384/#7385 plus JS #3727 are open PRs. B's local fix is still absent from its red remote head.
New evidence: closing a window that still reports a shared pinned tab makes system WebKit forget the surviving tab and reproduces exact P5/tab=false. Cycling the main flag through the production handler reproduces P6 on a view retaining the previous controller; occurrence of that flag transition in production remains unproven. Browser callbacks/delegates are modeled from source in the harness; this is not a full browser-app E2E run.
All 14 current P5/P6 methods pass across separate runs; ordinary package 478 XCTest / 28 skipped / 0 failures. No natural trigger is proven for empty listener metadata, P5/tab=true, or iOS lifecycle. Long documents and host stalls did not produce P5. Page initResp receipt is asserted in healthy controls; dashboard receipt alone still does not prove it in production. Temporary test copies may include instrumentation/fault preludes; the production bundled zip is unchanged. Full results, original red runs and commands are preserved in the main status.
Context
Cookie Pop-up Management (CPM) runs in the iOS and macOS browsers as a built-in web extension: a content script in every page plus a shared background service worker that WebKit starts on demand and unloads after about 30 seconds of inactivity.
The app's health monitor reports a failure when a page gets no CPM answer within 4 seconds. A second failure marks the episode as "stuck", and the app then reloads the extension to recover. The companion summary of the production data explains which failure patterns we see and how often.
Status: all code described here is still local. None of it is in review or merged yet.
How we reproduced the failures
- We built an automated test suite that runs the real WebKit framework on macOS 26.7.1 and the production CPM extension. It drives the app's real extension manager, startup logic, health monitor and recovery.
- Failures in the tests therefore produce the same diagnostic pixels as in production, which lets us compare them with real reports.
- The extension itself is unchanged. Problems are injected from outside:
- crashing or freezing WebKit's processes;
- calling private WebKit testing API, for example to stop service workers;
- adding a startup delay to the extension, once or for a single wake-up;
- filling the browser cache;
- removing extension files.
- The suite is opt-in, so it does not slow down regular CI.
- The iOS Simulator turned out to be unsuitable: test processes there do not get the system's background-execution grants. Device checks were done on an iPhone 12 mini with iOS 26.7.1.
What we reproduced
- The first message is lost when a new extension version loads (first install or update). WebKit starts the background immediately, and a message sent before the background code has run is dropped: 14 of 20 attempts on install, 18 of 20 on update. The app already waits for the extension at launch, but not when the extension is installed or updated mid-session.
- A crash of WebKit's networking process breaks CPM in every tab. The background looks alive, but its worker is dead. All tabs lose CPM, including pages that were already open. The current recovery, an extension reload, fixes new pages only; pages opened earlier stay broken. Reproduced 20 out of 20 times.
- The worker can die while the networking process survives, for example when service worker data is cleared. The symptoms are the same, and the app cannot detect this state directly.
- WebKit error 6, background failed to load. If waking the background takes longer than 30 seconds, WebKit's idle timer closes it mid-load and reports error 6. The test matches the production signature. It did not reproduce on the iPhone in four scenarios.
- A slow worker start on wake-up reproduces the most common production pattern.
- Up to 2.5 seconds: messages wait and are delivered, nothing is lost.
- At 5 seconds: the app reports a failure with a "healthy" snapshot exactly like production, then the late answer arrives and the app reports a recovery.
- At 12 seconds with a second page load: the episode becomes "stuck" and the app reloads the extension. That reload kills a worker that was slow but working, and the pending messages of already-open pages are lost.
- WebKit's website-data cleanup on every extension load delays CPM. With a large browser cache (about 20,000 entries), content scripts started 0.75–1.35 seconds late, against 23 milliseconds with an empty cache. On a fast Mac this stays under the 4-second limit; slower phones still need to be measured.
What recovers what:
- Restarting only the extension's background process makes WebKit start a fresh worker. That fixes every tab, including already-open pages.
- Reloading the extension, the current recovery, fixes new pages only.
Detecting when the worker has actually started:
- The background's own page finishes loading after about 0.1 seconds, long before the worker runs, so it is no use as a signal.
- WebKit's "background content loaded" callback and the worker's first message to the app both arrive right after the worker has started. Either is a reliable signal.
Proposed fixes (app side, not merged)
- Wait for the extension after a mid-session install or update, as the app already does at launch. Page loads wait at most 5 seconds.
- Watch the networking process. When it is replaced, restart only the extension's background process. This repairs every tab, including open pages.
- Restart the background after the first failure, but only if its process still responds. Restarting a frozen process left the new worker unable to receive messages.
Results in tests, without fix → with fix:
- Mid-session install: 3 of 3 messages lost → 5 of 5 delivered.
- Mid-session update: 5 of 5 lost → 5 of 5 delivered.
- Networking process crash with several tabs and open pages: everything broken → everything works, without an extension reload.
- Worker dies while the networking process survives: two failures, an extension reload, and old pages stay broken → one failed page, then everything works without a reload.
Known risk: fix 3 can make a slow wake-up worse. If the slow worker is not busy on the CPU (for example, it is waiting on disk or network), its process still responds, gets restarted, and a late answer becomes a lost one. Restarts should wait until the background has finished starting; see "Detector changes" below.
Telemetry (phase 1, ready, not merged)
- Adds diagnostic fields only and does not change behaviour. It should ship before the fixes, or together with them in an A/B experiment, so the fixes can be measured.
- Failure pixels gain:
- whether the background had finished starting and how long that took;
- the age of the background;
- whether the app was in the foreground;
- the number of open and loading tabs;
- for error 6, the app state and the time to the error.
- Recovery pixels gain:
- whether the failed page answered late or a later page answered;
- how long recovery took.
- How readiness is detected: the app asks WebKit to report when the background it has just created finishes loading. This wakes nothing, and it can be switched off remotely with the existing diagnostics flag.
- Verification:
- In the slow wake-up test, the failure pixel reported "background not ready, created 4–8 seconds ago", and the recovery pixel reported "the failed page answered late, under 4 seconds after the failure".
- All 677 automated tests of the extension package pass. Pixel definitions validate on iOS and macOS, and both apps build.
Next steps to decide
-
Detector changes (phase 2):
- start the 4-second timer only once the background is ready;
- report "background did not start within 30 seconds" as a separate pixel;
- recover (restart or reload) only when the background was ready but did not answer.
This removes false alarms from slow starts and avoids killing workers that are slow but working.
-
Wake the background early, when a page load starts or the app returns to the foreground, without blocking the page. Startup then overlaps with page loading.
-
Give the extension a stable internal base URL. This removes WebKit's data cleanup on every launch and reload.
- Privacy check: our embedded extensions expose no files to web pages, so pages cannot observe the URL.
- Still to test: extension reload and crash recovery with a stable URL, and the update path.
-
Error 6: if the data confirms the background-app pattern, restart the background when the app returns to the foreground after an error 6.
-
Extension changes:
- retry the first message when no answer comes, which covers both lost and late messages;
- add a heartbeat between the extension and the app, so a dead worker can be detected without waiting for a failed page.
-
Rollout: put the fixes behind remote feature flags and run them as an A/B experiment that is visible in CPM pixels. Comparing app versions has not given conclusive results.
-
Report to WebKit:
- after a networking process crash, WebKit does not notice that the background worker is dead;
- the idle timer starts before the background finishes loading, which causes error 6.