fix(renderer): replace the Service Worker client on initial Document commit

This commit is contained in:
ldm0
2026-10-06 01:01:03 +08:00
committed by Donough Liu
parent 85332301b2
commit 24ef334500
4 changed files with 96 additions and 22 deletions
@@ -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,
@@ -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,)),
@@ -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>,
) -> 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<ServiceWorkerClientId>,
) {
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
@@ -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(),