fix(html): fetch embedded javascript URLs as resources

This commit is contained in:
ldm0
2026-09-08 02:00:31 +08:00
parent 78b74a4146
commit 6d30c791b9
2 changed files with 86 additions and 1 deletions
@@ -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)
{
@@ -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/");