mirror of
https://github.com/lexmount/moli.git
synced 2026-10-09 08:01:05 +00:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user