From e1820f2b6bc85669efcf512626f09192c04edd25 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Wed, 30 Sep 2026 00:59:48 +0800 Subject: [PATCH] fix(script): share the service-worker loader for child classics --- moli-core/tests/scripts/integrity.rs | 52 +++++++++++++++++++ .../child_frames/classic_scripts.rs | 22 +++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/moli-core/tests/scripts/integrity.rs b/moli-core/tests/scripts/integrity.rs index fc9134b20a..882aa40894 100644 --- a/moli-core/tests/scripts/integrity.rs +++ b/moli-core/tests/scripts/integrity.rs @@ -335,6 +335,11 @@ fn fixture_response( ); ("200 OK", javascript.to_owned(), body) } + "/child-classic.html" => ( + "200 OK", + "Content-Type: text/html\r\n".to_owned(), + "".to_owned(), + ), "/page.html" | "/parser.html" | "/cookie-page.html" | "/csp-page.html" => { let mut html = "".to_owned(); if path == "/csp-page.html" { @@ -792,6 +797,53 @@ async fn manifest_roundtrip_checks_intermediate_and_final_cors_responses() -> Re Ok(()) } +#[tokio::test(flavor = "multi_thread")] +async fn child_classic_scripts_use_the_controlling_service_worker() -> Result<()> { + let servers = IntegrityServers::spawn().await?; + let browser = Browser::new(AppConfig::default())?; + let mut page = browser + .fetch(&format!("{}/page.html", servers.origin)) + .await?; + let result = tokio::time::timeout( + Duration::from_secs(15), + page.evaluate_runtime_expression_with_await_async( + r#"(async () => { + const controlled = navigator.serviceWorker.controller ? Promise.resolve() : + new Promise(resolve => navigator.serviceWorker.addEventListener('controllerchange', resolve, {once:true})); + await navigator.serviceWorker.register('/worker.js', {scope:'/'}); + await navigator.serviceWorker.ready; + await controlled; + const child = document.createElement('iframe'); + child.src='/child-classic.html'; + await new Promise(resolve => {child.onload=resolve;document.body.append(child);}); + const other = child.contentWindow; + if (other.sriExecutions !== 1) throw new Error('parser script did not execute'); + const script = other.document.createElement('script'); + script.src='/sw-default.js?dynamic'; + await new Promise((resolve,reject) => { + script.onload=resolve; + script.onerror=() => reject(new Error('dynamic script failed')); + other.document.body.append(script); + }); + const executions = other.sriExecutions; + child.remove(); + return executions; + })()"#, + true, + ), + ).await??; + assert_eq!(result["value"], 2, "{result}"); + assert!( + !servers + .requests + .lock() + .iter() + .any(|request| request.path.starts_with("/sw-default.js")), + "both child script paths must be served by the controlling service worker" + ); + Ok(()) +} + #[tokio::test(flavor = "multi_thread")] async fn service_worker_script_integrity_preserves_response_filter() -> Result<()> { let servers = IntegrityServers::spawn().await?; diff --git a/moli-renderer-v8/src/native_bridge/context_host/child_frames/classic_scripts.rs b/moli-renderer-v8/src/native_bridge/context_host/child_frames/classic_scripts.rs index 019eb73d34..3bc269eae9 100644 --- a/moli-renderer-v8/src/native_bridge/context_host/child_frames/classic_scripts.rs +++ b/moli-renderer-v8/src/native_bridge/context_host/child_frames/classic_scripts.rs @@ -295,14 +295,23 @@ impl JsContextHost { .unwrap_or_else(|| self.document_character_set().to_owned()); let completion_tx = self.resource_completion_tx.clone(); let task_loader = loader.clone(); + let browser_context_runtime = self.browser_context_runtime(); + let client_id = self.service_worker_client_id_for_subresource_owner( + crate::native_bridge::context_host::OwnerDispatchScope::Child(handle), + ); + let document_url = network_attribution.document_url.clone(); loader.spawn_resource_task(async move { let outcome = - crate::planning::load_prepared_script_source_outcome_with_document_character_set( + crate::planning::load_service_worker_aware_external_script_source_outcome( &script_for_load, &task_loader.fetch_context().request_origin(), task_loader.request_client(), + task_loader.task_runner(), Some(&document_character_set), None, + browser_context_runtime, + client_id, + document_url, ) .await; let _ = completion_tx.send_child_classic_script(ChildClassicScriptLoadCompletion { @@ -708,14 +717,23 @@ impl JsContextHost { return; } let task_loader = loader.clone(); + let browser_context_runtime = self.browser_context_runtime(); + let client_id = self.service_worker_client_id_for_subresource_owner( + crate::native_bridge::context_host::OwnerDispatchScope::Child(child_handle), + ); + let document_url = network_attribution.document_url.clone(); loader.spawn_resource_task(async move { let outcome = - crate::planning::load_prepared_script_source_outcome_with_document_character_set( + crate::planning::load_service_worker_aware_external_script_source_outcome( &script_for_load, &task_loader.fetch_context().request_origin(), task_loader.request_client(), + task_loader.task_runner(), Some(&document_character_set), None, + browser_context_runtime, + client_id, + document_url, ) .await; let _ = completion_tx.send_child_classic_script(ChildClassicScriptLoadCompletion {