fix(domparser): preserve HTML parse modes

This commit is contained in:
ldm0
2026-09-28 02:18:32 +08:00
parent a47b30ae5e
commit db3de692dd
6 changed files with 87 additions and 8 deletions
@@ -2553,7 +2553,6 @@ dom/ranges/tentative/OpaqueRange-range-updates.html
dom/ranges/tentative/OpaqueRange-supported-elements.html
dom/ranges/tentative/OpaqueRange-unsupported-elements.html
dom/ranges/tentative/OpaqueRange-validation.html
domparsing/DOMParser-parseFromString-html.html
domparsing/DOMParser-parseFromString-url-base-pushstate.html
domparsing/DOMParser-parseFromString-url-base.html
domparsing/DOMParser-parseFromString-url-moretests.html
@@ -4097,6 +4097,7 @@ dom/traversal/TreeWalker.html
dom/window-extends-event-target.html
dom/xpath-result-single-node-value-nullable.html
domparsing/DOMParser-parseFromString-encoding.html
domparsing/DOMParser-parseFromString-html.html
domparsing/DOMParser-parseFromString-stylesheets.html
domparsing/DOMParser-parseFromString-xml-parsererror.html
domparsing/createContextualFragment-in-detached-xml-document-crash.html
+19
View File
@@ -1120,6 +1120,25 @@ impl DomHost {
true
}
pub fn set_document_quirks_mode_for_handle(
&mut self,
document_handle: DomHandle,
quirks_mode: selectors::matching::QuirksMode,
) -> bool {
let Some(document) = self
.node_mut(document_handle)
.and_then(|node| node.data_mut().as_document_mut())
else {
return false;
};
if document.quirks_mode() == quirks_mode {
return false;
}
document.set_quirks_mode(quirks_mode);
self.record_mutation(MutationScope::QueryState);
true
}
pub fn set_document_default_language_for_handle(
&mut self,
document_handle: DomHandle,
@@ -200,16 +200,34 @@ pub(crate) fn build_detached_document_object_from_dom_host_with_content_type<'s>
character_set: Option<&str>,
) -> Option<v8::Local<'s, v8::Object>> {
let url = detached_document_url(&parsed);
let quirks_mode = parsed.dom().document()?.quirks_mode();
let compat_mode = if quirks_mode == selectors::matching::QuirksMode::Quirks {
"BackCompat"
} else {
"CSS1Compat"
};
let content_type = content_type.unwrap_or(parsed.dom().document()?.content_type());
let document = new_detached_document_shell(scope, kind, content_type, url)?;
if let Some(state) = detached_state_object(scope, document)
&& let Some(character_set) = character_set
{
if let Some(state) = detached_state_object(scope, document) {
let _ = state.set(
scope,
v8str(scope, "characterSet").into(),
v8_string(scope, character_set)?.into(),
v8str(scope, "compatMode").into(),
v8_string(scope, compat_mode)?.into(),
);
if let Some(character_set) = character_set {
let _ = state.set(
scope,
v8str(scope, "characterSet").into(),
v8_string(scope, character_set)?.into(),
);
}
}
if let Some(document_handle) = detached_native_handle(scope, document)
&& let Some(runtime_ptr) = context_host_ptr_from_global_bridge(scope)
{
let _ = unsafe { &mut *runtime_ptr }
.dom_host_mut()
.set_document_quirks_mode_for_handle(document_handle, quirks_mode);
}
import_detached_document_children_from_host(scope, document, &parsed)?;
Some(document)
@@ -267,10 +267,14 @@ pub(in crate::native_bridge) fn bridge_detached_document_character_set_callback<
pub(in crate::native_bridge) fn bridge_detached_document_compat_mode_callback<'a>(
scope: &mut v8::PinScope<'a, '_>,
_args: v8::FunctionCallbackArguments<'a>,
args: v8::FunctionCallbackArguments<'a>,
mut rv: v8::ReturnValue<'_, v8::Value>,
) {
set_string_return_value(scope, &mut rv, "CSS1Compat");
let compat_mode = v8::Local::<v8::Object>::try_from(args.get(0))
.ok()
.map(|document| detached_document_state_string(scope, document, "compatMode", "CSS1Compat"))
.unwrap_or_else(|| "CSS1Compat".to_owned());
set_string_return_value(scope, &mut rv, &compat_mode);
}
pub(in crate::native_bridge) fn bridge_detached_document_referrer_callback<'a>(
@@ -271,6 +271,44 @@ fn adopted_xml_cdata_uses_html_fragment_serialization() {
);
}
#[test]
fn domparser_html_preserves_quirks_mode_and_parses_with_scripting_disabled() {
let mut vm = new_storage_test_vm("https://domparser-html-mode.test/");
let result = vm
.eval(
r#"
(() => {
const parser = new DOMParser();
const quirks = parser.parseFromString(
"<html><head></head><body></body></html>",
"text/html"
);
const standards = parser.parseFromString(
"<!doctype html><html><head></head><body></body></html>",
"text/html"
);
const noscript = parser.parseFromString(
"<body><noscript><p id='first'></p><p id='second'></p></noscript></body>",
"text/html"
);
return JSON.stringify({
quirks: quirks.compatMode,
standards: standards.compatMode,
noscriptChildren: Array.from(noscript.querySelector("noscript").children)
.map(element => element.id)
});
})()
"#,
)
.expect("DOMParser HTML parse mode probe should evaluate");
assert_eq!(
result,
r#"{"quirks":"BackCompat","standards":"CSS1Compat","noscriptChildren":["first","second"]}"#
);
}
#[test]
fn detached_query_brand_checks_accept_standard_prototype_methods() {
let mut vm = new_storage_test_vm("https://detached-query-brand-check.test/");