From 6d30c791b9612fb08d2bbbc97b7f6b3605f8d5d2 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Mon, 31 Aug 2026 06:41:03 +0800 Subject: [PATCH] fix(html): fetch embedded javascript URLs as resources --- .../context_host/child_frames/discovery.rs | 15 +++- .../tests/dom_elements/dom_surface.rs | 72 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/moli-renderer-v8/src/native_bridge/context_host/child_frames/discovery.rs b/moli-renderer-v8/src/native_bridge/context_host/child_frames/discovery.rs index 4e7f614aa9..8c5cfc73f4 100644 --- a/moli-renderer-v8/src/native_bridge/context_host/child_frames/discovery.rs +++ b/moli-renderer-v8/src/native_bridge/context_host/child_frames/discovery.rs @@ -1,3 +1,4 @@ +use super::super::ChildBrowsingContextNavigationRequest; use super::*; use crate::dom::native::Node; @@ -212,7 +213,19 @@ impl JsContextHost { { return None; } - let bootstrap = ChildBrowsingContextBootstrap::Url(url); + let bootstrap = if !kind.always_hosts_document() && url.scheme() == "javascript" { + // embed/object setup fetches its attribute resource. Preserve that + // distinction from iframe/frame navigation so Fetch rejects the + // javascript scheme instead of executing it in the child realm. + ChildBrowsingContextBootstrap::Request(ChildBrowsingContextNavigationRequest { + url, + method: "GET".to_owned(), + body: None, + request_headers: Vec::new(), + }) + } else { + ChildBrowsingContextBootstrap::Url(url) + }; if kind == ChildFrameOwnerElementKind::Object && self.object_fallback_bootstraps.get(&handle) == Some(&bootstrap) { diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs index ed7bac4ee2..66309df9ca 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs @@ -10143,6 +10143,78 @@ fn document_open_with_three_arguments_uses_associated_window() { assert_eq!(result, "true|true|true|InvalidAccessError:15"); } + +#[tokio::test] +async fn embed_and_object_javascript_attributes_use_resource_fetch_not_script_navigation() { + let loader = ResourceRequestClient::new(&moli_fetch::FetchConfig::default()).expect("loader"); + let mut vm = new_storage_page_task_executor_test_vm_with_loader( + "https://embedded-javascript-attribute.test/", + &loader, + ); + + vm.eval( + r#" +(() => { + globalThis.__embeddedJavascriptAttributeEvents = []; + const root = document.body || document.documentElement || document; + for (const [tag, attribute] of [["embed", "src"], ["object", "data"]]) { + const element = document.createElement(tag); + element[attribute] = + `javascript:parent.__embeddedJavascriptAttributeEvents.push("executed-${tag}")`; + element.onload = () => { + __embeddedJavascriptAttributeEvents.push(`load-${tag}`); + }; + if (tag === "object") { + element.onerror = event => { + __embeddedJavascriptAttributeEvents.push( + `error-object:${event.isTrusted}:${element.contentDocument === null}` + ); + }; + } + root.appendChild(element); + } +})() +"#, + ) + .expect("embedded javascript attribute setup should evaluate"); + + for element in ["embed", "object"] { + expect_page_child_frame_task_source_after_realm_prerequisite( + &mut vm, + &loader, + ChildFrameSemanticTurnKind::NavigationCommit, + &format!("{element} javascript attribute should enter its navigation commit turn"), + ) + .await; + } + assert!( + vm._context_host.borrow().has_pending_child_document_loads(), + "embed and object javascript attributes should start resource fetches" + ); + assert!( + !vm.has_pending_child_frame_realm_materialization(), + "resource attributes must not schedule javascript execution in child realms" + ); + for element in ["embed", "object"] { + wait_for_one_page_resource_completion_selected_task_executor_test_turn( + &mut vm, + &loader, + &format!("{element} javascript attribute fetch failure"), + ) + .await; + } + assert!( + !vm._context_host.borrow().has_pending_child_document_loads(), + "both embedded resource fetch failures should settle" + ); + assert_eq!( + vm.eval("JSON.stringify(__embeddedJavascriptAttributeEvents)") + .expect("embedded javascript attribute events should evaluate"), + r#"["error-object:true:true"]"#, + "resource failures must not execute javascript or load, and object must enter fallback" + ); +} + #[tokio::test] async fn iframe_javascript_url_string_completion_replaces_child_document() { let mut vm = new_storage_test_vm("https://iframe-javascript-url.test/");