From 24ef334500c4ccc31bcbcaab7feff6468d5d4db9 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Mon, 5 Oct 2026 04:04:04 +0800 Subject: [PATCH] fix(renderer): replace the Service Worker client on initial Document commit --- .../auxiliary_page_retirement.rs | 27 ++++++++- .../src/native_bridge/context_host/core.rs | 28 +++------ .../context_host/service_workers.rs | 59 +++++++++++++++++++ .../src/script_vm/initial_window_document.rs | 4 ++ 4 files changed, 96 insertions(+), 22 deletions(-) diff --git a/moli-protocol-server/src/protocol_server/tests/cdp_dynamic_page/auxiliary_page_retirement.rs b/moli-protocol-server/src/protocol_server/tests/cdp_dynamic_page/auxiliary_page_retirement.rs index fcad3aac60..9826ee7715 100644 --- a/moli-protocol-server/src/protocol_server/tests/cdp_dynamic_page/auxiliary_page_retirement.rs +++ b/moli-protocol-server/src/protocol_server/tests/cdp_dynamic_page/auxiliary_page_retirement.rs @@ -170,7 +170,17 @@ async fn initial_popup_commit_keeps_window_timers_and_service_worker_completion( let response = Arc::clone(&response); async move { if uri.path() == "/worker.js" { - return ([("content-type", "text/javascript")], "self.addEventListener('install',e=>e.waitUntil(self.skipWaiting()));self.addEventListener('activate',e=>e.waitUntil(self.clients.claim()));"); + return ( + [("content-type", "text/javascript")], + r#" + self.addEventListener('install', e => e.waitUntil(self.skipWaiting())); + self.addEventListener('activate', e => e.waitUntil(self.clients.claim())); + self.addEventListener('message', e => e.waitUntil( + self.clients.matchAll({includeUncontrolled:true, type:'window'}) + .then(clients => e.source.postMessage(clients.map(c => c.url))) + )); + "#, + ); } if uri.path() == "/child" { response.notified().await; @@ -218,8 +228,21 @@ async fn initial_popup_commit_keeps_window_timers_and_service_worker_completion( json!({"autoAttach":false,"waitForDebuggerOnStart":false,"flatten":true}), ) .await; - evaluate_window_name_probe(&mut child, 4, "window.registered='pending';navigator.serviceWorker.register('/worker.js').then(()=>registered='success',e=>registered=e.name);true").await; + evaluate_window_name_probe(&mut child, 4, "window.registered='pending';window.controllerChanges=0;navigator.serviceWorker.oncontrollerchange=()=>controllerChanges++;navigator.serviceWorker.register('/worker.js').then(()=>registered='success',e=>registered=e.name);true").await; wait_for_value(&mut child, "registered", json!("success")).await; + wait_for_value( + &mut child, + "[navigator.serviceWorker.controller?.scriptURL,controllerChanges]", + json!([format!("{base}/worker.js"), 1]), + ) + .await; + evaluate_window_name_probe(&mut child, 6, "window.clientURLs=null;navigator.serviceWorker.onmessage=e=>clientURLs=e.data;navigator.serviceWorker.controller.postMessage('clients');true").await; + wait_for_value( + &mut child, + "clientURLs?.sort()", + json!([format!("{base}/child"), format!("{base}/opener")]), + ) + .await; assert_eq!( evaluate_window_name_probe(&mut child, 5, "clearInterval(intervalId);typeof timeoutId") .await, diff --git a/moli-renderer-v8/src/native_bridge/context_host/core.rs b/moli-renderer-v8/src/native_bridge/context_host/core.rs index d08750a0a2..751b2d3912 100644 --- a/moli-renderer-v8/src/native_bridge/context_host/core.rs +++ b/moli-renderer-v8/src/native_bridge/context_host/core.rs @@ -315,26 +315,14 @@ impl JsContextHost { .unwrap_or_else(|| { super::service_workers::service_worker_first_party_storage_key(&document_url) }); - let service_worker_client_id = reserved_service_worker_client_id - .filter(|client_id| { - browser_context_runtime.update_service_worker_client_document_and_page_endpoint( - *client_id, - document_url.clone(), - service_worker_storage_key.clone(), - crate::service_worker_runtime::ServiceWorkerClientFrameType::TopLevel, - Some(super::WindowDocumentOwner::Frame(main_document_owner)), - service_worker_task_tx.clone(), - ) - }) - .unwrap_or_else(|| { - browser_context_runtime.register_service_worker_client( - document_url, - service_worker_storage_key, - crate::service_worker_runtime::ServiceWorkerClientFrameType::TopLevel, - Some(super::WindowDocumentOwner::Frame(main_document_owner)), - service_worker_task_tx.clone(), - ) - }); + let service_worker_client_id = Self::register_main_service_worker_client( + &browser_context_runtime, + document_url, + service_worker_storage_key, + main_document_owner, + service_worker_task_tx.clone(), + reserved_service_worker_client_id, + ); assert!( frame_owner_store .set_current_main_service_worker_client_id(Some(service_worker_client_id,)), diff --git a/moli-renderer-v8/src/native_bridge/context_host/service_workers.rs b/moli-renderer-v8/src/native_bridge/context_host/service_workers.rs index 63ce1c5592..2ab1aab6ab 100644 --- a/moli-renderer-v8/src/native_bridge/context_host/service_workers.rs +++ b/moli-renderer-v8/src/native_bridge/context_host/service_workers.rs @@ -103,6 +103,65 @@ pub(crate) struct PendingServiceWorkerClientsOpenWindowPopup { } impl JsContextHost { + pub(in crate::native_bridge::context_host) fn register_main_service_worker_client( + browser_context_runtime: &crate::runtime::RendererBrowserContextRuntime, + document_url: Url, + storage_key: String, + document_owner: FrameDocumentTaskOwner, + completion_tx: crate::page_task_queue::RendererPageServiceWorkerTaskSender, + reserved_client_id: Option, + ) -> ServiceWorkerClientId { + reserved_client_id + .filter(|client_id| { + browser_context_runtime.update_service_worker_client_document_and_page_endpoint( + *client_id, + document_url.clone(), + storage_key.clone(), + ServiceWorkerClientFrameType::TopLevel, + Some(WindowDocumentOwner::Frame(document_owner)), + completion_tx.clone(), + ) + }) + .unwrap_or_else(|| { + browser_context_runtime.register_service_worker_client( + document_url, + storage_key, + ServiceWorkerClientFrameType::TopLevel, + Some(WindowDocumentOwner::Frame(document_owner)), + completion_tx, + ) + }) + } + + pub(crate) fn adopt_initial_document_service_worker_client( + &mut self, + transition: crate::frame_owner_model::MainDocumentOwnerTransition, + reserved_client_id: Option, + ) { + let document_url = self.document_url().clone(); + let storage_key = self.service_worker_document_storage_key(); + let client_id = Self::register_main_service_worker_client( + &self.browser_context_runtime, + document_url, + storage_key, + transition.current_owner(), + self.service_worker_task_sender(), + reserved_client_id, + ); + let retired_client_id = std::mem::replace(&mut self.service_worker_client_id, client_id); + if retired_client_id != client_id { + self.browser_context_runtime + .unregister_service_worker_client(retired_client_id); + } + self.service_worker_control = None; + assert!( + self.frame_owner_store + .set_current_main_service_worker_client_id(Some(client_id)), + "initial Document commit requires a current service worker client owner" + ); + self.apply_main_service_worker_document_owner_transition(transition); + } + fn allocate_service_worker_request_id(&mut self) -> u64 { let request_id = self.next_service_worker_request_id; self.next_service_worker_request_id = request_id diff --git a/moli-renderer-v8/src/script_vm/initial_window_document.rs b/moli-renderer-v8/src/script_vm/initial_window_document.rs index 1526785215..7b63715e11 100644 --- a/moli-renderer-v8/src/script_vm/initial_window_document.rs +++ b/moli-renderer-v8/src/script_vm/initial_window_document.rs @@ -116,6 +116,10 @@ impl ScriptVm { page_task_tx.top_level_navigation_handoff_sender(), page_task_tx.service_worker_task_sender(), ); + host.adopt_initial_document_service_worker_client( + transition, + env.reserved_service_worker_client_id, + ); host.retire_document_resource_loader( crate::native_bridge::WindowDocumentOwner::Frame( transition.retired_owner(),