[2026-10-30] eagle: work/projects/cpm-extension-js-review-2026-10-30.md work/projects/cpm-ios-idle-wake-investigation-2026-10-30.md

This commit is contained in:
Alexey Martemyanov committed 2026-10-30 19:33:15 +06:00
1 parent 9f3e3c5001
commit 71a206da13
2 files changed
+75

No files matched your search

@@ -0,0 +1,73 @@
---
tags:
- cpm
- ios
- investigation
updated: '2026-10-30'
status: verified-control-flow-findings
---
# CPM extension: проверка JavaScript — 30 октября 2026
Да: отдельно проверены исходники самого extension и CPM bundle, извлечённый из iOS 7.243.0. Дополнение к [[cpm-ios-idle-wake-investigation-2026-10-30]].
Проверенный source checkout: `b00cc227112d4548755af9d3c4d93029c6e07066`, 15 октября, с init progress instrumentation. Это локальный checkout, не утверждение об актуальном upstream HEAD. Shipped app tag: `7.243.0+ios`, embedded manifest `2026.10.14`. Код приложения и extension не менялся.
## 1. Запоздалый initResp не привязан к исходному документу
Shipped handler сохраняет tabId/frameId и URL, затем ждёт config/settings/site policy. Ответ отправляется через `tabs.sendMessage(tabId, ..., {frameId})`; идентификатора документа или init request в payload нет. Content-script callback принимает любой `initResp` и вызывает initialize без проверки, какому init он принадлежал.
Если пока handler A ждёт ответ на policy check, вкладка переходит на B, её main-frame по-прежнему адресуется как frameId=0. Старый handler не отменяется. WebKit tabsSendMessage выбирает текущее tab webView и процессы слушателей; исходный документ A не является целью вызова без document targeting.
Контролируемая проверка:
- A отправляет init; её site check намеренно задержан.
- В той же вкладке создан B; для B site check возвращает false, dashboard получает site_disabled.
- После отпускания A старый initResp попадает в modeled current frame B.
- Реальный shipped receiveMessageCallback вызывает initialize(enabled=true) у B.
- При контрольном порядке A response → navigation B initialize происходит только у A.
Это дефект корреляции протокола. В проверке используется модель navigation/frame routing и spy вместо DOM initialize; физический переход на iPhone в этом проходе не запускался. Из кода подтверждено отсутствие document guard. Доля production эпизодов и фактические действия CPM на странице B не измерены.
Исправление: локальный per-document/per-init nonce, echo в ответе и проверка на стороне content script; отмена/игнорирование устаревших background задач. Сравнения только URL недостаточно при reload того же адреса. Поддерживаемый documentId может быть дополнительной защитой, но его совместимость следует проверить отдельно.
Sources:
- [Shipped sendMessage](/Users/admin/DuckDuckGo/apple-browsers.git/cpm-ios-idle-wake-investigation-2026-10-30/shipped-extension/public/js/background-embedded.js:2715).
- [Shipped receiveMessageCallback](/Users/admin/DuckDuckGo/apple-browsers.git/cpm-ios-idle-wake-investigation-2026-10-30/shipped-extension/public/js/content-scripts/cpm.js:3333).
- [Локальный extension source](/Users/admin/DuckDuckGo/duckduckgo-privacy-extension/shared/js/background/components/cookie-prompt-management.js:640).
## 2. Восстановление config не восстанавливает init текущего документа
При remoteConfigJson=null handler вызывает scheduleConfigRefresh, отправляет dashboard config_unavailable и возвращается без initResp. Новый refresh не сохраняет/не повторяет исходный init. Shipped content script отправляет init в конструкторе один раз; recovery-retry этой инициализации не найден.
Контролируемая проверка:
- config для первого init отсутствует;
- новый refresh сразу успешно восстанавливает config;
- после его завершения initResp по-прежнему не отправлен;
- явно повторённый init получает initResp.
Это конкретный пробел восстановления после временной ошибки. Dashboard при этом может сообщить config_unavailable и закрыть health episode, хотя исходный документ остаётся без инициализации. Сам по себе этот сценарий не объясняет browser-wide пяти минутный no-response: новые navigation после восстановления config способны работать.
В существующем unit-test проверен dashboard config_unavailable; восстановление исходного init этот тест не утверждает.
Исправление: ограниченный retry pending init для ещё живого того же документа; обязательны document guard, отмена на navigation и idempotency, чтобы не повторять действия CPM. Нельзя сохранять pending init только по tabId/frameId.
Source: [Shipped config-unavailable branch](/Users/admin/DuckDuckGo/apple-browsers.git/cpm-ios-idle-wake-investigation-2026-10-30/shipped-extension/public/js/background-embedded.js:2588).
## 3. Storage ожидания могут глобально удерживать dashboard
Подтверждённое ранее свойство: config get/set без собственного deadline и общая _stateQueue, которая ждёт session storage. Одна never-settled запись cpmState удерживает dashboard разных вкладок. Shipped probes дали две попытки отправить initResp и ноль dashboard-вызовов, пока запись не отпущена.
Это не доказательство естественного зависания WebKit storage. Но это конкретная зависимость критичного наблюдения от persistence. Состояние JS event loop/runtime может оставаться работоспособным. Отдельный app↔page ACK нужен, чтобы отличить реальную потерю CPM от зависания dashboard pipeline.
Исправление требует отделить оперативное in-memory состояние и delivery evidence от persistence, сохранив порядок/revision записей. Простой timeout не отменяет старую запись и может создать stale-write race.
## Проверки и границы
`extension-flow-probes.cjs` выполняет три сценария: config recovery без replay, ordered-navigation control, overlapping navigation. Они повторены на:
- shipped CookiePromptManagement + shipped receiveMessageCallback;
- классе CookiePromptManagement из локального extension source + том же shipped receiveMessageCallback.
Оба набора дали одинаковые результаты. Source I/O замокан; реальные методы обработки сообщений и приёма initResp используются без изменения. Constructor/alarms/pixel emission не являются предметом этих проверок. Config/state probes предыдущего прохода сохранены отдельно.
Результаты: `extension-flow-probe-results.json`, `extension-flow-checkout-results.json` в `/Users/admin/DuckDuckGo/apple-browsers.git/cpm-ios-idle-wake-investigation-2026-10-30/`.
Приоритет для узкого extension fix: document-bound init/response и cancellation; затем bounded recovery исходного init после временной config ошибки. Параллельно измерить state queue, чтобы проверить её вклад в wake dashboard failures. Ни один из механизмов пока нельзя объявить единственной причиной всей production idle-wake группы.
@@ -8,6 +8,8 @@ updated: '2026-10-30'
status: investigation-and-proposal
---
# CPM iOS idle wake: действия, возможные механизмы и диагностический план
Продолжение с проверенными JavaScript findings: [[cpm-extension-js-review-2026-10-30]] — stale initResp пересекает navigation; config refresh не повторяет исходный init. Есть контролируемые проверки shipped bundle и локального extension source, с указанными границами.
Дата: 30 октября 2026. Продолжение [[cpm-ios-uncovered-groups-2026-10-30]].
**Основной вывод:** idle wake остаётся приоритетным сценарием, но текущий пиксель объединяет разные механизмы. Нужно разделить выполнение content script, доставку init в worker, ожидания внутри worker, dashboard → native → monitor и реальное получение initResp страницей. Проверка shipped bundle выявила две неограниченные storage-зависимости; контролируемые проверки показали, что общая очередь cpmState может остановить dashboard-ответы нескольких вкладок при продолжении отправок initResp. Естественный production-trigger такого storage stall пока не найден.