fix(custom-elements): honor owner registries during parser construction

This commit is contained in:
ldm0
2026-10-05 03:54:08 +08:00
committed by Donough Liu
parent a483ff3c76
commit 144eb02540
7 changed files with 74 additions and 20 deletions
@@ -18,7 +18,6 @@ pub(crate) fn create_and_construct_parser_custom_element_direct_for_document(
scope: &mut v8::PinScope<'_, '_>,
host_ptr: *mut JsContextHost,
document_handle: DomHandle,
document_has_body: bool,
local_name: &str,
namespace: &str,
prefix: Option<&str>,
@@ -29,7 +28,6 @@ pub(crate) fn create_and_construct_parser_custom_element_direct_for_document(
let definition_match = lookup_parser_custom_element_definition_for_token(
host_ptr,
document_handle,
document_has_body,
local_name,
namespace,
token_attributes,
@@ -16,7 +16,6 @@ pub(super) struct ParserCustomElementDefinitionMatch {
pub(super) fn lookup_parser_custom_element_definition_for_token(
host_ptr: *mut JsContextHost,
document_handle: DomHandle,
document_has_body: bool,
local_name: &str,
namespace: &str,
token_attributes: &[Attribute],
@@ -25,9 +24,6 @@ pub(super) fn lookup_parser_custom_element_definition_for_token(
if namespace != XHTML_NS {
return None;
}
if !document_has_body {
return None;
}
let host = unsafe { &*host_ptr };
let document_default =
host.default_custom_element_registry_association_for_document(document_handle);
@@ -331,15 +331,11 @@ impl ParserElementCreationConsumer for DocumentWriteParserMutationOwner<'_, '_,
&mut self,
request: ParserElementCreationRequest<'_>,
) -> Option<DomHandle> {
let document_has_body = self
.document_body_handle_for_document(request.document_handle)
.is_some();
let runtime = &mut *self.runtime;
custom_elements::create_and_construct_parser_custom_element_direct_for_document(
self.scope,
self.host_ptr,
request.document_handle,
document_has_body,
request.local_name,
request.namespace,
request.prefix,
@@ -504,9 +504,6 @@ impl ParserElementCreationConsumer for ChildFrameLiveParserOwner<'_, '_, '_> {
if !self.targets_current_document() {
return None;
}
let document_has_body = self
.document_body_handle_for_document(request.document_handle)
.is_some();
let child_handle = self
.host
.child_browsing_context_host_for_document_handle(request.document_handle)?;
@@ -521,7 +518,6 @@ impl ParserElementCreationConsumer for ChildFrameLiveParserOwner<'_, '_, '_> {
scope,
host_ptr,
request.document_handle,
document_has_body,
request.local_name,
request.namespace,
request.prefix,
@@ -369,14 +369,10 @@ impl ParserElementCreationConsumer for PhaseOneParserOwner<'_> {
&mut self,
request: ParserElementCreationRequest<'_>,
) -> Option<NativeNodeId> {
let document_has_body = self
.document_body_handle_for_document(request.document_handle)
.is_some();
self.vm
.create_and_construct_parser_custom_element_direct_in_default_context(
request.construction,
request.document_handle,
document_has_body,
request.local_name,
request.namespace,
request.prefix,
@@ -82,7 +82,6 @@ impl ScriptVm {
&mut self,
construction: &moli_dom::native::ParserConstruction,
document_handle: DomHandle,
document_has_body: bool,
local_name: &str,
namespace: &str,
prefix: Option<&str>,
@@ -107,7 +106,6 @@ impl ScriptVm {
scope,
host_ptr,
document_handle,
document_has_body,
local_name,
namespace,
prefix,
@@ -2082,3 +2082,77 @@ async fn child_document_write_custom_element_reaction_queue_wpt_shape() {
assert_eq!(result, expected);
}
#[test]
fn child_document_write_constructs_predefined_custom_elements_without_a_body() {
let mut vm = new_storage_test_vm("https://document-write-custom-elements.test/");
vm.eval(
r#"
(() => {
const target = document.body || document.documentElement || document;
window.__writeCustomElementFrames = ["write", "writeln"].map(() => {
const frame = document.createElement("iframe");
target.appendChild(frame);
frame.srcdoc = "";
return frame;
});
return "scheduled";
})()
"#,
)
.expect("child document.write predefined custom element setup should evaluate");
vm.drain_pending_child_frame_work_for_test();
let result = vm
.eval(
r#"
(() => {
const exercise = (frame, method, name) => {
const childWindow = frame.contentWindow;
const childDocument = frame.contentDocument;
const registry = childWindow.customElements;
let constructorCount = 0;
let errorName = null;
childWindow.addEventListener("error", event => {
errorName = event.error && event.error.name;
event.preventDefault();
}, { once: true });
class DefinedElement extends childWindow.HTMLElement {
constructor() {
super();
constructorCount++;
}
}
registry.define(name, DefinedElement);
const definitionBeforeWrite = registry.get(name) === DefinedElement;
childDocument[method](`<${name}></${name}>`);
const element = childDocument.querySelector(name);
return {
registryPreserved: childWindow.customElements === registry,
definitionBeforeWrite,
definitionAfterWrite: registry.get(name) === DefinedElement,
constructorCount,
errorName,
htmlElement: element instanceof childWindow.HTMLElement,
customElement: element instanceof DefinedElement,
ownerDocument: element.ownerDocument === childDocument
};
};
const [writeFrame, writelnFrame] = window.__writeCustomElementFrames;
return JSON.stringify({
write: exercise(writeFrame, "write", "write-defined-element"),
writeln: exercise(writelnFrame, "writeln", "writeln-defined-element")
});
})()
"#,
)
.expect("child document.write predefined custom element result should evaluate");
assert_eq!(
result,
r#"{"write":{"registryPreserved":true,"definitionBeforeWrite":true,"definitionAfterWrite":true,"constructorCount":1,"errorName":null,"htmlElement":true,"customElement":true,"ownerDocument":true},"writeln":{"registryPreserved":true,"definitionBeforeWrite":true,"definitionAfterWrite":true,"constructorCount":1,"errorName":null,"htmlElement":true,"customElement":true,"ownerDocument":true}}"#
);
}