hpx_browser/
html_parser.rs1use blitz_dom::{BaseDocument, DocumentConfig, StyleThreading};
2use blitz_html::HtmlDocument;
3
4use crate::dom::Dom;
5
6#[cfg_attr(feature = "hotpath", hotpath::measure)]
7pub fn parse_html(html: &str) -> Dom {
8 let mut config = DocumentConfig::default();
9 config.style_threading = StyleThreading::Sequential;
10 config.base_url = Some("http://localhost".to_string());
11 let html_doc = HtmlDocument::from_html(html, config);
12 let base: BaseDocument = html_doc.into();
13 Dom::from_base(base)
14}
15
16#[cfg(test)]
17mod tests {
18 use super::*;
19 use crate::dom::{DomElement, NodeId, QualName};
20
21 fn find_child_by_tag(dom: &Dom, parent: NodeId, tag: &str) -> NodeId {
22 dom.child_elements(parent)
23 .into_iter()
24 .find(|&id| {
25 dom.get(id)
26 .map(|n| n.is_element_with_tag(tag))
27 .unwrap_or(false)
28 })
29 .unwrap()
30 }
31
32 #[test]
33 fn parse_basic_html() {
34 let dom = parse_html("<html><body><h1>Hello</h1></body></html>");
35 let children = dom.children(dom.document());
36 assert!(!children.is_empty(), "Document should have children");
37 }
38
39 #[test]
40 fn parse_has_html_element() {
41 let dom = parse_html("<html><head></head><body><p>Test</p></body></html>");
42 let doc_children = dom.child_elements(dom.document());
43 assert!(!doc_children.is_empty());
44
45 let html_el = DomElement::new(&dom, doc_children[0]).unwrap();
46 assert_eq!(html_el.local_name(), "html");
47 }
48
49 #[test]
50 fn parse_text_content() {
51 let dom = parse_html("<html><body><p>Hello world</p></body></html>");
52 let html = dom.child_elements(dom.document())[0];
53 let body = find_child_by_tag(&dom, html, "body");
54 let p = dom.child_elements(body)[0];
55 assert_eq!(dom.text_content(p), "Hello world");
56 }
57
58 #[test]
59 fn parse_attributes() {
60 let dom = parse_html("<div id=\"main\" class=\"container\">test</div>");
61 let html = dom.child_elements(dom.document())[0];
62 let body = find_child_by_tag(&dom, html, "body");
63 let div = dom.child_elements(body)[0];
64 let el = DomElement::new(&dom, div).unwrap();
65
66 assert_eq!(el.id(), Some("main"));
67 assert!(el.has_class("container"));
68 }
69
70 #[test]
71 fn parse_nested_structure() {
72 let dom = parse_html("<html><body><div><span>a</span><span>b</span></div></body></html>");
73 let html = dom.child_elements(dom.document())[0];
74 let body = find_child_by_tag(&dom, html, "body");
75 let div = find_child_by_tag(&dom, body, "div");
76
77 let spans = dom.child_elements(div);
78 assert!(!spans.is_empty(), "expected at least 1 span");
79 assert_eq!(dom.text_content(div), "ab");
80 }
81
82 #[test]
83 fn bdd_parse_simple_html_via_parser() {
84 let dom = parse_html("<html><body><h1>Hello</h1></body></html>");
85 assert!(dom.get(dom.document()).is_some());
86
87 let html = dom.child_elements(dom.document())[0];
88 let body = find_child_by_tag(&dom, html, "body");
89 let h1 = find_child_by_tag(&dom, body, "h1");
90
91 assert_eq!(dom.text_content(h1), "Hello");
92 }
93
94 #[test]
95 fn bdd_query_elements_via_parser() {
96 let dom = parse_html("<div class='content'><p>First</p><p>Second</p></div>");
97 let ps = dom.get_elements_by_tag_name(dom.document(), "p");
98 assert_eq!(ps.len(), 2);
99 assert_eq!(dom.text_content(ps[0]), "First");
100 }
101
102 #[test]
103 fn bdd_mutate_dom_via_parser() {
104 let dom = parse_html("<div><span>Old</span></div>");
105 let html = dom.child_elements(dom.document())[0];
106 let body = find_child_by_tag(&dom, html, "body");
107 let div = find_child_by_tag(&dom, body, "div");
108
109 let mut dom = dom;
110 let p = dom.create_element(QualName::new("p"), vec![]);
111 let text = dom.create_text("New".to_string());
112 dom.append_child(div, p);
113 dom.append_child(p, text);
114
115 assert_eq!(dom.children(div).len(), 2);
116 }
117}