refactor(parser): centralize element construction lifecycles

Separate parser provenance from active child construction. Share one
construction session across HTML/XML parsers and runtime adapters, with
completion ownership, normal-EOF checks, and cancellation that does not
publish incomplete styles.

Use ordinary creation for generated document shells and remove obsolete
per-element completion forwarding. Cover parser ownership, adoption,
duplicate completion, missing callbacks, and cancellation in tests.
This commit is contained in:
ldm0
2026-09-26 07:01:12 +08:00
committed by Donough Liu
parent 83e0afd1a1
commit 511c8a2341
24 changed files with 499 additions and 353 deletions
+2 -2
View File
@@ -622,7 +622,7 @@ impl ElementControlState {
.is_some_and(ScriptElementState::note_parser_created)
}
pub fn note_parser_created_link(&mut self) -> bool {
pub fn begin_parsing_link_children(&mut self) -> bool {
if self.link_created_by_parser {
return false;
}
@@ -634,7 +634,7 @@ impl ElementControlState {
self.style_children_parsing
}
pub fn note_parser_created_style(&mut self) {
pub fn begin_parsing_style_children(&mut self) {
self.style_children_parsing = true;
}
+14 -15
View File
@@ -94,6 +94,8 @@ impl Element {
)
}
/// Record parser provenance (including script preparation semantics).
/// Child construction is started separately by `ParserConstruction`.
pub fn new_parser_created(
local_name: String,
namespace: String,
@@ -133,21 +135,6 @@ impl Element {
{
rare_data.control_state_mut().note_parser_created_script();
}
if creation_source == ElementCreationSource::Parser
&& local_name == "link"
&& namespace == "http://www.w3.org/1999/xhtml"
{
rare_data.control_state_mut().note_parser_created_link();
}
if creation_source == ElementCreationSource::Parser
&& local_name == "style"
&& matches!(
namespace.as_str(),
"http://www.w3.org/1999/xhtml" | "http://www.w3.org/2000/svg"
)
{
rare_data.control_state_mut().note_parser_created_style();
}
Self {
local_name: LocalName::from(local_name),
namespace: Namespace::from(namespace),
@@ -779,6 +766,18 @@ impl Element {
.finish_parsing_script_children(source)
}
pub(in crate::native) fn begin_parsing_children(&mut self) -> bool {
if self.is_inline_style_element() {
self.control_state_mut().begin_parsing_style_children();
true
} else if self.is_html_element("link") {
self.control_state_mut().begin_parsing_link_children();
true
} else {
self.is_script_element()
}
}
pub fn finish_parsing_link_children(&mut self) -> bool {
if !self.is_html_element("link") {
return false;
+2
View File
@@ -410,6 +410,8 @@ fn parser_created_link_processing_state_is_consumed_at_children_finish() {
None,
Vec::new(),
);
assert!(!parser_created.link_created_by_parser());
assert!(parser_created.begin_parsing_children());
assert!(parser_created.link_created_by_parser());
assert!(parser_created.finish_parsing_link_children());
assert!(!parser_created.link_created_by_parser());
+15 -30
View File
@@ -713,24 +713,15 @@ impl DomHost {
let document_element = match self.document_element_handle() {
Some(document_element) => document_element,
None => {
let html = self.create_parser_element(
"html".to_owned(),
"http://www.w3.org/1999/xhtml".to_owned(),
None,
Vec::new(),
);
let head = self.create_parser_element(
"head".to_owned(),
"http://www.w3.org/1999/xhtml".to_owned(),
None,
Vec::new(),
);
let body = self.create_parser_element(
"body".to_owned(),
"http://www.w3.org/1999/xhtml".to_owned(),
None,
Vec::new(),
);
let html = self
.create_element_ns(Some("http://www.w3.org/1999/xhtml"), "html")
.expect("valid HTML element");
let head = self
.create_element_ns(Some("http://www.w3.org/1999/xhtml"), "head")
.expect("valid HTML element");
let body = self
.create_element_ns(Some("http://www.w3.org/1999/xhtml"), "body")
.expect("valid HTML element");
let _ = self.append_child(self.document_handle(), html);
let _ = self.append_child(html, head);
let _ = self.append_child(html, body);
@@ -744,12 +735,9 @@ impl DomHost {
let body = self.ensure_html_document_body()?;
if self.document_head_handle().is_none() {
let head = self.create_parser_element(
"head".to_owned(),
"http://www.w3.org/1999/xhtml".to_owned(),
None,
Vec::new(),
);
let head = self
.create_element_ns(Some("http://www.w3.org/1999/xhtml"), "head")
.expect("valid HTML element");
let _ = self.insert_before(document_element, head, Some(body));
}
Some(body)
@@ -770,12 +758,9 @@ impl DomHost {
return None;
}
let body = self.create_parser_element(
"body".to_owned(),
"http://www.w3.org/1999/xhtml".to_owned(),
None,
Vec::new(),
);
let body = self
.create_element_ns(Some("http://www.w3.org/1999/xhtml"), "body")
.expect("valid HTML element");
let _ = self.append_child(document_element, body);
Some(body)
}
+21 -6
View File
@@ -857,7 +857,7 @@ impl DomHost {
did_change
}
pub fn finish_parsing_script_children(&mut self, handle: DomHandle) -> bool {
pub(in crate::native) fn finish_parsing_script_children(&mut self, handle: DomHandle) -> bool {
let Some(source) = self.dom.direct_text_content(handle) else {
return false;
};
@@ -876,7 +876,7 @@ impl DomHost {
did_change
}
pub fn finish_parsing_link_children(&mut self, handle: DomHandle) -> bool {
fn finish_parsing_link_children(&mut self, handle: DomHandle) -> bool {
let did_change = {
let Some(element) = self
.node_mut(handle)
@@ -894,10 +894,7 @@ impl DomHost {
/// Finish a parser-created style at its closing tag or EOF. Report the
/// lifecycle transition without producing child or text mutation records.
pub fn finish_parsing_style_children_effects(
&mut self,
handle: DomHandle,
) -> DomMutationEffects {
fn finish_parsing_style_children_effects(&mut self, handle: DomHandle) -> DomMutationEffects {
let did_change = self
.node_mut(handle)
.and_then(|node| node.data_mut().as_element_mut())
@@ -914,6 +911,24 @@ impl DomHost {
effects
}
pub(in crate::native) fn finish_parsing_children_effects(
&mut self,
handle: DomHandle,
) -> DomMutationEffects {
if self
.node(handle)
.and_then(Node::as_element)
.is_some_and(Element::is_script_element)
{
self.finish_parsing_script_children(handle);
} else if self.is_html_element_named(handle, "link") {
self.finish_parsing_link_children(handle);
} else {
return self.finish_parsing_style_children_effects(handle);
}
DomMutationEffects::default()
}
pub fn set_cryptographic_nonce(&mut self, handle: DomHandle, nonce: Option<String>) -> bool {
let did_change = {
let Some(element) = self
+10 -12
View File
@@ -25,18 +25,8 @@ impl DomHost {
}
}
pub fn create_parser_element(
&mut self,
local_name: String,
namespace: String,
prefix: Option<String>,
attributes: Vec<crate::dom::native::Attribute>,
) -> DomHandle {
let node_id = self.create_parser_element_without_attributes(local_name, namespace, prefix);
self.add_attrs_if_missing_for_parser(node_id, attributes);
node_id
}
/// Allocate parser provenance without starting child construction. Active
/// parsers allocate through `ParserConstruction` instead.
pub fn create_parser_element_without_attributes(
&mut self,
local_name: String,
@@ -51,6 +41,8 @@ impl DomHost {
)
}
/// Allocate an element with parser provenance only. An active parser must
/// use `ParserConstruction::create_element` to also own child completion.
pub fn create_parser_element_without_attributes_for_document(
&mut self,
document_handle: DomHandle,
@@ -61,6 +53,12 @@ impl DomHost {
self.allocate_element_for_document(document_handle, local_name, namespace, prefix, true)
}
pub(in crate::native) fn begin_parsing_children(&mut self, node: DomHandle) -> bool {
self.node_mut(node)
.and_then(|node| node.data_mut().as_element_mut())
.is_some_and(Element::begin_parsing_children)
}
pub fn parser_template_contents_handle(&self, node_id: DomHandle) -> Option<DomHandle> {
self.node(node_id)
.and_then(Node::as_element)
+25 -31
View File
@@ -5,6 +5,7 @@ mod host;
mod html_serialization;
mod markdown;
mod node;
mod parser_construction;
mod queries;
mod scripts;
@@ -36,6 +37,7 @@ pub use node::{
CDataSection, Comment, LiveDomNodeMetadata, NativeNodeId, Node, NodeData, NodeFlags, NodeType,
ProcessingInstruction, Text,
};
pub use parser_construction::ParserConstruction;
// Node IDs remain dense indexes, while immutable page snapshots share complete
// chunks. A mutation detaches only its 256-node chunk, bounding copy-on-write
@@ -2955,12 +2957,9 @@ mod tests {
#[test]
fn ensure_html_document_body_synthesizes_missing_body_without_replacing_frameset() {
let mut host = DomHost::from_dom(NativeDom::new_html(test_url()));
let html = host.create_parser_element(
"html".to_owned(),
"http://www.w3.org/1999/xhtml".to_owned(),
None,
Vec::new(),
);
let html = host
.create_element_ns(Some("http://www.w3.org/1999/xhtml"), "html")
.expect("valid HTML element");
assert!(host.append_child(host.document_handle(), html));
let body = host
@@ -2970,23 +2969,18 @@ mod tests {
assert_eq!(host.node(body).and_then(Node::parent_node), Some(html));
assert!(
host.node(body)
.is_some_and(|node| node.flags().parser_created())
.is_some_and(|node| !node.flags().parser_created()),
"synthesizing a missing body is ordinary DOM construction"
);
assert_eq!(host.ensure_html_document_body(), Some(body));
let mut frameset_host = DomHost::from_dom(NativeDom::new_html(test_url()));
let frameset_html = frameset_host.create_parser_element(
"html".to_owned(),
"http://www.w3.org/1999/xhtml".to_owned(),
None,
Vec::new(),
);
let frameset = frameset_host.create_parser_element(
"frameset".to_owned(),
"http://www.w3.org/1999/xhtml".to_owned(),
None,
Vec::new(),
);
let frameset_html = frameset_host
.create_element_ns(Some("http://www.w3.org/1999/xhtml"), "html")
.expect("valid HTML element");
let frameset = frameset_host
.create_element_ns(Some("http://www.w3.org/1999/xhtml"), "frameset")
.expect("valid HTML element");
assert!(frameset_host.append_child(frameset_host.document_handle(), frameset_html));
assert!(frameset_host.append_child(frameset_html, frameset));
@@ -3012,6 +3006,12 @@ mod tests {
assert!(host.is_html_element_named(html, "html"));
assert!(host.is_html_element_named(head, "head"));
assert!(host.is_html_element_named(body, "body"));
for generated in [html, head, body] {
assert!(
!host.node(generated).unwrap().flags().parser_created(),
"generated document shells do not enter parser construction"
);
}
assert_eq!(
host.node(doctype).and_then(Node::parent_node),
Some(host.document_handle())
@@ -3029,18 +3029,12 @@ mod tests {
#[test]
fn ensure_html_document_shell_inserts_missing_head_before_body() {
let mut host = DomHost::from_dom(NativeDom::new_html(test_url()));
let html = host.create_parser_element(
"html".to_owned(),
"http://www.w3.org/1999/xhtml".to_owned(),
None,
Vec::new(),
);
let body = host.create_parser_element(
"body".to_owned(),
"http://www.w3.org/1999/xhtml".to_owned(),
None,
Vec::new(),
);
let html = host
.create_element_ns(Some("http://www.w3.org/1999/xhtml"), "html")
.expect("valid HTML element");
let body = host
.create_element_ns(Some("http://www.w3.org/1999/xhtml"), "body")
.expect("valid HTML element");
assert!(host.append_child(host.document_handle(), html));
assert!(host.append_child(html, body));
+175
View File
@@ -0,0 +1,175 @@
use std::{cell::Cell, cell::RefCell, collections::HashSet};
use super::{DomHost, DomMutationEffects, NativeNodeId};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
enum ConstructionState {
#[default]
Parsing,
Finished,
Aborted,
}
/// Owns child-construction obligations for one parser, independently of where
/// its nodes are subsequently moved. Creating a node through this session
/// starts its lifecycle before attributes, insertion, or custom constructors
/// can expose it. Ordinary DOM creation has no such obligation.
#[derive(Debug, Default)]
pub struct ParserConstruction {
pending: RefCell<HashSet<NativeNodeId>>,
state: Cell<ConstructionState>,
}
impl ParserConstruction {
pub fn create_element(
&self,
host: &mut DomHost,
document: NativeNodeId,
local_name: String,
namespace: String,
prefix: Option<String>,
) -> NativeNodeId {
assert_eq!(self.state.get(), ConstructionState::Parsing);
let node = host.create_parser_element_without_attributes_for_document(
document, local_name, namespace, prefix,
);
if host.begin_parsing_children(node) {
assert!(self.pending.borrow_mut().insert(node));
}
node
}
pub fn is_pending(&self, node: NativeNodeId) -> bool {
self.pending.borrow().contains(&node)
}
/// Close a node once, before delivering any resulting runtime effects.
pub fn finish_children(&self, host: &mut DomHost, node: NativeNodeId) -> DomMutationEffects {
if !self.pending.borrow_mut().remove(&node) {
return DomMutationEffects::default();
}
host.finish_parsing_children_effects(node)
}
/// Called after the tree builder has processed normal EOF. Missing close
/// callbacks are bugs, not implicit permission to publish unfinished text.
pub fn finish(&self) {
assert_ne!(self.state.get(), ConstructionState::Aborted);
assert!(
self.pending.borrow().is_empty(),
"parser finished with unfinished element children: {:?}",
self.pending.borrow(),
);
self.state.set(ConstructionState::Finished);
}
/// Cancellation deliberately leaves unfinished DOM elements unprocessed.
/// In particular, dropping a parser must not publish a partial stylesheet.
pub fn abort(&self) {
self.pending.borrow_mut().clear();
self.state.set(ConstructionState::Aborted);
}
}
impl Drop for ParserConstruction {
fn drop(&mut self) {
if self.state.get() == ConstructionState::Parsing {
self.abort();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::native::{DomStylesheetOwnerChangeKind, NativeDom};
fn host() -> DomHost {
DomHost::from_dom(NativeDom::new_html(
url::Url::parse("https://parser-construction.test/").unwrap(),
))
}
fn style(construction: &ParserConstruction, host: &mut DomHost) -> NativeNodeId {
construction.create_element(
host,
host.document_handle(),
"style".into(),
"http://www.w3.org/1999/xhtml".into(),
None,
)
}
#[test]
fn parser_provenance_does_not_start_child_construction() {
let mut host = host();
let style = host.create_parser_element_without_attributes(
"style".into(),
"http://www.w3.org/1999/xhtml".into(),
None,
);
assert!(host.node(style).unwrap().flags().parser_created());
assert!(!host.is_style_element_parsing_children(style));
}
#[test]
fn completion_belongs_to_the_creating_parser_after_document_adoption() {
let mut host = host();
let creator = ParserConstruction::default();
let other_parser = ParserConstruction::default();
let style = style(&creator, &mut host);
let other_document = host.create_detached_html_document();
assert_eq!(host.adopt_node(other_document, style), Some(style));
let ignored = other_parser.finish_children(&mut host, style);
assert!(ignored.stylesheet_owners().changes().is_empty());
assert!(host.is_style_element_parsing_children(style));
let completion = creator.finish_children(&mut host, style);
assert!(!host.is_style_element_parsing_children(style));
assert_eq!(completion.stylesheet_owners().changes().len(), 1);
assert_eq!(
completion.stylesheet_owners().changes()[0].kind(),
&DomStylesheetOwnerChangeKind::ParsingFinished
);
assert!(
creator
.finish_children(&mut host, style)
.stylesheet_owners()
.changes()
.is_empty()
);
creator.finish();
other_parser.finish();
}
#[test]
#[should_panic(expected = "parser finished with unfinished element children")]
fn normal_finish_rejects_a_missing_close_callback() {
let mut host = host();
let construction = ParserConstruction::default();
style(&construction, &mut host);
construction.finish();
}
#[test]
fn explicit_abort_and_drop_leave_incomplete_styles_unprocessed() {
for explicit in [false, true] {
let mut host = host();
let construction = ParserConstruction::default();
let style = style(&construction, &mut host);
host.set_text_content(style, "body { color: red; }");
if explicit {
construction.abort();
assert!(
construction
.finish_children(&mut host, style)
.stylesheet_owners()
.changes()
.is_empty()
);
}
drop(construction);
assert!(host.is_style_element_parsing_children(style));
}
}
}
+7 -1
View File
@@ -1568,6 +1568,10 @@ impl DocumentSink {
.pop_pending_blocking_stylesheet_pause()
}
pub(super) fn finish_construction(&self) {
self.target.borrow().finish_construction();
}
pub(super) fn begin_tree_builder_finish(&self) {
self.target.borrow_mut().begin_tree_builder_finish();
}
@@ -1619,7 +1623,9 @@ impl TreeSink for DocumentSink {
Self: 'a;
fn finish(self) -> Self::Output {
self.target.into_inner()
let target = self.target.into_inner();
target.finish_construction();
target
}
fn parse_error(&self, err: Cow<'static, str>) {
+86 -132
View File
@@ -11,6 +11,7 @@ use moli_dom::{
NodeId,
native::{
Attribute as NativeAttribute, DomHost, DomMutationEffects, NativeDom, NativeNodeId, Node,
ParserConstruction,
},
};
use moli_stylesheet_blocking::{
@@ -571,6 +572,7 @@ pub trait ParserDomMutationConsumer {
fn create_parser_element_for_document_without_attributes(
&mut self,
construction: &ParserConstruction,
document_handle: NativeNodeId,
local_name: String,
namespace: String,
@@ -625,13 +627,8 @@ pub trait ParserDomMutationConsumer {
fn mark_script_already_started_for_parser(&mut self, node_id: NativeNodeId);
fn finish_parsing_script_children(&mut self, node_id: NativeNodeId);
fn finish_parsing_link_children(&mut self, node_id: NativeNodeId);
/// Finish constructing a style element's source and process the resulting
/// stylesheet lifecycle effects. This does not mutate its child nodes.
fn finish_parsing_style_children(&mut self, node_id: NativeNodeId);
/// Complete the session-owned element and deliver its DOM lifecycle effects.
fn finish_parsing_children(&mut self, construction: &ParserConstruction, node_id: NativeNodeId);
fn attach_declarative_shadow_for_parser(
&mut self,
@@ -649,8 +646,14 @@ struct ParserDomMutationSink {
apply: unsafe fn(NonNull<()>, ParserDomMutation),
create_element_for_document_without_attributes:
unsafe fn(NonNull<()>, NativeNodeId, String, String, Option<String>) -> NativeNodeId,
create_parser_element_for_document_without_attributes:
unsafe fn(NonNull<()>, NativeNodeId, String, String, Option<String>) -> NativeNodeId,
create_parser_element_for_document_without_attributes: unsafe fn(
NonNull<()>,
&ParserConstruction,
NativeNodeId,
String,
String,
Option<String>,
) -> NativeNodeId,
add_attrs_if_missing_for_parser: unsafe fn(NonNull<()>, NativeNodeId, Vec<NativeAttribute>),
create_text_node: unsafe fn(NonNull<()>, NativeNodeId, String) -> NativeNodeId,
create_comment: unsafe fn(NonNull<()>, NativeNodeId, String) -> NativeNodeId,
@@ -664,9 +667,7 @@ struct ParserDomMutationSink {
push_parse_error: unsafe fn(NonNull<()>, String),
set_html_quirks_mode_for_parser: unsafe fn(NonNull<()>, QuirksMode),
mark_script_already_started_for_parser: unsafe fn(NonNull<()>, NativeNodeId),
finish_parsing_script_children: unsafe fn(NonNull<()>, NativeNodeId),
finish_parsing_link_children: unsafe fn(NonNull<()>, NativeNodeId),
finish_parsing_style_children: unsafe fn(NonNull<()>, NativeNodeId),
finish_parsing_children: unsafe fn(NonNull<()>, &ParserConstruction, NativeNodeId),
attach_declarative_shadow_for_parser:
unsafe fn(NonNull<()>, NativeNodeId, NativeNodeId, Vec<NativeAttribute>) -> bool,
associate_parser_form_owner: unsafe fn(NonNull<()>, NativeNodeId, NativeNodeId) -> bool,
@@ -704,6 +705,7 @@ impl ParserDomMutationSink {
T: ParserDomMutationConsumer,
>(
data: NonNull<()>,
construction: &ParserConstruction,
document_handle: NativeNodeId,
local_name: String,
namespace: String,
@@ -713,6 +715,7 @@ impl ParserDomMutationSink {
// pointed-to consumer to remain live and exclusive for the pump step.
unsafe { data.cast::<T>().as_mut() }
.create_parser_element_for_document_without_attributes(
construction,
document_handle,
local_name,
namespace,
@@ -827,29 +830,14 @@ impl ParserDomMutationSink {
// pointed-to consumer to remain live and exclusive for the pump step.
unsafe { data.cast::<T>().as_mut() }.mark_script_already_started_for_parser(node_id);
}
unsafe fn finish_parsing_script_children_impl<T: ParserDomMutationConsumer>(
unsafe fn finish_parsing_children_impl<T: ParserDomMutationConsumer>(
data: NonNull<()>,
construction: &ParserConstruction,
node_id: NativeNodeId,
) {
// SAFETY: ParserDomMutationSink::from_consumer_unchecked requires the
// pointed-to consumer to remain live and exclusive for the pump step.
unsafe { data.cast::<T>().as_mut() }.finish_parsing_script_children(node_id);
}
unsafe fn finish_parsing_link_children_impl<T: ParserDomMutationConsumer>(
data: NonNull<()>,
node_id: NativeNodeId,
) {
// SAFETY: ParserDomMutationSink::from_consumer_unchecked requires the
// pointed-to consumer to remain live and exclusive for the pump step.
unsafe { data.cast::<T>().as_mut() }.finish_parsing_link_children(node_id);
}
unsafe fn finish_parsing_style_children_impl<T: ParserDomMutationConsumer>(
data: NonNull<()>,
node_id: NativeNodeId,
) {
// SAFETY: ParserDomMutationSink::from_consumer_unchecked requires the
// pointed-to consumer to remain live and exclusive for the pump step.
unsafe { data.cast::<T>().as_mut() }.finish_parsing_style_children(node_id);
unsafe { data.cast::<T>().as_mut() }.finish_parsing_children(construction, node_id);
}
unsafe fn attach_declarative_shadow_for_parser_impl<T: ParserDomMutationConsumer>(
data: NonNull<()>,
@@ -893,9 +881,7 @@ impl ParserDomMutationSink {
push_parse_error: push_parse_error_impl::<T>,
set_html_quirks_mode_for_parser: set_html_quirks_mode_for_parser_impl::<T>,
mark_script_already_started_for_parser: mark_script_already_started_for_parser_impl::<T>,
finish_parsing_script_children: finish_parsing_script_children_impl::<T>,
finish_parsing_link_children: finish_parsing_link_children_impl::<T>,
finish_parsing_style_children: finish_parsing_style_children_impl::<T>,
finish_parsing_children: finish_parsing_children_impl::<T>,
attach_declarative_shadow_for_parser: attach_declarative_shadow_for_parser_impl::<T>,
associate_parser_form_owner: associate_parser_form_owner_impl::<T>,
}
@@ -929,6 +915,7 @@ impl ParserDomMutationSink {
fn create_parser_element_for_document_without_attributes(
self,
construction: &ParserConstruction,
document_handle: NativeNodeId,
local_name: String,
namespace: String,
@@ -939,6 +926,7 @@ impl ParserDomMutationSink {
unsafe {
(self.create_parser_element_for_document_without_attributes)(
self.data,
construction,
document_handle,
local_name,
namespace,
@@ -1026,22 +1014,9 @@ impl ParserDomMutationSink {
unsafe { (self.mark_script_already_started_for_parser)(self.data, node_id) };
}
fn finish_parsing_script_children(self, node_id: NativeNodeId) {
// SAFETY: construction ties the raw pointer and callback to the same
// consumer remains live for the current runtime-DOM sink step.
unsafe { (self.finish_parsing_script_children)(self.data, node_id) };
}
fn finish_parsing_link_children(self, node_id: NativeNodeId) {
// SAFETY: construction ties the raw pointer and callback to the same
// consumer remains live for the current runtime-DOM sink step.
unsafe { (self.finish_parsing_link_children)(self.data, node_id) };
}
fn finish_parsing_style_children(self, node_id: NativeNodeId) {
// SAFETY: construction ties the raw pointer and callback to the same
// consumer remains live for the current runtime-DOM sink step.
unsafe { (self.finish_parsing_style_children)(self.data, node_id) };
fn finish_parsing_children(self, construction: &ParserConstruction, node_id: NativeNodeId) {
// SAFETY: the consumer remains live and exclusive for this parser step.
unsafe { (self.finish_parsing_children)(self.data, construction, node_id) };
}
fn attach_declarative_shadow_for_parser(
@@ -1065,6 +1040,7 @@ impl ParserDomMutationSink {
}
pub struct ParserElementCreationRequest<'a> {
pub construction: &'a ParserConstruction,
pub document_handle: NativeNodeId,
pub intended_parent: Option<NativeNodeId>,
pub local_name: &'a str,
@@ -1485,14 +1461,15 @@ impl ParserDomMutationConsumer for TestMutationEffectCollector<'_> {
fn create_parser_element_for_document_without_attributes(
&mut self,
construction: &ParserConstruction,
document_handle: NativeNodeId,
local_name: String,
namespace: String,
prefix: Option<String>,
) -> NativeNodeId {
// SAFETY: tests keep the borrowed DomHost pointer alive and route the
// parser pump through this collector for the duration of the step.
unsafe { &mut *self.host }.create_parser_element_without_attributes_for_document(
// SAFETY: tests keep the DomHost alive and exclusive for this parser step.
construction.create_element(
unsafe { &mut *self.host },
document_handle,
local_name,
namespace,
@@ -1602,22 +1579,14 @@ impl ParserDomMutationConsumer for TestMutationEffectCollector<'_> {
let _ = unsafe { &mut *self.host }.set_script_already_started(node_id, true);
}
fn finish_parsing_script_children(&mut self, node_id: NativeNodeId) {
fn finish_parsing_children(
&mut self,
construction: &ParserConstruction,
node_id: NativeNodeId,
) {
// SAFETY: tests keep the borrowed DomHost pointer alive and route the
// parser pump through this collector for the duration of the step.
let _ = unsafe { &mut *self.host }.finish_parsing_script_children(node_id);
}
fn finish_parsing_link_children(&mut self, node_id: NativeNodeId) {
// SAFETY: tests keep the borrowed DomHost pointer alive and route the
// parser pump through this collector for the duration of the step.
let _ = unsafe { &mut *self.host }.finish_parsing_link_children(node_id);
}
fn finish_parsing_style_children(&mut self, node_id: NativeNodeId) {
// SAFETY: tests keep the borrowed DomHost pointer alive and route the
// parser pump through this collector for the duration of the step.
let effects = unsafe { &mut *self.host }.finish_parsing_style_children_effects(node_id);
let effects = construction.finish_children(unsafe { &mut *self.host }, node_id);
self.consume_parser_mutation_effects(effects);
}
@@ -1867,14 +1836,15 @@ impl ParserDomMutationConsumer for TestReadTrackingCollector<'_> {
fn create_parser_element_for_document_without_attributes(
&mut self,
construction: &ParserConstruction,
document_handle: NativeNodeId,
local_name: String,
namespace: String,
prefix: Option<String>,
) -> NativeNodeId {
// SAFETY: tests keep the borrowed DomHost pointer alive and route the
// parser pump through this collector for the duration of the step.
unsafe { &mut *self.host }.create_parser_element_without_attributes_for_document(
// SAFETY: tests keep the DomHost alive and exclusive for this parser step.
construction.create_element(
unsafe { &mut *self.host },
document_handle,
local_name,
namespace,
@@ -1984,22 +1954,14 @@ impl ParserDomMutationConsumer for TestReadTrackingCollector<'_> {
let _ = unsafe { &mut *self.host }.set_script_already_started(node_id, true);
}
fn finish_parsing_script_children(&mut self, node_id: NativeNodeId) {
fn finish_parsing_children(
&mut self,
construction: &ParserConstruction,
node_id: NativeNodeId,
) {
// SAFETY: tests keep the borrowed DomHost pointer alive and route the
// parser pump through this collector for the duration of the step.
let _ = unsafe { &mut *self.host }.finish_parsing_script_children(node_id);
}
fn finish_parsing_link_children(&mut self, node_id: NativeNodeId) {
// SAFETY: tests keep the borrowed DomHost pointer alive and route the
// parser pump through this collector for the duration of the step.
let _ = unsafe { &mut *self.host }.finish_parsing_link_children(node_id);
}
fn finish_parsing_style_children(&mut self, node_id: NativeNodeId) {
// SAFETY: tests keep the borrowed DomHost pointer alive and route the
// parser pump through this collector for the duration of the step.
let effects = unsafe { &mut *self.host }.finish_parsing_style_children_effects(node_id);
let effects = construction.finish_children(unsafe { &mut *self.host }, node_id);
self.consume_parser_mutation_effects(effects);
}
@@ -2060,7 +2022,8 @@ impl ParserElementCreationConsumer for TestElementCreationCollector {
// SAFETY: the test keeps the borrowed DomHost pointer alive and
// exclusively routes this parser pump through the test sink.
let host = unsafe { &mut *self.dom_host };
let handle = host.create_parser_element_without_attributes_for_document(
let handle = request.construction.create_element(
host,
request.document_handle,
request.local_name.to_owned(),
request.namespace.to_owned(),
@@ -2097,6 +2060,7 @@ pub(super) struct ParserStreamHtmlTreeSinkTarget {
open_template_element_depth: usize,
pending_open_parser_element: Option<NativeNodeId>,
open_parser_elements: Vec<OpenParserElement>,
construction: ParserConstruction,
allow_declarative_shadow_roots: bool,
pending_null_custom_element_registry_elements: Vec<NativeNodeId>,
state: HtmlTreeSinkState,
@@ -2148,6 +2112,7 @@ impl ParserStreamHtmlTreeSinkTarget {
open_template_element_depth: 0,
pending_open_parser_element: None,
open_parser_elements: Vec::new(),
construction: ParserConstruction::default(),
allow_declarative_shadow_roots,
pending_null_custom_element_registry_elements: Vec::new(),
state: HtmlTreeSinkState::default(),
@@ -2168,6 +2133,7 @@ impl ParserStreamHtmlTreeSinkTarget {
open_template_element_depth: 0,
pending_open_parser_element: None,
open_parser_elements: Vec::new(),
construction: ParserConstruction::default(),
allow_declarative_shadow_roots: false,
pending_null_custom_element_registry_elements: Vec::new(),
state: HtmlTreeSinkState::default(),
@@ -2205,6 +2171,7 @@ impl ParserStreamHtmlTreeSinkTarget {
open_template_element_depth: 0,
pending_open_parser_element: None,
open_parser_elements: Vec::new(),
construction: ParserConstruction::default(),
allow_declarative_shadow_roots,
pending_null_custom_element_registry_elements: Vec::new(),
state: HtmlTreeSinkState::default(),
@@ -2229,6 +2196,7 @@ impl ParserStreamHtmlTreeSinkTarget {
open_template_element_depth: 0,
pending_open_parser_element: None,
open_parser_elements: Vec::new(),
construction: ParserConstruction::default(),
allow_declarative_shadow_roots,
pending_null_custom_element_registry_elements: Vec::new(),
state: HtmlTreeSinkState::default(),
@@ -2669,27 +2637,27 @@ impl ParserStreamHtmlTreeSinkTarget {
}
fn note_parser_element_popped(&mut self, node_id: NativeNodeId) {
if let Some(index) = self
let element = self
.open_parser_elements
.iter()
.rposition(|candidate| candidate.node_id == node_id)
{
let element = self.open_parser_elements.remove(index);
if element.name.local.as_ref() == "style"
.map(|index| self.open_parser_elements.remove(index));
if !self.construction.is_pending(node_id) {
return;
}
if self.read_is_html_element_named(node_id, "link") {
// Capture parser-inserted blocking state before completion consumes it.
let _ = self.capture_parser_blocking_stylesheet(node_id);
}
self.finish_parsing_children(node_id);
if element.is_some_and(|element| {
element.name.local.as_ref() == "style"
&& matches!(
element.name.ns.as_ref(),
"http://www.w3.org/1999/xhtml" | "http://www.w3.org/2000/svg"
)
{
// Like Blink's StyleElement::FinishParsingChildren, process
// the complete source, not every growing tokenizer prefix.
self.finish_parsing_style_children(node_id);
self.note_blocking_stylesheet_pause_if_needed(node_id);
}
}
if self.read_is_html_element_named(node_id, "link") {
let _ = self.capture_parser_blocking_stylesheet(node_id);
self.finish_parsing_link_children_for_dom_host(node_id);
}) {
self.note_blocking_stylesheet_pause_if_needed(node_id);
}
}
@@ -2756,19 +2724,20 @@ impl ParserStreamHtmlTreeSinkTarget {
owner
.dom_mutation_sink()
.create_parser_element_for_document_without_attributes(
&self.construction,
document_handle,
local_name,
namespace,
prefix,
)
} else {
self.dom_host_mut()
.create_parser_element_without_attributes_for_document(
document_handle,
local_name,
namespace,
prefix,
)
self.construction.create_element(
self.owned_dom_host.as_mut().expect("owned parser DOM"),
document_handle,
local_name,
namespace,
prefix,
)
}
}
@@ -2947,37 +2916,21 @@ impl ParserStreamHtmlTreeSinkTarget {
}
}
fn finish_parsing_script_children_for_dom_host(&mut self, node_id: NativeNodeId) {
fn finish_parsing_children(&mut self, node_id: NativeNodeId) {
if let Some(owner) = &self.runtime_dom_sinks {
owner
.dom_mutation_sink()
.finish_parsing_script_children(node_id);
.finish_parsing_children(&self.construction, node_id);
} else {
let _ = self.dom_host_mut().finish_parsing_script_children(node_id);
let _ = self.construction.finish_children(
self.owned_dom_host.as_mut().expect("owned parser DOM"),
node_id,
);
}
}
fn finish_parsing_link_children_for_dom_host(&mut self, node_id: NativeNodeId) {
if let Some(owner) = &self.runtime_dom_sinks {
owner
.dom_mutation_sink()
.finish_parsing_link_children(node_id);
} else {
let _ = self.dom_host_mut().finish_parsing_link_children(node_id);
}
}
/// Complete a parser-created style after the tree builder closes it.
fn finish_parsing_style_children(&mut self, node_id: NativeNodeId) {
if let Some(owner) = &self.runtime_dom_sinks {
owner
.dom_mutation_sink()
.finish_parsing_style_children(node_id);
} else {
let _ = self
.dom_host_mut()
.finish_parsing_style_children_effects(node_id);
}
pub(super) fn finish_construction(&self) {
self.construction.finish();
}
fn attach_declarative_shadow_for_dom_host(
@@ -3285,6 +3238,7 @@ impl ParserStreamHtmlTreeSinkTarget {
.and_then(ParserRuntimeDomSinks::element_creation_sink)
{
let request = ParserElementCreationRequest {
construction: &self.construction,
document_handle,
intended_parent,
local_name: &local_name,
@@ -3584,11 +3538,11 @@ impl ParserStreamHtmlTreeSinkTarget {
}
pub(super) fn note_node_closed(&mut self, node_id: NativeNodeId, is_script_element: bool) {
if is_script_element {
self.finish_parsing_script_children_for_dom_host(node_id);
let ready_script = is_script_element && self.construction.is_pending(node_id);
self.note_node_popped_without_script_handoff(node_id);
if ready_script {
self.state.ready_parser_scripts.push_back(node_id);
}
self.note_node_popped_without_script_handoff(node_id);
}
fn note_node_popped_without_script_handoff(&mut self, node_id: NativeNodeId) {
+1
View File
@@ -340,6 +340,7 @@ impl HtmlParserSession {
tokenizer.sink.sink().begin_tree_builder_finish();
tokenizer.end();
let sink = tokenizer.sink.sink();
sink.finish_construction();
ParserFinishDiscoverySignals {
parser_created_null_registry_elements: sink
.take_parser_stream_null_custom_element_registry_elements(),
+9 -13
View File
@@ -1045,13 +1045,15 @@ mod tests {
fn create_parser_element_for_document_without_attributes(
&mut self,
construction: &moli_dom::native::ParserConstruction,
document_handle: NativeNodeId,
local_name: String,
namespace: String,
prefix: Option<String>,
) -> NativeNodeId {
// SAFETY: the test keeps the DomHost alive for this parser pump step.
unsafe { &mut *self.host }.create_parser_element_without_attributes_for_document(
construction.create_element(
unsafe { &mut *self.host },
document_handle,
local_name,
namespace,
@@ -1169,19 +1171,13 @@ mod tests {
let _ = unsafe { &mut *self.host }.set_script_already_started(node_id, true);
}
fn finish_parsing_script_children(&mut self, node_id: NativeNodeId) {
fn finish_parsing_children(
&mut self,
construction: &moli_dom::native::ParserConstruction,
node_id: NativeNodeId,
) {
// SAFETY: the test keeps the DomHost alive for this parser pump step.
let _ = unsafe { &mut *self.host }.finish_parsing_script_children(node_id);
}
fn finish_parsing_link_children(&mut self, node_id: NativeNodeId) {
// SAFETY: the test keeps the DomHost alive for this parser pump step.
let _ = unsafe { &mut *self.host }.finish_parsing_link_children(node_id);
}
fn finish_parsing_style_children(&mut self, node_id: NativeNodeId) {
// SAFETY: the test keeps the DomHost alive for this parser pump step.
let effects = unsafe { &mut *self.host }.finish_parsing_style_children_effects(node_id);
let effects = construction.finish_children(unsafe { &mut *self.host }, node_id);
self.effects.merge(effects);
}
+20 -17
View File
@@ -15,7 +15,9 @@ use xml5ever::{
};
use super::{html_chunks, xml_tree_viewer::transform_document_to_xml_tree_view};
use moli_dom::native::{Attribute as NativeAttribute, DomHost, NativeDom, NativeNodeId, Node};
use moli_dom::native::{
Attribute as NativeAttribute, DomHost, NativeDom, NativeNodeId, Node, ParserConstruction,
};
#[derive(Debug, Clone, Default)]
pub struct XmlParser;
@@ -57,6 +59,7 @@ impl DerefMut for XmlDomHost<'_> {
}
struct XmlLiveTreeSinkTarget<'a> {
construction: ParserConstruction,
dom_host: XmlDomHost<'a>,
document_handle: NativeNodeId,
}
@@ -145,6 +148,7 @@ impl XmlLiveTreeSinkTarget<'static> {
let dom_host = DomHost::from_dom(NativeDom::new_xml(final_url));
let document_handle = dom_host.document_handle();
Self {
construction: ParserConstruction::default(),
dom_host: XmlDomHost::Owned(Box::new(dom_host)),
document_handle,
}
@@ -169,6 +173,7 @@ impl<'a> XmlLiveTreeSinkTarget<'a> {
return None;
}
Some(Self {
construction: ParserConstruction::default(),
dom_host: XmlDomHost::Borrowed(dom_host),
document_handle,
})
@@ -197,14 +202,13 @@ impl<'a> XmlLiveTreeSinkTarget<'a> {
)
})
.collect::<Vec<_>>();
let node_id = self
.dom_host
.create_parser_element_without_attributes_for_document(
self.document_handle,
name.local.to_string(),
name.ns.to_string(),
name.prefix.as_ref().map(|prefix| prefix.to_string()),
);
let node_id = self.construction.create_element(
&mut self.dom_host,
self.document_handle,
name.local.to_string(),
name.ns.to_string(),
name.prefix.as_ref().map(|prefix| prefix.to_string()),
);
self.dom_host
.add_attrs_if_missing_for_parser(node_id, attributes);
XmlParseHandle::new(node_id, Some(element_name))
@@ -467,7 +471,9 @@ impl<'host> XmlTreeSinkBase for XmlDocumentSink<'host> {
Self: 'a;
fn finish(self) -> Self::Output {
self.target.into_inner()
let target = self.target.into_inner();
target.construction.finish();
target
}
fn parse_error(&self, err: Cow<'static, str>) {
@@ -556,13 +562,10 @@ impl<'host> XmlTreeSinkBase for XmlDocumentSink<'host> {
}
fn pop(&self, node: &Self::Handle) {
// This inert sink has no renderer consumer. Complete parser state here
// so a later document handoff can prepare the finished stylesheet.
let _ = self
.target
.borrow_mut()
.dom_host
.finish_parsing_style_children_effects(node.node_id);
let target = &mut *self.target.borrow_mut();
let _ = target
.construction
.finish_children(&mut target.dom_host, node.node_id);
}
fn set_quirks_mode(&self, mode: XmlQuirksMode) {
+4 -1
View File
@@ -491,6 +491,7 @@ impl XmlParserSession {
debug_assert!(input_buffer.is_empty());
tokenizer.end();
let mut target = tokenizer.sink.inner.sink.target.borrow_mut();
target.common.finish_construction();
target.present_unstyled_top_level_document_if_needed();
ParserFinishDiscoverySignals {
parser_created_null_registry_elements: target
@@ -588,7 +589,9 @@ impl TreeSink for XmlStreamDocumentSink {
Self: 'a;
fn finish(self) -> Self::Output {
self.target.into_inner()
let target = self.target.into_inner();
target.common.finish_construction();
target
}
fn parse_error(&self, error: Cow<'static, str>) {
@@ -203,6 +203,7 @@ impl ParserDomMutationConsumer for DocumentWriteParserMutationOwner<'_, '_, '_>
fn create_parser_element_for_document_without_attributes(
&mut self,
construction: &moli_dom::native::ParserConstruction,
document_handle: DomHandle,
local_name: String,
namespace: String,
@@ -210,6 +211,7 @@ impl ParserDomMutationConsumer for DocumentWriteParserMutationOwner<'_, '_, '_>
) -> DomHandle {
self.runtime
.create_parser_element_for_document_without_attributes_in_live_dom_host(
construction,
document_handle,
local_name,
namespace,
@@ -299,25 +301,12 @@ impl ParserDomMutationConsumer for DocumentWriteParserMutationOwner<'_, '_, '_>
.mark_script_already_started_for_parser_in_live_dom_host(node_id);
}
fn finish_parsing_script_children(&mut self, node_id: DomHandle) {
let _ = self
.runtime
.dom_host_mut()
.finish_parsing_script_children(node_id);
}
fn finish_parsing_link_children(&mut self, node_id: DomHandle) {
let _ = self
.runtime
.dom_host_mut()
.finish_parsing_link_children(node_id);
}
fn finish_parsing_style_children(&mut self, node_id: DomHandle) {
let effects = self
.runtime
.dom_host_mut()
.finish_parsing_style_children_effects(node_id);
fn finish_parsing_children(
&mut self,
construction: &moli_dom::native::ParserConstruction,
node_id: DomHandle,
) {
let effects = construction.finish_children(self.runtime.dom_host_mut(), node_id);
self.consume_parser_mutation_effects(effects);
}
@@ -358,6 +347,7 @@ impl ParserElementCreationConsumer for DocumentWriteParserMutationOwner<'_, '_,
request.intended_parent,
|document_handle, local_name, namespace, prefix| {
runtime.create_parser_element_for_document_without_attributes_in_live_dom_host(
request.construction,
document_handle,
local_name,
namespace,
@@ -286,18 +286,19 @@ impl DocumentRuntime {
pub(crate) fn create_parser_element_for_document_without_attributes_in_live_dom_host(
&mut self,
construction: &moli_dom::native::ParserConstruction,
document_handle: DomHandle,
local_name: String,
namespace: String,
prefix: Option<String>,
) -> DomHandle {
self.dom_host_mut_for_active_parser_step()
.create_parser_element_without_attributes_for_document(
document_handle,
local_name,
namespace,
prefix,
)
construction.create_element(
self.dom_host_mut_for_active_parser_step(),
document_handle,
local_name,
namespace,
prefix,
)
}
pub(crate) fn add_attrs_if_missing_for_parser_in_live_dom_host(
+4 -4
View File
@@ -234,13 +234,13 @@ fn materialize_xml_parser_error_document(parsed: NativeDom) -> NativeDom {
for child in host.child_handles(document).collect::<Vec<_>>() {
let _ = host.remove_child(document, child);
}
let html = host.create_parser_element_without_attributes_for_document(
let html = host.create_element_without_attributes_for_document(
document,
"html".to_owned(),
HTML_NAMESPACE.to_owned(),
None,
);
let body = host.create_parser_element_without_attributes_for_document(
let body = host.create_element_without_attributes_for_document(
document,
"body".to_owned(),
HTML_NAMESPACE.to_owned(),
@@ -257,7 +257,7 @@ fn create_dom_parser_error_element(
document: NativeNodeId,
error_detail: &str,
) -> NativeNodeId {
let parser_error = host.create_parser_element_without_attributes_for_document(
let parser_error = host.create_element_without_attributes_for_document(
document,
"parsererror".to_owned(),
HTML_NAMESPACE.to_owned(),
@@ -294,7 +294,7 @@ fn create_dom_parser_error_child(
local_name: &str,
style: Option<&str>,
) -> NativeNodeId {
let element = host.create_parser_element_without_attributes_for_document(
let element = host.create_element_without_attributes_for_document(
document,
local_name.to_owned(),
HTML_NAMESPACE.to_owned(),
@@ -186,7 +186,7 @@ impl JsContextHost {
.create_document_type_for_document(document_handle, "html", "", "");
let html = self
.dom_host_mut()
.create_parser_element_without_attributes_for_document(
.create_element_without_attributes_for_document(
document_handle,
"html".to_owned(),
"http://www.w3.org/1999/xhtml".to_owned(),
@@ -194,7 +194,7 @@ impl JsContextHost {
);
let head = self
.dom_host_mut()
.create_parser_element_without_attributes_for_document(
.create_element_without_attributes_for_document(
document_handle,
"head".to_owned(),
"http://www.w3.org/1999/xhtml".to_owned(),
@@ -202,7 +202,7 @@ impl JsContextHost {
);
let body = self
.dom_host_mut()
.create_parser_element_without_attributes_for_document(
.create_element_without_attributes_for_document(
document_handle,
"body".to_owned(),
"http://www.w3.org/1999/xhtml".to_owned(),
@@ -361,19 +361,19 @@ impl ParserDomMutationConsumer for ChildFrameLiveParserOwner<'_, '_, '_> {
fn create_parser_element_for_document_without_attributes(
&mut self,
construction: &moli_dom::native::ParserConstruction,
document_handle: DomHandle,
local_name: String,
namespace: String,
prefix: Option<String>,
) -> DomHandle {
self.host
.dom_host_mut()
.create_parser_element_without_attributes_for_document(
document_handle,
local_name,
namespace,
prefix,
)
construction.create_element(
self.host.dom_host_mut(),
document_handle,
local_name,
namespace,
prefix,
)
}
fn add_attrs_if_missing_for_parser(&mut self, node_id: DomHandle, attrs: Vec<Attribute>) {
@@ -466,28 +466,15 @@ impl ParserDomMutationConsumer for ChildFrameLiveParserOwner<'_, '_, '_> {
.set_script_already_started(node_id, true);
}
fn finish_parsing_script_children(&mut self, node_id: DomHandle) {
let _ = self
.host
.dom_host_mut()
.finish_parsing_script_children(node_id);
}
fn finish_parsing_link_children(&mut self, node_id: DomHandle) {
let _ = self
.host
.dom_host_mut()
.finish_parsing_link_children(node_id);
}
fn finish_parsing_style_children(&mut self, node_id: DomHandle) {
fn finish_parsing_children(
&mut self,
construction: &moli_dom::native::ParserConstruction,
node_id: DomHandle,
) {
if !self.targets_current_document() {
return;
}
let effects = self
.host
.dom_host_mut()
.finish_parsing_style_children_effects(node_id);
let effects = construction.finish_children(self.host.dom_host_mut(), node_id);
self.consume_parser_mutation_effects(effects);
}
@@ -541,14 +528,13 @@ impl ParserElementCreationConsumer for ChildFrameLiveParserOwner<'_, '_, '_> {
request.attributes,
request.intended_parent,
|document_handle, local_name, namespace, prefix| {
self.host
.dom_host_mut()
.create_parser_element_without_attributes_for_document(
document_handle,
local_name,
namespace,
prefix,
)
request.construction.create_element(
self.host.dom_host_mut(),
document_handle,
local_name,
namespace,
prefix,
)
},
)
}
@@ -221,6 +221,7 @@ impl ParserDomMutationConsumer for PhaseOneParserOwner<'_> {
fn create_parser_element_for_document_without_attributes(
&mut self,
construction: &moli_dom::native::ParserConstruction,
document_handle: NativeNodeId,
local_name: String,
namespace: String,
@@ -229,6 +230,7 @@ impl ParserDomMutationConsumer for PhaseOneParserOwner<'_> {
self.vm
.document_runtime
.create_parser_element_for_document_without_attributes_in_live_dom_host(
construction,
document_handle,
local_name,
namespace,
@@ -334,28 +336,13 @@ impl ParserDomMutationConsumer for PhaseOneParserOwner<'_> {
.mark_script_already_started_for_parser_in_live_dom_host(node_id);
}
fn finish_parsing_script_children(&mut self, node_id: NativeNodeId) {
let _ = self
.vm
.document_runtime
.dom_host_mut()
.finish_parsing_script_children(node_id);
}
fn finish_parsing_link_children(&mut self, node_id: NativeNodeId) {
let _ = self
.vm
.document_runtime
.dom_host_mut()
.finish_parsing_link_children(node_id);
}
fn finish_parsing_style_children(&mut self, node_id: NativeNodeId) {
let effects = self
.vm
.document_runtime
.dom_host_mut()
.finish_parsing_style_children_effects(node_id);
fn finish_parsing_children(
&mut self,
construction: &moli_dom::native::ParserConstruction,
node_id: NativeNodeId,
) {
let effects =
construction.finish_children(self.vm.document_runtime.dom_host_mut(), node_id);
self.consume_parser_mutation_effects(effects);
}
@@ -387,6 +374,7 @@ impl ParserElementCreationConsumer for PhaseOneParserOwner<'_> {
.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,
+2
View File
@@ -4692,6 +4692,7 @@ impl ScriptVm {
// parser step.
pub(crate) fn create_and_construct_parser_custom_element_direct_in_default_context(
&mut self,
construction: &moli_dom::native::ParserConstruction,
document_handle: DomHandle,
document_has_body: bool,
local_name: &str,
@@ -4726,6 +4727,7 @@ impl ScriptVm {
intended_parent,
|document_handle, local_name, namespace, prefix| {
document_runtime.create_parser_element_for_document_without_attributes_in_live_dom_host(
construction,
document_handle,
local_name,
namespace,
@@ -394,7 +394,7 @@ fn trusted_types_default_policy_can_make_changed_empty_script_sources_executable
assert!(
vm.document_runtime
.dom_host_mut()
.finish_parsing_script_children(script)
.set_script_text_internal_slot(script, ";")
);
assert!(
vm.document_runtime
@@ -2467,6 +2467,50 @@ fn parser_style_waits_for_complete_source_in_main_and_child_documents() {
);
}
#[test]
fn parser_style_completion_follows_the_creating_parser_and_preserves_abort() {
let mut vm = new_parsed_test_vm(
"https://parser-style-ownership.test/",
"<!doctype html><body><div id=probe>probe</div></body>",
);
let result = vm.eval(r#"
(() => {
const frame = document.body.appendChild(document.createElement('iframe'));
const d = frame.contentDocument;
d.open();
d.write('<!doctype html><head><style>#probe { color: rgb(1, 2, 3) }');
const moved = d.querySelector('style');
document.head.appendChild(moved);
const pending = moved.sheet === null;
d.write('</style>');
d.close();
const complete = [!!moved.sheet, getComputedStyle(document.getElementById('probe')).color];
moved.remove();
d.open();
d.write('<!doctype html><head><style>#probe { color: red }');
const aborted = d.querySelector('style');
d.open();
d.write('<!doctype html><body>replacement</body>');
d.close();
document.head.appendChild(aborted);
aborted.textContent = '#probe { color: blue }';
const afterAbort = [aborted.sheet === null, getComputedStyle(document.getElementById('probe')).color];
aborted.remove();
frame.remove();
return JSON.stringify({pending, complete, afterAbort});
})()
"#).expect("moving a style preserves parser ownership, while cancellation cannot complete it");
assert_eq!(
serde_json::from_str::<serde_json::Value>(&result).unwrap(),
serde_json::json!({
"pending": true,
"complete": [true, "rgb(1, 2, 3)"],
"afterAbort": [true, "rgb(0, 0, 0)"]
}),
);
}
#[test]
fn parser_style_finishes_at_eof_in_main_and_child_documents() {
let mut vm = new_parsed_test_vm(
@@ -4,7 +4,10 @@ use super::*;
fn style_element_initialization_waits_for_parser_and_preserves_prepared_sources() {
let mut host = test_host();
let document = host.document_handle();
let owner = host.create_parser_element_without_attributes(
let construction = moli_dom::native::ParserConstruction::default();
let owner = construction.create_element(
&mut host,
document,
"style".into(),
"http://www.w3.org/1999/xhtml".into(),
None,
@@ -16,7 +19,8 @@ fn style_element_initialization_waits_for_parser_and_preserves_prepared_sources(
engine.initialize_style_element_sources_with_host(&host, document);
assert!(engine.owner_style_sheet_processing_source(owner).is_none());
let completion = host.finish_parsing_style_children_effects(owner);
let completion = construction.finish_children(&mut host, owner);
construction.finish();
engine
.apply_stylesheet_owner_changes_with_host(&host, completion.stylesheet_owners().changes());
let first = engine.owner_style_sheet_processing_source(owner).unwrap();