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

This commit is contained in:
ldm0
2026-09-27 19:23:03 +08:00
parent cc3152e87e
commit 1d0a620fd6
6 changed files with 74 additions and 16 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);
@@ -337,15 +337,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,
@@ -375,14 +375,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,
-2
View File
@@ -4733,7 +4733,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>,
@@ -4758,7 +4757,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}}"#
);
}