75 lines
3.6 KiB
Markdown
75 lines
3.6 KiB
Markdown
---
|
|
tags:
|
|
- work
|
|
- cpm
|
|
- tech-design
|
|
- pixel
|
|
- web-extension
|
|
updated: '2026-11-11'
|
|
status: draft
|
|
---
|
|
# TD: Pixel Definition: CPM Wake Init Detection (macOS/iOS + embedded web extension)
|
|
|
|
**Author:** Alex M
|
|
**Reviewer:** TBD (previous CPM pixel designs: Michal)
|
|
**Stakeholders:** Michal, Konrad, Russell (from the previous Tech Designs; confirm). Extension change: the privacy-extension owners.
|
|
**Project:** [CPM: root-cause the stuck embedded extension messaging (macOS/iOS)](https://app.asana.com/1/137249556945/task/1216761517055116)
|
|
|
|
## Background & Requirements
|
|
|
|
Failures after a background wake are the largest group with no fix: on macOS 1.214, 39% of all failures (0.68% of DAU); on iOS 7.244, 22% (0.06% of DAU).
|
|
|
|
For one page, startup is: page script sends `init` → background receives it → reads configuration → reads the user setting → checks the site → answers the page and refreshes the app's dashboard. The app sees only the last step, so every failure looks the same: no answer.
|
|
|
|
Requirements: find where the chain stopped, without changing what the extension does for the page or collecting URLs or persistent identifiers.
|
|
|
|
## Problem Statement
|
|
|
|
For every tab the permanent breakage metric reports as silent, record the furthest init step the extension reported.
|
|
|
|
## Recommended Approach
|
|
|
|
1. The extension sends the app a fire-and-forget notification at each init step ([PR #3727](https://github.com/duckduckgo/duckduckgo-privacy-extension/pull/3727)). It never waits, throws or changes what the page receives.
|
|
2. The app keeps the furthest step per tab, for its current page only. A notification does not count as the extension answering: the tab is still reported silent unless the dashboard refresh arrives.
|
|
3. `web_extension_cpm_breakage` gets one parameter, `last_stage` [1]:
|
|
|
|
| `last_stage` | The chain stopped |
|
|
|---|---|
|
|
| `none` | nothing reached the app [3] |
|
|
| `page_init_sent` | the page script sent `init`; the background never processed it |
|
|
| `received` | `init` arrived; configuration or storage read did not finish |
|
|
| `config_read` | waiting for the user setting from the app |
|
|
| `settings_checked` | waiting for the site check |
|
|
| `site_checked` | all checks passed; no answer arrived |
|
|
| `response_sent` | the answer was sent to the page, but no dashboard refresh followed [2] |
|
|
| `response_failed` | sending the answer to the page failed |
|
|
|
|
4. The extension ships inside the app, so the app side can ship first. The notifications and `last_stage` are removed when the wake group has a fix or is closed.
|
|
|
|
## Notes
|
|
|
|
[1] Each stage follows a separate wait in the extension's `init` handling: the cached configuration, the user setting and the site check (the last two are native round trips). A stall at each has a different cause and fix.
|
|
|
|
[2] WebKit can report a send as done when nobody was listening, so `response_sent` does not prove the page got the answer.
|
|
|
|
[3] Notifications pass through the background we study, so the earliest failures end as `none`.
|
|
|
|
## Testing
|
|
|
|
Unit tests in the app and in the extension. Not done: a real-WebKit run showing the right `last_stage` for a stall at each step.
|
|
|
|
## Additional Considerations
|
|
|
|
1. Privacy
|
|
1. One new enumerated parameter. Tab number and URL stay inside the app. Privacy Triage not filed yet.
|
|
2. Security
|
|
1. No new private WebKit selectors; the new message is read-only.
|
|
3. Site Breakage
|
|
1. No intended change.
|
|
4. Experimentation
|
|
1. None.
|
|
5. Operational
|
|
1. First data comes about two release cycles after the work, because the extension ships with the app.
|
|
6. Localization / Internationalization
|
|
1. Not affected.
|