From 71a206da137c01770f5862400f5c91a83bc1a238 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Fri, 30 Oct 2026 19:33:15 +0600 Subject: [PATCH] [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 --- .../cpm-extension-js-review-2026-10-30.md | 73 +++++++++++++++++++ ...-ios-idle-wake-investigation-2026-10-30.md | 2 + 2 files changed, 75 insertions(+) create mode 100644 work/projects/cpm-extension-js-review-2026-10-30.md diff --git a/work/projects/cpm-extension-js-review-2026-10-30.md b/work/projects/cpm-extension-js-review-2026-10-30.md new file mode 100644 index 00000000..5cbc2ae1 --- /dev/null +++ b/work/projects/cpm-extension-js-review-2026-10-30.md @@ -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 группы. diff --git a/work/projects/cpm-ios-idle-wake-investigation-2026-10-30.md b/work/projects/cpm-ios-idle-wake-investigation-2026-10-30.md index aa4e749a..b54a3f91 100644 --- a/work/projects/cpm-ios-idle-wake-investigation-2026-10-30.md +++ b/work/projects/cpm-ios-idle-wake-investigation-2026-10-30.md @@ -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 пока не найден.