Skip to main content

hpx_browser/
html_parser.rs

1use 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(NodeId::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(NodeId::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(NodeId::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(NodeId::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(NodeId::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(NodeId::DOCUMENT).is_some());
86
87        let html = dom.child_elements(NodeId::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(NodeId::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(NodeId::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}