diff --git a/moli-dom/src/native/element/control_state.rs b/moli-dom/src/native/element/control_state.rs index 02bcb64ca0..c5d720f442 100644 --- a/moli-dom/src/native/element/control_state.rs +++ b/moli-dom/src/native/element/control_state.rs @@ -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; } diff --git a/moli-dom/src/native/element/mod.rs b/moli-dom/src/native/element/mod.rs index feb2b416f3..e82d2b6665 100644 --- a/moli-dom/src/native/element/mod.rs +++ b/moli-dom/src/native/element/mod.rs @@ -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; diff --git a/moli-dom/src/native/element/tests.rs b/moli-dom/src/native/element/tests.rs index 079aa67c6e..6cedfa0f84 100644 --- a/moli-dom/src/native/element/tests.rs +++ b/moli-dom/src/native/element/tests.rs @@ -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()); diff --git a/moli-dom/src/native/host/document.rs b/moli-dom/src/native/host/document.rs index ea6f1c9d76..7d8f7689b1 100644 --- a/moli-dom/src/native/host/document.rs +++ b/moli-dom/src/native/host/document.rs @@ -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) } diff --git a/moli-dom/src/native/host/mutation/state.rs b/moli-dom/src/native/host/mutation/state.rs index 0e0a5a69f2..4aca93b3c5 100644 --- a/moli-dom/src/native/host/mutation/state.rs +++ b/moli-dom/src/native/host/mutation/state.rs @@ -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) -> bool { let did_change = { let Some(element) = self diff --git a/moli-dom/src/native/host/parser.rs b/moli-dom/src/native/host/parser.rs index 5802f548df..92560a7116 100644 --- a/moli-dom/src/native/host/parser.rs +++ b/moli-dom/src/native/host/parser.rs @@ -25,18 +25,8 @@ impl DomHost { } } - pub fn create_parser_element( - &mut self, - local_name: String, - namespace: String, - prefix: Option, - attributes: Vec, - ) -> 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 { self.node(node_id) .and_then(Node::as_element) diff --git a/moli-dom/src/native/mod.rs b/moli-dom/src/native/mod.rs index d7322a3c38..64b68acf6c 100644 --- a/moli-dom/src/native/mod.rs +++ b/moli-dom/src/native/mod.rs @@ -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)); diff --git a/moli-dom/src/native/parser_construction.rs b/moli-dom/src/native/parser_construction.rs new file mode 100644 index 0000000000..d8df1b437c --- /dev/null +++ b/moli-dom/src/native/parser_construction.rs @@ -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>, + state: Cell, +} + +impl ParserConstruction { + pub fn create_element( + &self, + host: &mut DomHost, + document: NativeNodeId, + local_name: String, + namespace: String, + prefix: Option, + ) -> 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)); + } + } +} diff --git a/moli-parser/src/html.rs b/moli-parser/src/html.rs index 2eb2c8dc8c..11c8a72f50 100644 --- a/moli-parser/src/html.rs +++ b/moli-parser/src/html.rs @@ -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>) { diff --git a/moli-parser/src/live_target.rs b/moli-parser/src/live_target.rs index 08f64f80c7..75571105b1 100644 --- a/moli-parser/src/live_target.rs +++ b/moli-parser/src/live_target.rs @@ -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) -> NativeNodeId, - create_parser_element_for_document_without_attributes: - unsafe fn(NonNull<()>, NativeNodeId, String, String, Option) -> NativeNodeId, + create_parser_element_for_document_without_attributes: unsafe fn( + NonNull<()>, + &ParserConstruction, + NativeNodeId, + String, + String, + Option, + ) -> NativeNodeId, add_attrs_if_missing_for_parser: unsafe fn(NonNull<()>, NativeNodeId, Vec), 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) -> 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::().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::().as_mut() }.mark_script_already_started_for_parser(node_id); } - unsafe fn finish_parsing_script_children_impl( + unsafe fn finish_parsing_children_impl( 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::().as_mut() }.finish_parsing_script_children(node_id); - } - unsafe fn finish_parsing_link_children_impl( - 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::().as_mut() }.finish_parsing_link_children(node_id); - } - unsafe fn finish_parsing_style_children_impl( - 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::().as_mut() }.finish_parsing_style_children(node_id); + unsafe { data.cast::().as_mut() }.finish_parsing_children(construction, node_id); } unsafe fn attach_declarative_shadow_for_parser_impl( data: NonNull<()>, @@ -893,9 +881,7 @@ impl ParserDomMutationSink { push_parse_error: push_parse_error_impl::, set_html_quirks_mode_for_parser: set_html_quirks_mode_for_parser_impl::, mark_script_already_started_for_parser: mark_script_already_started_for_parser_impl::, - finish_parsing_script_children: finish_parsing_script_children_impl::, - finish_parsing_link_children: finish_parsing_link_children_impl::, - finish_parsing_style_children: finish_parsing_style_children_impl::, + finish_parsing_children: finish_parsing_children_impl::, attach_declarative_shadow_for_parser: attach_declarative_shadow_for_parser_impl::, associate_parser_form_owner: associate_parser_form_owner_impl::, } @@ -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, 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, ) -> 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, ) -> 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, open_parser_elements: Vec, + construction: ParserConstruction, allow_declarative_shadow_roots: bool, pending_null_custom_element_registry_elements: Vec, 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) { diff --git a/moli-parser/src/session.rs b/moli-parser/src/session.rs index c172426d11..9fa20921c3 100644 --- a/moli-parser/src/session.rs +++ b/moli-parser/src/session.rs @@ -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(), diff --git a/moli-parser/src/stream.rs b/moli-parser/src/stream.rs index 2aca656fc6..6faf1e1fee 100644 --- a/moli-parser/src/stream.rs +++ b/moli-parser/src/stream.rs @@ -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, ) -> 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); } diff --git a/moli-parser/src/xml.rs b/moli-parser/src/xml.rs index bc553283d7..d80b67d9b5 100644 --- a/moli-parser/src/xml.rs +++ b/moli-parser/src/xml.rs @@ -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::>(); - 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) { diff --git a/moli-parser/src/xml_stream.rs b/moli-parser/src/xml_stream.rs index 7d15c73df5..ff55f92647 100644 --- a/moli-parser/src/xml_stream.rs +++ b/moli-parser/src/xml_stream.rs @@ -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>) { diff --git a/moli-renderer-v8/src/document_runtime/document_write.rs b/moli-renderer-v8/src/document_runtime/document_write.rs index 03cc1daca7..8b278b7aee 100644 --- a/moli-renderer-v8/src/document_runtime/document_write.rs +++ b/moli-renderer-v8/src/document_runtime/document_write.rs @@ -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, diff --git a/moli-renderer-v8/src/document_runtime/dom_facade.rs b/moli-renderer-v8/src/document_runtime/dom_facade.rs index 8b6ba3c3f0..f40a31ed72 100644 --- a/moli-renderer-v8/src/document_runtime/dom_facade.rs +++ b/moli-renderer-v8/src/document_runtime/dom_facade.rs @@ -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, ) -> 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( diff --git a/moli-renderer-v8/src/dom_parser.rs b/moli-renderer-v8/src/dom_parser.rs index 02ec8c7270..cff5ea1cc2 100644 --- a/moli-renderer-v8/src/dom_parser.rs +++ b/moli-renderer-v8/src/dom_parser.rs @@ -234,13 +234,13 @@ fn materialize_xml_parser_error_document(parsed: NativeDom) -> NativeDom { for child in host.child_handles(document).collect::>() { 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(), diff --git a/moli-renderer-v8/src/native_bridge/context_host/child_documents/initial_empty.rs b/moli-renderer-v8/src/native_bridge/context_host/child_documents/initial_empty.rs index 1a87b86096..24e6225d16 100644 --- a/moli-renderer-v8/src/native_bridge/context_host/child_documents/initial_empty.rs +++ b/moli-renderer-v8/src/native_bridge/context_host/child_documents/initial_empty.rs @@ -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(), diff --git a/moli-renderer-v8/src/native_bridge/context_host/child_documents/live_parser.rs b/moli-renderer-v8/src/native_bridge/context_host/child_documents/live_parser.rs index 4b8652caa5..631bd47aad 100644 --- a/moli-renderer-v8/src/native_bridge/context_host/child_documents/live_parser.rs +++ b/moli-renderer-v8/src/native_bridge/context_host/child_documents/live_parser.rs @@ -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, ) -> 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) { @@ -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, + ) }, ) } diff --git a/moli-renderer-v8/src/runtime/phase_one/parser_turn.rs b/moli-renderer-v8/src/runtime/phase_one/parser_turn.rs index 4e2815d550..db3b8f6261 100644 --- a/moli-renderer-v8/src/runtime/phase_one/parser_turn.rs +++ b/moli-renderer-v8/src/runtime/phase_one/parser_turn.rs @@ -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, diff --git a/moli-renderer-v8/src/script_vm.rs b/moli-renderer-v8/src/script_vm.rs index 1b5c482761..47563fd1ee 100644 --- a/moli-renderer-v8/src/script_vm.rs +++ b/moli-renderer-v8/src/script_vm.rs @@ -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, diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/trusted_types.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/trusted_types.rs index 2f73df4443..566a0f0ae0 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/trusted_types.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/trusted_types.rs @@ -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 diff --git a/moli-renderer-v8/src/script_vm/tests/dom_xhr/dom.rs b/moli-renderer-v8/src/script_vm/tests/dom_xhr/dom.rs index a4e161d53b..fc743c70ce 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_xhr/dom.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_xhr/dom.rs @@ -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/", + "
probe
", + ); + let result = vm.eval(r#" +(() => { + const frame = document.body.appendChild(document.createElement('iframe')); + const d = frame.contentDocument; + d.open(); + d.write(''); + d.close(); + const complete = [!!moved.sheet, getComputedStyle(document.getElementById('probe')).color]; + moved.remove(); + + d.open(); + d.write('