diff --git a/crates/gosub_css3/src/matcher.rs b/crates/gosub_css3/src/matcher.rs index c58366aa7..415a3c642 100644 --- a/crates/gosub_css3/src/matcher.rs +++ b/crates/gosub_css3/src/matcher.rs @@ -1,6 +1,7 @@ +pub mod functions; +mod property_definitions; +mod shorthands; +mod styling; mod syntax; mod syntax_matcher; -mod shorthands; -mod property_definitions; mod walker; -mod styling; diff --git a/crates/gosub_css3/src/matcher/functions/attr.rs b/crates/gosub_css3/src/matcher/functions/attr.rs index 56ca22797..6c5ecb649 100644 --- a/crates/gosub_css3/src/matcher/functions/attr.rs +++ b/crates/gosub_css3/src/matcher/functions/attr.rs @@ -1,14 +1,20 @@ -use gosub_css3::stylesheet::CssValue; -use gosub_html5::node::Node; +use crate::stylesheet::CssValue; +use gosub_shared::traits::css3::CssSystem; +use gosub_shared::traits::node::{ElementDataType, Node}; -pub fn resolve_attr(values: &[CssValue], node: &Node) -> Vec { +// Probably this shouldn't quite be in gosub_css3 +pub fn resolve_attr, C: CssSystem>(values: &[CssValue], node: &N) -> Vec { let Some(attr_name) = values.first().map(|v| v.to_string()) else { return vec![]; }; let ty = values.get(1).cloned(); - let Some(attr_value) = node.get_attribute(&attr_name) else { + let Some(data) = node.get_element_data() else { + return vec![]; + }; + + let Some(attr_value) = data.attribute(&attr_name) else { let _default_value = values.get(2).cloned(); if let Some(ty) = ty { diff --git a/crates/gosub_css3/src/matcher/functions/calc.rs b/crates/gosub_css3/src/matcher/functions/calc.rs index 5e24bd79f..b0df90801 100644 --- a/crates/gosub_css3/src/matcher/functions/calc.rs +++ b/crates/gosub_css3/src/matcher/functions/calc.rs @@ -1,4 +1,4 @@ -use gosub_css3::stylesheet::CssValue; +use crate::stylesheet::CssValue; pub fn resolve_calc(_values: &[CssValue]) -> Vec { todo!() diff --git a/crates/gosub_css3/src/matcher/functions/var.rs b/crates/gosub_css3/src/matcher/functions/var.rs index 1180456ca..5aacdb63f 100644 --- a/crates/gosub_css3/src/matcher/functions/var.rs +++ b/crates/gosub_css3/src/matcher/functions/var.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; -use gosub_css3::stylesheet::CssValue; -use gosub_html5::node::Node; -use gosub_html5::parser::document::Document; +use crate::stylesheet::CssValue; +use gosub_shared::traits::css3::CssSystem; +use gosub_shared::traits::document::Document; #[derive(Clone, Debug, Default)] pub struct VariableEnvironment { @@ -10,7 +10,12 @@ pub struct VariableEnvironment { } impl VariableEnvironment { - pub fn get(&self, name: &str, _doc: &Document, _node: &Node) -> Option { + pub fn get, C: CssSystem>( + &self, + name: &str, + _doc: &D, + _node: &D::Node, + ) -> Option { let mut current = Some(self); while let Some(env) = current { @@ -29,7 +34,11 @@ impl VariableEnvironment { } } -pub fn resolve_var(values: &[CssValue], doc: &Document, node: &Node) -> Vec { +pub fn resolve_var, C: CssSystem>( + values: &[CssValue], + doc: &D, + node: &D::Node, +) -> Vec { let Some(name) = values.first().map(|v| { let mut str = v.to_string(); diff --git a/crates/gosub_html5/src/document/builder.rs b/crates/gosub_html5/src/document/builder.rs index 2428b3799..8016e050d 100644 --- a/crates/gosub_html5/src/document/builder.rs +++ b/crates/gosub_html5/src/document/builder.rs @@ -1,22 +1,20 @@ use std::collections::HashMap; -use url::Url; use gosub_shared::traits::css3::CssSystem; +use url::Url; -use gosub_shared::traits::document::{Document, DocumentType}; -use gosub_shared::traits::node::{Node, QuirksMode}; use crate::document::document::DocumentImpl; -use crate::DocumentHandle; use crate::node::HTML_NAMESPACE; +use crate::DocumentHandle; +use gosub_shared::traits::document::{Document, DocumentType}; +use gosub_shared::traits::node::{Node, QuirksMode}; /// This struct will be used to create a fully initialized document or document fragment pub struct DocumentBuilder {} impl gosub_shared::traits::document::DocumentBuilder for DocumentBuilder { - type Document = DocumentImpl; - /// Creates a new document with a document root node fn new_document(url: Option) -> DocumentHandle { let doc = >::new(DocumentType::HTML, url, None); @@ -24,7 +22,9 @@ impl gosub_shared::traits::document::DocumentBuilder for Docume } /// Creates a new document fragment with the context as the root node - fn new_document_fragment(context_node: &>::Node) -> DocumentHandle { + fn new_document_fragment( + context_node: &>::Node, + ) -> DocumentHandle { let handle = context_node.handle(); // Create a new document with an HTML node as the root node @@ -35,20 +35,28 @@ impl gosub_shared::traits::document::DocumentBuilder for Docume HashMap::new(), context_node.location().clone(), ); - let fragment_doc = >::new(DocumentType::HTML, None, Some(&fragment_root_node)); + let fragment_doc = >::new( + DocumentType::HTML, + None, + Some(fragment_root_node), + ); let mut fragment_handle = DocumentHandle::create(fragment_doc); let context_doc_handle = context_node.handle(); match context_doc_handle.get().quirks_mode() { QuirksMode::Quirks => { - fragment_handle.get_mut().set_quirks_mode(QuirksMode::Quirks); + fragment_handle + .get_mut() + .set_quirks_mode(QuirksMode::Quirks); } QuirksMode::LimitedQuirks => { - fragment_handle.get_mut().set_quirks_mode(QuirksMode::LimitedQuirks); + fragment_handle + .get_mut() + .set_quirks_mode(QuirksMode::LimitedQuirks); } _ => {} } fragment_handle } -} \ No newline at end of file +} diff --git a/crates/gosub_html5/src/document/document.rs b/crates/gosub_html5/src/document/document.rs index 318f49869..ca5354583 100755 --- a/crates/gosub_html5/src/document/document.rs +++ b/crates/gosub_html5/src/document/document.rs @@ -1,14 +1,9 @@ -use gosub_shared::traits::document::{Document as OtherDocument, Document, DocumentType}; use crate::DocumentHandle; use core::fmt::Debug; +use gosub_shared::traits::document::{Document as OtherDocument, Document, DocumentType}; use std::collections::HashMap; use url::Url; -use gosub_shared::byte_stream::Location; -use gosub_shared::node::NodeId; -use gosub_shared::traits::css3::CssSystem; -use gosub_shared::traits::node::Node; -use gosub_shared::traits::node::QuirksMode; use crate::document::builder::DocumentBuilder; use crate::document::fragment::DocumentFragmentImpl; use crate::node::arena::NodeArena; @@ -19,7 +14,12 @@ use crate::node::data::element::{ElementClass, ElementData}; use crate::node::data::text::TextData; use crate::node::node::{NodeDataTypeInternal, NodeImpl}; use crate::node::visitor::Visitor; - +use crate::writer::DocumentWriter; +use gosub_shared::byte_stream::Location; +use gosub_shared::node::NodeId; +use gosub_shared::traits::css3::CssSystem; +use gosub_shared::traits::node::Node; +use gosub_shared::traits::node::QuirksMode; // /// according to HTML spec: // /// https://html.spec.whatwg.org/#global-attributes // pub(crate) fn is_valid_id_attribute_value(value: &str) -> bool { @@ -30,7 +30,6 @@ use crate::node::visitor::Visitor; #[derive(Debug)] pub struct DocumentImpl { // pub handle: Weak>, - /// URL of the given document (if any) pub url: Option, /// Holds and owns all nodes in the document @@ -84,7 +83,7 @@ impl Document for DocumentImpl { /// Creates a new document without a doc handle #[must_use] - fn new(document_type: DocumentType, url: Option, root_node: Option<&Self::Node>) -> Self { + fn new(document_type: DocumentType, url: Option, root_node: Option) -> Self { let mut doc = Self { url, arena: NodeArena::new(), @@ -106,11 +105,6 @@ impl Document for DocumentImpl { // self.handle.upgrade().expect("failure").get().unwrap() } - fn add_stylesheet(&mut self, stylesheet: C::Stylesheet) { - self.stylesheets.push(stylesheet); - } - - /// Returns the URL of the document, or "" when no location is set fn url(&self) -> Option { match self.url { @@ -119,16 +113,16 @@ impl Document for DocumentImpl { } } + fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) { + self.quirks_mode = quirks_mode; + } + // // Returns a shared reference-counted handle for the document // fn new_with_dochandle(document_type: DocumentType, location: Option) -> DocumentHandle { // let doc = Self::new(document_type, location); // DocumentHandle::(Rc::new(RefCell::new(doc))) // } - fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) { - self.quirks_mode = quirks_mode; - } - fn quirks_mode(&self) -> QuirksMode { self.quirks_mode } @@ -151,6 +145,10 @@ impl Document for DocumentImpl { self.arena.node_mut(node_id) } + fn add_stylesheet(&mut self, stylesheet: C::Stylesheet) { + self.stylesheets.push(stylesheet); + } + /// returns the root node fn get_root(&self) -> &Self::Node { self.arena @@ -168,7 +166,7 @@ impl Document for DocumentImpl { fn attach_node(&mut self, node_id: NodeId, parent_id: NodeId, position: Option) { //check if any children of node have parent as child if parent_id == node_id || self.has_node_id_recursive(node_id, parent_id) { - return + return; } if let Some(parent_node) = self.node_by_id_mut(parent_id) { @@ -189,7 +187,10 @@ impl Document for DocumentImpl { } fn detach_node(&mut self, node_id: NodeId) { - let parent = self.node_by_id(node_id).expect("node not found").parent_id(); + let parent = self + .node_by_id(node_id) + .expect("node not found") + .parent_id(); if let Some(parent_id) = parent { let parent_node = self @@ -260,13 +261,18 @@ impl Document for DocumentImpl { } /// Register a node - fn register_node(&mut self, _node: &Self::Node) -> NodeId { + fn register_node(&mut self, _node: Self::Node) -> NodeId { todo!("register_node() not implemented"); } /// Inserts a node to the parent node at the given position in the children (or none /// to add at the end). Will automatically register the node if not done so already - fn register_node_at(&mut self, node: &Self::Node, parent_id: NodeId, position: Option) -> NodeId { + fn register_node_at( + &mut self, + node: Self::Node, + parent_id: NodeId, + position: Option, + ) -> NodeId { let node_id = self.register_node(node); self.attach_node(node_id, parent_id, position); @@ -274,60 +280,106 @@ impl Document for DocumentImpl { } /// Creates a new document node - fn new_document_node(handle: DocumentHandle, quirks_mode: QuirksMode, location: Location) -> Self::Node { + fn new_document_node( + handle: DocumentHandle, + quirks_mode: QuirksMode, + location: Location, + ) -> Self::Node { NodeImpl::new( handle.clone(), location, - &NodeDataTypeInternal::Document(DocumentData::new(quirks_mode)) + &NodeDataTypeInternal::Document(DocumentData::new(quirks_mode)), ) } - fn new_doctype_node(handle: DocumentHandle, name: &str, public_id: Option<&str>, system_id: Option<&str>, location: Location) -> Self::Node { + fn new_doctype_node( + handle: DocumentHandle, + name: &str, + public_id: Option<&str>, + system_id: Option<&str>, + location: Location, + ) -> Self::Node { NodeImpl::new( handle.clone(), location, - &NodeDataTypeInternal::DocType(DocTypeData::new(name, public_id.unwrap_or(""), system_id.unwrap_or(""))) + &NodeDataTypeInternal::DocType(DocTypeData::new( + name, + public_id.unwrap_or(""), + system_id.unwrap_or(""), + )), ) } /// Creates a new comment node - fn new_comment_node(handle: DocumentHandle, comment: &str, location: Location) -> Self::Node { + fn new_comment_node( + handle: DocumentHandle, + comment: &str, + location: Location, + ) -> Self::Node { NodeImpl::new( handle.clone(), location, - &NodeDataTypeInternal::Comment(CommentData::with_value(comment)) + &NodeDataTypeInternal::Comment(CommentData::with_value(comment)), ) } /// Creates a new text node - fn new_text_node(handle: DocumentHandle, value: &str, location: Location) -> Self::Node { + fn new_text_node( + handle: DocumentHandle, + value: &str, + location: Location, + ) -> Self::Node { NodeImpl::new( handle.clone(), location, - &NodeDataTypeInternal::Text(TextData::with_value(value)) + &NodeDataTypeInternal::Text(TextData::with_value(value)), ) } /// Creates a new element node - fn new_element_node(handle: DocumentHandle, name: &str, namespace: Option<&str>, attributes: HashMap, location: Location) -> Self::Node { + fn new_element_node( + handle: DocumentHandle, + name: &str, + namespace: Option<&str>, + attributes: HashMap, + location: Location, + ) -> Self::Node { NodeImpl::new( handle.clone(), location, - &NodeDataTypeInternal::Element(ElementData::new(name, namespace, attributes, ElementClass::default())) + &NodeDataTypeInternal::Element(ElementData::new( + name, + namespace, + attributes, + ElementClass::default(), + )), ) } + + fn write(&self) -> String { + self.write_from_node(NodeId::root()) + } + + fn write_from_node(&self, node_id: NodeId) -> String { + todo!(); //This should definitely be implemented + } } impl DocumentImpl { - /// Fetches a node by named id (string) or returns None when no node with this ID is found - pub fn get_node_by_named_id(&self, named_id: &str) -> Option<& as Document>::Node> { + pub fn get_node_by_named_id( + &self, + named_id: &str, + ) -> Option<& as Document>::Node> { let node_id = self.named_id_elements.get(named_id)?; self.arena.node(*node_id) } /// Fetches a mutable node by named id (string) or returns None when no node with this ID is found - pub fn get_node_by_named_id_mut(&mut self, named_id: &str) -> Option<&mut as Document>::Node> { + pub fn get_node_by_named_id_mut( + &mut self, + named_id: &str, + ) -> Option<&mut as Document>::Node> { let node_id = self.named_id_elements.get(named_id)?; self.arena.node_mut(*node_id) } @@ -374,20 +426,26 @@ impl DocumentImpl { false } - pub fn nodes(&self) -> &HashMap as Document>::Node> - { + pub fn nodes(&self) -> &HashMap as Document>::Node> { self.arena.nodes() } } // Walk the document tree with the given visitor -pub fn walk_document_tree(handle: DocumentHandle, C>, visitor: &mut Box as Document>::Node, C>>) { +pub fn walk_document_tree( + handle: DocumentHandle, C>, + visitor: &mut Box as Document>::Node, C>>, +) { let binding = handle.get(); let root = binding.get_root(); internal_visit(handle.clone(), root, visitor); } -fn internal_visit(handle: DocumentHandle, C>, node: & as Document>::Node, visitor: &mut Box as Document>::Node, C>>) { +fn internal_visit( + handle: DocumentHandle, C>, + node: & as Document>::Node, + visitor: &mut Box as Document>::Node, C>>, +) { visitor.document_enter(&node); let binding = handle.get(); @@ -447,11 +505,11 @@ impl + Clone, S: CssSystem> Iterator for TreeIterator { #[cfg(test)] mod tests { - use crate::node::{NodeTrait, NodeType, HTML_NAMESPACE}; - use crate::parser::document::{DocumentBuilder, DocumentTaskQueue, TreeIterator}; - use crate::parser::query::Query; - use crate::parser::tree_builder::TreeBuilder; - use crate::parser::{Node, NodeData, NodeId}; + use crate::node::HTML_NAMESPACE; + // use crate::parser::{DocumentBuilder, DocumentTaskQueue, TreeIterator}; + // use crate::parser::query::Query; + // use crate::parser::tree_builder::TreeBuilder; + // use crate::parser::{Node, NodeData, NodeId}; use gosub_shared::byte_stream::Location; use std::collections::HashMap; diff --git a/crates/gosub_html5/src/document/task_queue.rs b/crates/gosub_html5/src/document/task_queue.rs index fd30370f8..ab82bba76 100644 --- a/crates/gosub_html5/src/document/task_queue.rs +++ b/crates/gosub_html5/src/document/task_queue.rs @@ -1,6 +1,6 @@ -use std::collections::HashMap; -use gosub_shared::traits::document::Document; use crate::DocumentHandle; +use gosub_shared::traits::document::Document; +use std::collections::HashMap; use gosub_shared::byte_stream::Location; use gosub_shared::node::NodeId; @@ -91,7 +91,9 @@ impl, C: CssSystem> DocumentTaskQueue { HashMap::new(), location.clone(), ); - self.document.get_mut().register_node_at(&node, *parent_id, *position); + self.document + .get_mut() + .register_node_at(node, *parent_id, *position); } DocumentTask::CreateText { content, @@ -99,15 +101,20 @@ impl, C: CssSystem> DocumentTaskQueue { location, } => { let node = D::new_text_node(self.document.clone(), content, location.clone()); - self.document.get_mut().register_node_at(&node, *parent_id, None); + self.document + .get_mut() + .register_node_at(node, *parent_id, None); } DocumentTask::CreateComment { content, parent_id, location, } => { - let node = D::new_comment_node(self.document.clone(), content, location.clone()); - self.document.get_mut().register_node_at(&node, *parent_id, None); + let node = + D::new_comment_node(self.document.clone(), content, location.clone()); + self.document + .get_mut() + .register_node_at(node, *parent_id, None); } DocumentTask::InsertAttribute { key, @@ -130,4 +137,3 @@ impl, C: CssSystem> DocumentTaskQueue { errors } } - diff --git a/crates/gosub_html5/src/parser.rs b/crates/gosub_html5/src/parser.rs index 5cddcfb8b..6b5c9cf58 100644 --- a/crates/gosub_html5/src/parser.rs +++ b/crates/gosub_html5/src/parser.rs @@ -8,16 +8,6 @@ use std::rc::Rc; use log::warn; use url::Url; -use gosub_shared::node::NodeId; -use gosub_shared::byte_stream::{ByteStream, Location}; -use gosub_shared::types::{ParseError, Result}; -use gosub_shared::{timing_start, timing_stop}; -use gosub_shared::document::DocumentHandle; -use gosub_shared::traits::node::TextDataType; -use gosub_shared::traits::document::{Document, DocumentFragment, DocumentBuilder, DocumentType}; -use gosub_shared::traits::node::{ElementDataType, Node, QuirksMode}; -use gosub_shared::traits::{Context, ParserConfig}; -use gosub_shared::traits::css3::{CssOrigin, CssSystem}; use crate::node::{HTML_NAMESPACE, MATHML_NAMESPACE, SVG_NAMESPACE}; use crate::parser::attr_replacements::{ MATHML_ADJUSTMENTS, SVG_ADJUSTMENTS_ATTRIBUTES, SVG_ADJUSTMENTS_TAGS, XML_ADJUSTMENTS, @@ -26,12 +16,23 @@ use crate::parser::errors::{ErrorLogger, ParserError}; use crate::tokenizer::state::State; use crate::tokenizer::token::Token; use crate::tokenizer::{ParserData, Tokenizer, CHAR_REPLACEMENT}; +use gosub_shared::byte_stream::{ByteStream, Location}; +use gosub_shared::document::DocumentHandle; +use gosub_shared::node::NodeId; +use gosub_shared::traits::css3::{CssOrigin, CssSystem}; +use gosub_shared::traits::document::{Document, DocumentBuilder, DocumentFragment, DocumentType}; +use gosub_shared::traits::html5::ParserOptions; +use gosub_shared::traits::node::TextDataType; +use gosub_shared::traits::node::{ElementDataType, Node, QuirksMode}; +use gosub_shared::traits::{Context, ParserConfig}; +use gosub_shared::types::{ParseError, Result}; +use gosub_shared::{timing_start, timing_stop}; mod attr_replacements; +pub mod errors; pub mod query; mod quirks; pub mod tree_builder; -pub mod errors; // ------------------------------------------------------------ @@ -69,7 +70,7 @@ macro_rules! get_node_by_id { .get() .node_by_id($id) .expect("Node not found") - // @todo: clone or not? + // @todo: clone or not? .clone() }; } @@ -85,13 +86,17 @@ macro_rules! get_node_by_id { macro_rules! get_element_data { ($node:expr) => { - $node.get_element_data().expect("Node is not an element node") + $node + .get_element_data() + .expect("Node is not an element node") }; } macro_rules! get_element_data_mut { ($node:expr) => { - $node.get_element_data_mut().expect("Node is not an element node") + $node + .get_element_data_mut() + .expect("Node is not an element node") }; } @@ -109,7 +114,7 @@ macro_rules! current_node { .get() .node_by_id(*current_node_idx) .expect("Current node not found") - // @todo: clone or not? + // @todo: clone or not? .clone() }}; } @@ -121,7 +126,7 @@ macro_rules! open_elements_get { .get() .node_by_id($self.open_elements[$idx]) .expect("node in open_elements not found") - // @todo: clone or not? + // @todo: clone or not? .clone() }}; } @@ -149,6 +154,14 @@ pub struct Html5ParserOptions { pub scripting_enabled: bool, } +impl ParserOptions for Html5ParserOptions { + fn new(scripting: bool) -> Self { + Self { + scripting_enabled: scripting, + } + } +} + impl Default for Html5ParserOptions { fn default() -> Self { Self { @@ -234,11 +247,11 @@ enum DispatcherMode { } impl<'chars, D, C> Html5Parser<'chars, D, C> - where - D: Document, - C: CssSystem, - // <>::Node as Node>::ElementData: ElementDataType, - // <<>::Node as Node>::ElementData as ElementDataType>::DocumentFragment: DocumentFragment, +where + D: Document, + C: CssSystem, + // <>::Node as Node>::ElementData: ElementDataType, + // <<>::Node as Node>::ElementData as ElementDataType>::DocumentFragment: DocumentFragment, { // Initializes the parser for whole document parsing fn init( @@ -333,7 +346,9 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> ) -> Result> { // https://html.spec.whatwg.org/multipage/parsing.html#parsing-html-fragments - let context_node_element_data = context_node.get_element_data().expect("context node is not an element"); + let context_node_element_data = context_node + .get_element_data() + .expect("context node is not an element"); // 1. document.get_mut().set_doctype(DocumentType::HTML); @@ -345,8 +360,7 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> let error_logger = Rc::new(RefCell::new(ErrorLogger::new())); let tokenizer = Tokenizer::new(stream, None, error_logger.clone(), start_location.clone()); - let mut parser = - Html5Parser::init(tokenizer, document.clone(), error_logger, options); + let mut parser = Html5Parser::init(tokenizer, document.clone(), error_logger, options); // 4. / 12. parser.initialize_fragment_case(context_node); @@ -571,10 +585,7 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> } self.adjust_foreign_attributes(&mut current_token); - self.insert_foreign_element( - ¤t_token, - acn_element_data.namespace(), - ); + self.insert_foreign_element(¤t_token, acn_element_data.namespace()); if *is_self_closing { if name == "script" @@ -1051,7 +1062,9 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> .clone(); // Load stylesheet from text node - if let Some(stylesheet) = self.load_inline_stylesheet(CssOrigin::Author, &style_text_node) { + if let Some(stylesheet) = + self.load_inline_stylesheet(CssOrigin::Author, &style_text_node) + { self.document.get_mut().add_stylesheet(stylesheet); } @@ -1638,7 +1651,9 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> self.open_elements.pop(); - if !self.is_fragment_case && get_element_data!(current_node!(self)).name() != "frameset" { + if !self.is_fragment_case + && get_element_data!(current_node!(self)).name() != "frameset" + { // fragment case self.insertion_mode = InsertionMode::AfterFrameset; } @@ -1937,20 +1952,12 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> comment: value, location, .. - } => D::new_comment_node( - self.document.clone(), - value, - location.clone() - ), + } => D::new_comment_node(self.document.clone(), value, location.clone()), Token::Text { text: value, location, .. - } => D::new_text_node( - self.document.clone(), - value.as_str(), - location.clone() - ), + } => D::new_text_node(self.document.clone(), value.as_str(), location.clone()), Token::Eof { .. } => { panic!("EOF token not allowed"); } @@ -1984,12 +1991,18 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> if !([ "tbody", "td", "tfoot", "th", "thead", "tr", "dd", "dt", "li", "option", "optgroup", "p", "rb", "rp", "rt", "rtc", - ].contains(&tag) && is_html) { + ] + .contains(&tag) + && is_html) + { return; } } else if !([ "dd", "dt", "li", "option", "optgroup", "p", "rb", "rp", "rt", "rtc", - ].contains(&tag) && is_html) { + ] + .contains(&tag) + && is_html) + { return; } @@ -2112,7 +2125,9 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> /// Pop all elements back to a table context fn clear_stack_back_to_table_context(&mut self) { while !self.open_elements.is_empty() { - if ["table", "template", "html"].contains(&get_element_data!(current_node!(self)).name()) { + if ["table", "template", "html"] + .contains(&get_element_data!(current_node!(self)).name()) + { return; } self.open_elements.pop(); @@ -2271,9 +2286,7 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> // Add attributes to html element let first_node_id = *self.open_elements.first().unwrap(); let mut doc = self.document.get_mut(); - let first_node = doc - .node_by_id_mut(first_node_id) - .expect("node not found"); + let first_node = doc.node_by_id_mut(first_node_id).expect("node not found"); if first_node.is_element_node() { let mut element_data = get_element_data_mut!(first_node); @@ -2323,19 +2336,20 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> let node = get_node_by_id!(self.document, *node_id); let node_element_data = get_element_data!(node); - node_element_data.name() == "body" && node_element_data.is_namespace(HTML_NAMESPACE.into()) + node_element_data.name() == "body" + && node_element_data.is_namespace(HTML_NAMESPACE.into()) }); if let Some(body_node_id) = body_node_id { let mut doc = self.document.get_mut(); - let body_node = doc - .node_by_id_mut(*body_node_id) - .expect("node not found"); + let body_node = doc.node_by_id_mut(*body_node_id).expect("node not found"); let mut element_data = get_element_data_mut!(body_node); for (key, value) in attributes { if !element_data.attributes_mut().contains_key(key) { - element_data.attributes_mut().insert(key.to_owned(), value.to_owned()); + element_data + .attributes_mut() + .insert(key.to_owned(), value.to_owned()); } } } @@ -2343,7 +2357,9 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> Token::StartTag { name, .. } if name == "frameset" => { self.parse_error("frameset tag not allowed in in body insertion mode"); - if self.open_elements.len() == 1 || get_element_data!(open_elements_get!(self, 1)).name() != "body" { + if self.open_elements.len() == 1 + || get_element_data!(open_elements_get!(self, 1)).name() != "body" + { // ignore token return; } @@ -2357,9 +2373,7 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> let second_node_id = self.open_elements[1]; let second_node = get_node_by_id!(self.document, second_node_id); if second_node.parent_id().is_some() { - self.document - .get_mut() - .detach_node(second_node_id) + self.document.get_mut().detach_node(second_node_id) } } @@ -2447,7 +2461,8 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> self.close_p_element(); } - if ["h1", "h2", "h3", "h4", "h5", "h6"].contains(&get_element_data!(current_node!(self)).name()) + if ["h1", "h2", "h3", "h4", "h5", "h6"] + .contains(&get_element_data!(current_node!(self)).name()) { self.parse_error("h1-h6 not allowed in in body insertion mode"); self.open_elements.pop(); @@ -3001,7 +3016,9 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> self.generate_implied_end_tags(Some("rtc"), false); } - if get_element_data!(current_node!(self)).name() != "rtc" && get_element_data!(current_node!(self)).name() != "ruby" { + if get_element_data!(current_node!(self)).name() != "rtc" + && get_element_data!(current_node!(self)).name() != "ruby" + { self.parse_error("rp or rt not in scope"); } @@ -3155,7 +3172,7 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> Token::StartTag { name, .. } if name == "script" => { let insert_position = self.appropriate_place_insert(None); let node = self.create_node(&self.current_token.clone(), HTML_NAMESPACE); - let node_id = self.document.get_mut().register_node(&node); + let node_id = self.document.get_mut().register_node(node); self.insert_element_helper(node_id, insert_position); // TODO Set the element's parser document to the Document, and set the element's force async to false. @@ -3208,7 +3225,10 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> let node = binding.node_by_id_mut(node_id).expect("node not found"); if node.is_element_node() { let mut element_data = get_element_data_mut!(node); - element_data.set_template_contents(D::Fragment::new(clone_document, current_node_id)); + element_data.set_template_contents(D::Fragment::new( + clone_document, + current_node_id, + )); } } } @@ -3562,7 +3582,9 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> Token::EndTag { name, .. } if name == "optgroup" => { if get_element_data!(current_node!(self)).name() == "option" && self.open_elements.len() > 1 - && get_element_data!(open_elements_get!(self, self.open_elements.len() - 2)).name() == "optgroup" + && get_element_data!(open_elements_get!(self, self.open_elements.len() - 2)) + .name() + == "optgroup" { self.open_elements.pop(); } @@ -3701,7 +3723,9 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> ActiveElement::Marker => break, &ActiveElement::Node(id) => { let current_node = get_node_by_id!(self.document, id); - if get_element_data!(current_node).matches_tag_and_attrs_without_order(&node_element_data) { + if get_element_data!(current_node) + .matches_tag_and_attrs_without_order(&node_element_data) + { if matched >= 2 { first_matched = Some(id); break; @@ -4259,23 +4283,37 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> // Fetch the html from the url let response = ureq::get(url.as_ref()).call(); if response.is_err() { - warn!("Could not load external stylesheet from {}. Error: {}", url, response.unwrap_err()); + warn!( + "Could not load external stylesheet from {}. Error: {}", + url, + response.unwrap_err() + ); return None; } let response = response.expect("result"); if response.status() != 200 { - warn!("Could not load external stylesheet from {}. Status code {} ", url, response.status()); + warn!( + "Could not load external stylesheet from {}. Status code {} ", + url, + response.status() + ); return None; } if response.content_type() != "text/css" { - warn!("External stylesheet has no text/css content type: {} ", response.content_type()); + warn!( + "External stylesheet has no text/css content type: {} ", + response.content_type() + ); } match response.into_string() { Ok(css) => css, Err(err) => { - warn!("Could not load external stylesheet from {}. Error: {}", url, err); + warn!( + "Could not load external stylesheet from {}. Error: {}", + url, err + ); return None; } } @@ -4285,12 +4323,18 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> match std::fs::read_to_string(path) { Ok(css) => css, Err(err) => { - warn!("Could not load external stylesheet from {}. Error: {}", url, err); + warn!( + "Could not load external stylesheet from {}. Error: {}", + url, err + ); return None; } } } else { - warn!("Unsupported URL scheme for external stylesheet: {}", url.scheme()); + warn!( + "Unsupported URL scheme for external stylesheet: {}", + url.scheme() + ); return None; }; diff --git a/crates/gosub_html5/src/parser/helper.rs b/crates/gosub_html5/src/parser/helper.rs index ed674265f..a1e0dfb56 100644 --- a/crates/gosub_html5/src/parser/helper.rs +++ b/crates/gosub_html5/src/parser/helper.rs @@ -1,12 +1,12 @@ -use gosub_shared::document::DocumentHandle; use crate::node::HTML_NAMESPACE; use crate::parser::{ActiveElement, Html5Parser, Scope}; use crate::tokenizer::token::Token; +use gosub_shared::document::DocumentHandle; use gosub_shared::node::NodeId; use gosub_shared::traits::css3::CssSystem; use gosub_shared::traits::document::Document; -use gosub_shared::traits::node::{ElementDataType, Node, TextDataType}; use gosub_shared::traits::document::DocumentFragment; +use gosub_shared::traits::node::{ElementDataType, Node, TextDataType}; const ADOPTION_AGENCY_OUTER_LOOP_DEPTH: usize = 8; const ADOPTION_AGENCY_INNER_LOOP_DEPTH: usize = 3; @@ -33,10 +33,10 @@ impl<'chars, D, C> Html5Parser<'chars, D, C> where D: Document, C: CssSystem, - <>::Node as Node>::ElementData: ElementDataType, - <<>::Node as Node>::ElementData as ElementDataType>::DocumentFragment: DocumentFragment, + <>::Node as Node>::ElementData: ElementDataType, + <<>::Node as Node>::ElementData as ElementDataType>::DocumentFragment: + DocumentFragment, { - fn find_position_in_active_format(&self, node_id: NodeId) -> Option { self.active_formatting_elements .iter() @@ -81,7 +81,11 @@ where }) } - pub fn insert_element_helper(&mut self, node_id: NodeId, position: InsertionPositionMode) { + pub fn insert_element_helper( + &mut self, + node_id: NodeId, + position: InsertionPositionMode, + ) { match position { InsertionPositionMode::Sibling { handle, @@ -92,17 +96,23 @@ where let parent_node = get_node_by_id!(handle, parent); let position = parent_node.children().iter().position(|&x| x == before); let mut_handle = &mut handle.clone(); - mut_handle.get_mut().register_node_at(&node, parent, position); + mut_handle + .get_mut() + .register_node_at(node, parent, position); } InsertionPositionMode::LastChild { handle, parent } => { let node = get_node_by_id!(handle, node_id); let mut_handle = &mut handle.clone(); - mut_handle.get_mut().register_node_at(&node, parent, None); + mut_handle.get_mut().register_node_at(node, parent, None); } } } - pub fn insert_text_helper(&mut self, position: InsertionPositionMode, token: &Token) { + pub fn insert_text_helper( + &mut self, + position: InsertionPositionMode, + token: &Token, + ) { match position { InsertionPositionMode::Sibling { handle, @@ -115,7 +125,9 @@ where None | Some(0) => { let node = self.create_node(token, HTML_NAMESPACE); let mut_handle = &mut handle.clone(); - mut_handle.get_mut().register_node_at(&node, parent, position); + mut_handle + .get_mut() + .register_node_at(node, parent, position); } Some(index) => { let last_node_id = parent_node.children()[index - 1]; @@ -130,7 +142,9 @@ where let node = self.create_node(token, HTML_NAMESPACE); let mut_handle = &mut handle.clone(); - mut_handle.get_mut().register_node_at(&node, parent, Some(index)); + mut_handle + .get_mut() + .register_node_at(node, parent, Some(index)); } } } @@ -148,13 +162,13 @@ where let node = self.create_node(token, HTML_NAMESPACE); let mut_handle = &mut handle.clone(); - mut_handle.get_mut().register_node_at(&node, parent, None); + mut_handle.get_mut().register_node_at(node, parent, None); return; } let node = self.create_node(token, HTML_NAMESPACE); let mut_handle = &mut handle.clone(); - mut_handle.get_mut().register_node_at(&node, parent, None); + mut_handle.get_mut().register_node_at(node, parent, None); } } } @@ -186,14 +200,13 @@ where let mut data = get_element_data_mut!(&mut node); if let Some(class_string) = data.attributes().get("class") { - let class_string = class_string.clone(); data.add_class(&class_string.clone()); } } - self.insert_element(&node, override_node) + self.insert_element(node, override_node) } pub fn insert_element_from_node( @@ -214,10 +227,10 @@ where data.add_class(&class_string); } } - self.insert_element(&new_node, override_node) + self.insert_element(new_node, override_node) } - pub fn insert_element(&mut self, node: &D::Node, override_node: Option) -> NodeId { + pub fn insert_element(&mut self, node: D::Node, override_node: Option) -> NodeId { let node_id = self.document.get_mut().register_node(node); let insert_position = self.appropriate_place_insert(override_node); @@ -232,12 +245,17 @@ where pub fn insert_doctype_element(&mut self, token: &Token) { let node = self.create_node(token, HTML_NAMESPACE); - self.document.get_mut().register_node_at(&node, NodeId::root(), None); + self.document + .get_mut() + .register_node_at(node, NodeId::root(), None); } pub fn insert_document_element(&mut self, token: &Token) { let node = self.create_node(token, HTML_NAMESPACE); - let node_id = self.document.get_mut().register_node_at(&node, NodeId::root(), None); + let node_id = self + .document + .get_mut() + .register_node_at(node, NodeId::root(), None); self.open_elements.push(node_id); } @@ -245,11 +263,13 @@ where pub fn insert_comment_element(&mut self, token: &Token, insert_position: Option) { let node = self.create_node(token, HTML_NAMESPACE); if let Some(position) = insert_position { - self.document.get_mut().register_node_at(&node, position, None); + self.document + .get_mut() + .register_node_at(node, position, None); return; } - let node_id = self.document.get_mut().register_node(&node); + let node_id = self.document.get_mut().register_node(node); let insert_position = self.appropriate_place_insert(None); self.insert_element_helper(node_id, insert_position); } @@ -279,7 +299,8 @@ where if !(self.foster_parenting && ["table", "tbody", "thead", "tfoot", "tr"].contains(&element_data.name())) { - if element_data.name() == "template" && element_data.is_namespace(HTML_NAMESPACE.into()) { + if element_data.name() == "template" && element_data.is_namespace(HTML_NAMESPACE.into()) + { if let Some(template_fragment) = element_data.template_contents() { return InsertionPositionMode::LastChild { handle: template_fragment.handle(), @@ -461,7 +482,7 @@ where element_data.attributes().clone(), element_node.location().clone(), ); - let replace_node_id = self.document.get_mut().register_node(&replacement_node); + let replace_node_id = self.document.get_mut().register_node(replacement_node); self.active_formatting_elements[node_active_position] = ActiveElement::Node(replace_node_id); @@ -477,7 +498,9 @@ where // step 4.13.8 self.document.get_mut().detach_node(last_node_id); - self.document.get_mut().attach_node(last_node_id, replace_node_id, None); + self.document + .get_mut() + .attach_node(last_node_id, replace_node_id, None); // step 4.13.9 last_node_id = node_id; @@ -498,10 +521,7 @@ where ); // step 4.16 - let new_node_id = self - .document - .get_mut() - .register_node(&new_format_node); + let new_node_id = self.document.get_mut().register_node(new_format_node); let further_block_node = get_node_by_id!(self.document, further_block_node_id); for child in further_block_node.children() { diff --git a/crates/gosub_html5/src/writer.rs b/crates/gosub_html5/src/writer.rs index 751ca5e6f..5404b9bf7 100644 --- a/crates/gosub_html5/src/writer.rs +++ b/crates/gosub_html5/src/writer.rs @@ -1,13 +1,13 @@ +use crate::node::visitor::Visitor; use gosub_shared::document::DocumentHandle; use gosub_shared::node::NodeId; use gosub_shared::traits::css3::CssSystem; use gosub_shared::traits::document::Document; -use gosub_shared::traits::node::{CommentDataType, DocTypeDataType, Node, NodeType, TextDataType}; -use crate::node::visitor::Visitor; use gosub_shared::traits::node::ElementDataType; +use gosub_shared::traits::node::{CommentDataType, DocTypeDataType, Node, NodeType, TextDataType}; // Writer to convert a document to a string -struct DocumentWriter { +pub struct DocumentWriter { /// The buffer to write to buffer: String, /// Whether to include comments in the output @@ -15,7 +15,10 @@ struct DocumentWriter { } impl DocumentWriter { - pub fn write_from_node, C: CssSystem>(node: NodeId, handle: DocumentHandle) -> String { + pub fn write_from_node, C: CssSystem>( + node: NodeId, + handle: DocumentHandle, + ) -> String { let mut w = Self { comments: false, buffer: String::new(), @@ -25,7 +28,11 @@ impl DocumentWriter { w.buffer } - pub fn visit_node, C: CssSystem>(&mut self, id: NodeId, handle: DocumentHandle) { + pub fn visit_node, C: CssSystem>( + &mut self, + id: NodeId, + handle: DocumentHandle, + ) { let binding = handle.get(); let node = match binding.node_by_id(id) { Some(node) => node, @@ -61,7 +68,11 @@ impl DocumentWriter { } } - pub fn visit_children, C: CssSystem>(&mut self, children: &[NodeId], handle: DocumentHandle) { + pub fn visit_children, C: CssSystem>( + &mut self, + children: &[NodeId], + handle: DocumentHandle, + ) { for child in children { self.visit_node(*child, handle.clone()); } diff --git a/crates/gosub_renderer/src/draw.rs b/crates/gosub_renderer/src/draw.rs index b7a0f5552..c54480d2e 100644 --- a/crates/gosub_renderer/src/draw.rs +++ b/crates/gosub_renderer/src/draw.rs @@ -6,7 +6,6 @@ use url::Url; use gosub_css3::colors::RgbColor; use gosub_css3::stylesheet::CssValue; -use gosub_html5::node::NodeId; use gosub_net::http::fetcher::Fetcher; use gosub_render_backend::geo::{Size, SizeU32, FP}; use gosub_render_backend::layout::{Layout, LayoutTree, Layouter, TextLayout}; @@ -16,6 +15,7 @@ use gosub_render_backend::{ RenderBorder, RenderRect, RenderText, Scene as TScene, Text, Transform, }; use gosub_rendering::position::PositionTree; +use gosub_shared::node::NodeId; use gosub_shared::types::Result; use gosub_styling::render_tree::{RenderNodeData, RenderTree, RenderTreeNode}; diff --git a/crates/gosub_shared/src/traits/document.rs b/crates/gosub_shared/src/traits/document.rs index b07ebbfb7..5890482d5 100644 --- a/crates/gosub_shared/src/traits/document.rs +++ b/crates/gosub_shared/src/traits/document.rs @@ -1,10 +1,10 @@ -use std::collections::HashMap; -use url::Url; use crate::byte_stream::Location; use crate::document::DocumentHandle; use crate::node::NodeId; use crate::traits::css3::CssSystem; use crate::traits::node::{Node, QuirksMode}; +use std::collections::HashMap; +use url::Url; /// Type of the given document #[derive(PartialEq, Debug, Copy, Clone)] @@ -19,7 +19,9 @@ pub trait DocumentBuilder { type Document: Document; fn new_document(url: Option) -> DocumentHandle; - fn new_document_fragment(context_node: &>::Node) -> DocumentHandle; + fn new_document_fragment( + context_node: &>::Node, + ) -> DocumentHandle; } pub trait DocumentFragment: Sized { @@ -37,7 +39,7 @@ pub trait Document: Sized { type Builder: DocumentBuilder; // Creates a new doc with an optional document root node - fn new(document_type: DocumentType, url: Option, root_node: Option<&Self::Node>) -> Self; + fn new(document_type: DocumentType, url: Option, root_node: Option) -> Self; // /// Creates a new document with an optional document root node // fn new_with_handle(document_type: DocumentType, url: Option, location: &Location, root_node: Option<&Self::Node>) -> DocumentHandle; @@ -59,12 +61,12 @@ pub trait Document: Sized { fn add_stylesheet(&mut self, stylesheet: C::Stylesheet); - /// Return the root node of the document fn get_root(&self) -> &Self::Node; fn get_root_mut(&mut self) -> &mut Self::Node; fn attach_node(&mut self, node_id: NodeId, parent_id: NodeId, position: Option); + fn detach_node(&mut self, node_id: NodeId); fn relocate_node(&mut self, node_id: NodeId, parent_id: NodeId); @@ -81,14 +83,46 @@ pub trait Document: Sized { // fn peek_next_id(&self) -> NodeId; /// Register a new node - fn register_node(&mut self, node: &Self::Node) -> NodeId; + fn register_node(&mut self, node: Self::Node) -> NodeId; /// Register a new node at a specific position - fn register_node_at(&mut self, node: &Self::Node, parent_id: NodeId, position: Option) -> NodeId; + fn register_node_at( + &mut self, + node: Self::Node, + parent_id: NodeId, + position: Option, + ) -> NodeId; /// Node creation methods. The root node is needed in order to fetch the document handle (it can't be created from the document itself) - fn new_document_node(handle: DocumentHandle, quirks_mode: QuirksMode, location: Location) -> Self::Node; - fn new_doctype_node(handle: DocumentHandle, name: &str, public_id: Option<&str>, system_id: Option<&str>, location: Location) -> Self::Node; - fn new_comment_node(handle: DocumentHandle, comment: &str, location: Location) -> Self::Node; - fn new_text_node(handle: DocumentHandle, value: &str, location: Location) -> Self::Node; - fn new_element_node(handle: DocumentHandle, name: &str, namespace: Option<&str>, attributes: HashMap, location: Location) -> Self::Node; -} \ No newline at end of file + fn new_document_node( + handle: DocumentHandle, + quirks_mode: QuirksMode, + location: Location, + ) -> Self::Node; + fn new_doctype_node( + handle: DocumentHandle, + name: &str, + public_id: Option<&str>, + system_id: Option<&str>, + location: Location, + ) -> Self::Node; + fn new_comment_node( + handle: DocumentHandle, + comment: &str, + location: Location, + ) -> Self::Node; + fn new_text_node( + handle: DocumentHandle, + value: &str, + location: Location, + ) -> Self::Node; + fn new_element_node( + handle: DocumentHandle, + name: &str, + namespace: Option<&str>, + attributes: HashMap, + location: Location, + ) -> Self::Node; + + fn write(&self) -> String; + fn write_from_node(&self, node_id: NodeId) -> String; +} diff --git a/crates/gosub_shared/src/traits/html5.rs b/crates/gosub_shared/src/traits/html5.rs index fc138daaf..10916e2cc 100644 --- a/crates/gosub_shared/src/traits/html5.rs +++ b/crates/gosub_shared/src/traits/html5.rs @@ -1,12 +1,31 @@ +use crate::byte_stream::{ByteStream, Location}; use crate::document::DocumentHandle; use crate::traits::css3::CssSystem; use crate::traits::document::Document; -use crate::types::Result; +use crate::types::{ParseError, Result}; pub trait Html5Parser { - type Document: Document; - fn parse(&self, data: String) -> Result>; + type Options: ParserOptions; + + fn parse( + stream: &mut ByteStream, + doc: DocumentHandle, + opts: Option, + ) -> Result>; + + #[allow(clippy::type_complexity)] + fn parse_fragment( + stream: &mut ByteStream, + doc: DocumentHandle, + context_node: &>::Node, + options: Option, + start_location: Location, + ) -> Result>; +} + +pub trait ParserOptions { + fn new(scripting: bool) -> Self; } diff --git a/crates/gosub_styling/src/render_tree.rs b/crates/gosub_styling/src/render_tree.rs index 4529e3f31..c2fd8869f 100644 --- a/crates/gosub_styling/src/render_tree.rs +++ b/crates/gosub_styling/src/render_tree.rs @@ -9,6 +9,10 @@ use gosub_html5::node::{NodeData, NodeId}; use gosub_html5::parser::document::{Document, DocumentHandle, TreeIterator}; use gosub_render_backend::geo::Size; use gosub_render_backend::layout::{HasTextLayout, Layout, LayoutTree, Layouter, Node, TextLayout}; +use gosub_shared::document::DocumentHandle; +use gosub_shared::node::NodeId; +use gosub_shared::traits::css3::CssSystem; +use gosub_shared::traits::document::Document; use gosub_shared::types::Result; use crate::functions::{resolve_attr, resolve_calc, resolve_var}; @@ -243,7 +247,7 @@ impl RenderTree { render_tree } - fn generate_from(&mut self, mut document: DocumentHandle) { + fn generate_from, C: CssSystem>(&mut self, mut document: DocumentHandle) { // Iterate the complete document tree let tree_iterator = TreeIterator::new(&document); diff --git a/crates/gosub_svg/src/lib.rs b/crates/gosub_svg/src/lib.rs index 33b9f2fe9..76163a18f 100644 --- a/crates/gosub_svg/src/lib.rs +++ b/crates/gosub_svg/src/lib.rs @@ -1,6 +1,8 @@ use ::resvg::usvg; - -use gosub_html5::{node::NodeId, parser::document::DocumentHandle}; +use gosub_shared::document::DocumentHandle; +use gosub_shared::node::NodeId; +use gosub_shared::traits::css3::CssSystem; +use gosub_shared::traits::document::Document; use gosub_shared::types::Result; #[cfg(feature = "resvg")] @@ -21,8 +23,13 @@ impl SVGDocument { Ok(Self { tree }) } - pub fn from_html_doc(id: NodeId, doc: DocumentHandle) -> Result { - let str = doc.get().write_from_node(id); + pub fn from_html_doc, C: CssSystem>( + id: NodeId, + doc: DocumentHandle, + ) -> Result { + let doc = doc.get(); + + let str = doc.write_from_node(id); Self::from_str(&str) } diff --git a/crates/gosub_svg/src/resvg.rs b/crates/gosub_svg/src/resvg.rs index 4857cfb90..13df7255d 100644 --- a/crates/gosub_svg/src/resvg.rs +++ b/crates/gosub_svg/src/resvg.rs @@ -1,11 +1,13 @@ use anyhow::anyhow; use tiny_skia::Pixmap; -use gosub_html5::node::NodeId; -use gosub_html5::parser::document::DocumentHandle; use gosub_render_backend::geo::FP; use gosub_render_backend::svg::SvgRenderer; use gosub_render_backend::{Image, ImageBuffer, RenderBackend}; +use gosub_shared::document::DocumentHandle; +use gosub_shared::node::NodeId; +use gosub_shared::traits::css3::CssSystem; +use gosub_shared::traits::document::Document; use gosub_shared::types::{Result, Size}; use crate::SVGDocument; @@ -23,7 +25,10 @@ impl SvgRenderer for Resvg { SVGDocument::from_str(&data) } - fn parse_internal(tree: DocumentHandle, id: NodeId) -> Result { + fn parse_internal, C: CssSystem>( + tree: DocumentHandle, + id: NodeId, + ) -> Result { SVGDocument::from_html_doc(id, tree) } diff --git a/crates/gosub_testing/src/testing/tree_construction.rs b/crates/gosub_testing/src/testing/tree_construction.rs index 644065a07..bd3b6222e 100644 --- a/crates/gosub_testing/src/testing/tree_construction.rs +++ b/crates/gosub_testing/src/testing/tree_construction.rs @@ -3,7 +3,6 @@ mod generator; pub(crate) mod parser; pub mod result; -use gosub_shared::traits::document::{Document, DocumentBuilder}; use generator::TreeOutputGenerator; use gosub_html5::node::{HTML_NAMESPACE, MATHML_NAMESPACE, SVG_NAMESPACE}; use gosub_html5::parser::Html5ParserOptions; @@ -11,11 +10,13 @@ use gosub_shared::byte_stream::{ByteStream, Config, Encoding, Location}; use gosub_shared::document::DocumentHandle; use gosub_shared::node::NodeId; use gosub_shared::traits::css3::CssSystem; -use gosub_shared::traits::html5::Html5Parser; +use gosub_shared::traits::document::{Document, DocumentBuilder}; +use gosub_shared::traits::html5::{Html5Parser, ParserOptions}; use gosub_shared::types::{ParseError, Result}; use parser::{ScriptMode, TestSpec}; use result::TestResult; use result::{ResultStatus, TreeLineResult}; +use std::collections::HashMap; /// Holds a single parser test #[derive(Debug, Default, PartialEq, Clone)] @@ -75,19 +76,26 @@ impl Harness { } /// Runs a single test and returns the test result of that run - pub fn run_test(&mut self, test: Test, scripting_enabled: bool) -> Result { + pub fn run_test, C: CssSystem>( + &mut self, + test: Test, + scripting_enabled: bool, + ) -> Result { self.test = test; self.next_document_line = 0; - let (actual_document, actual_errors) = self.do_parse(scripting_enabled)?; - let result = self.generate_test_result(Document::clone(&actual_document), &actual_errors); + let (actual_document, actual_errors) = self.do_parse::(scripting_enabled)?; + let result = self.generate_test_result::(actual_document.clone(), &actual_errors); Ok(result) } /// Run the html5 parser and return the document tree and errors - fn do_parse, C: CssSystem>(&mut self, scripting_enabled: bool) -> Result<(DocumentHandle, Vec)> { - let options = Html5ParserOptions { scripting_enabled }; + fn do_parse, C: CssSystem>( + &mut self, + scripting_enabled: bool, + ) -> Result<(DocumentHandle, Vec)> { + let options = ::new(scripting_enabled); let mut stream = ByteStream::new( Encoding::UTF8, Some(Config { @@ -101,14 +109,12 @@ impl Harness { let (document, parse_errors) = if let Some(fragment) = self.test.spec.document_fragment.clone() { - self.parse_fragment(fragment, stream, options, Location::default())? + self.parse_fragment::(fragment, stream, options, Location::default())? } else { let document = >::Builder::new_document(None); - let parser_errors = P::parse( - &mut stream, - Document::clone(&document), - Some(options), - )?; + let parser_errors = + P::parse(&mut stream, DocumentHandle::clone(&document), Some(options))?; + (document, parser_errors) }; @@ -119,12 +125,13 @@ impl Harness { &mut self, fragment: String, mut stream: ByteStream, - options: Html5ParserOptions, + options: P::Options, start_location: Location, ) -> Result<(DocumentHandle, Vec)> { // First, create a (fake) main document that contains only the fragment as node let main_document = >::Builder::new_document(None); - let mut main_document = Document::clone(&main_document); + + let mut main_document = main_document.clone(); let (element, namespace) = if fragment.starts_with("svg ") { ( fragment.strip_prefix("svg ").unwrap().to_string(), @@ -139,25 +146,29 @@ impl Harness { (fragment, HTML_NAMESPACE) }; - // Add context node - let context_node_id = main_document.create_element( + let doc_clone = DocumentHandle::clone(&main_document); + + let mut doc = main_document.get_mut(); + + let node = >::new_element_node( + doc_clone.clone(), element.as_str(), - NodeId::root(), - None, - namespace, - start_location.clone(), + Some(namespace), + HashMap::new(), + start_location, ); - let context_node = main_document - .get() - .get_node_by_id(context_node_id) - .unwrap() - .clone(); - let document = DocumentBuilder::new_document_fragment(&context_node); + let context_node_id = doc.register_node(node); + + doc.attach_node(context_node_id, NodeId::root(), None); + + let context_node = doc.node_by_id(context_node_id).unwrap().clone(); + + let document = >::Builder::new_document_fragment(&context_node); - let parser_errors = Html5Parser::parse_fragment( + let parser_errors = P::parse_fragment( &mut stream, - Document::clone(&document), + DocumentHandle::clone(&document), &context_node, Some(options), start_location, @@ -209,9 +220,9 @@ impl Harness { Some(line.to_string()) } - fn generate_test_result( + fn generate_test_result, C: CssSystem>( &mut self, - document: DocumentHandle, + document: DocumentHandle, _parse_errors: &[ParseError], ) -> TestResult { let mut result = TestResult::default(); diff --git a/crates/gosub_testing/src/testing/tree_construction/generator.rs b/crates/gosub_testing/src/testing/tree_construction/generator.rs index 479c306f2..8ded9011c 100644 --- a/crates/gosub_testing/src/testing/tree_construction/generator.rs +++ b/crates/gosub_testing/src/testing/tree_construction/generator.rs @@ -1,9 +1,11 @@ -use gosub_shared::traits::node::{CommentDataType, ElementDataType, Node, NodeType, TextDataType}; use gosub_html5::node::HTML_NAMESPACE; use gosub_html5::node::{MATHML_NAMESPACE, SVG_NAMESPACE, XLINK_NAMESPACE, XMLNS_NAMESPACE}; use gosub_shared::document::DocumentHandle; use gosub_shared::traits::css3::CssSystem; use gosub_shared::traits::document::Document; +use gosub_shared::traits::node::{ + CommentDataType, DocTypeDataType, ElementDataType, Node, NodeType, TextDataType, +}; /// Generates a tree output that can be used for matching with the expected output pub struct TreeOutputGenerator, C: CssSystem> { @@ -39,7 +41,7 @@ impl, C: CssSystem> TreeOutputGenerator { if node.type_of() == NodeType::ElementNode { if let Some(element) = &node.get_element_data() { let mut sorted_attrs = vec![]; - for attr in &element.attributes { + for attr in element.attributes() { sorted_attrs.push(attr); } sorted_attrs.sort_by(|a, b| a.0.cmp(b.0)); @@ -52,19 +54,19 @@ impl, C: CssSystem> TreeOutputGenerator { attr.1 )); } - } - } - // Template tags have an extra "content" node in the test tree ouput - if node.name == "template" && node.is_namespace(HTML_NAMESPACE) { - output.push(format!("| {}content", " ".repeat(indent_level))); - indent_level += 1; + // Template tags have an extra "content" node in the test tree ouput + if element.name() == "template" && element.is_namespace(HTML_NAMESPACE) { + output.push(format!("| {}content", " ".repeat(indent_level))); + indent_level += 1; + } + } } } - for child_id in &node.children { + for child_id in node.children() { let doc = self.document.get(); - let child_node = doc.get_node_by_id(*child_id).expect("node not found"); + let child_node = doc.node_by_id(*child_id).expect("node not found"); output.append(&mut self.output_treeline(child_node, indent_level + 1)); } @@ -96,15 +98,14 @@ impl, C: CssSystem> TreeOutputGenerator { }; format!(r#""{}""#, data.value()) - }, + } NodeType::CommentNode => { - let Some(data) = node.get_comment_data() else { return "unknown".to_owned(); }; format!("", data.value()) - }, + } NodeType::DocTypeNode => { let Some(data) = node.get_doctype_data() else { return "".to_owned(); @@ -116,14 +117,16 @@ impl, C: CssSystem> TreeOutputGenerator { data.name() } else { // - format!( + &*format!( r#"{0} "{1}" "{2}""#, - data.name(), data.pub_identifier(), data.sys_identifier() + data.name(), + data.pub_identifier(), + data.sys_identifier() ) }; format!("", doctype_text.trim()) - }, + } NodeType::DocumentNode => String::new(), } }