swc_html_parser/parser/
active_formatting_element_stack.rs

1use swc_html_ast::Token;
2
3use crate::parser::{is_same_node, RcNode, TokenAndInfo};
4
5#[derive(Debug)]
6pub enum ActiveFormattingElement {
7    Element(RcNode, TokenAndInfo),
8    Marker,
9}
10
11pub struct ActiveFormattingElementStack {
12    pub items: Vec<ActiveFormattingElement>,
13}
14
15impl ActiveFormattingElementStack {
16    pub fn new() -> Self {
17        ActiveFormattingElementStack {
18            items: Vec::with_capacity(8),
19        }
20    }
21
22    pub fn push(&mut self, value: ActiveFormattingElement) {
23        // When the steps below require the UA to push onto the list of active
24        // formatting elements an element element, the UA must perform the following
25        // steps:
26
27        // 1. If there are already three elements in the list of active formatting
28        // elements after the last marker, if any, or anywhere in the list if
29        // there are no markers, that have the same tag name, namespace, and
30        // attributes as element, then remove the earliest such element from the
31        // list of active formatting elements. For these purposes, the attributes
32        // must be compared as they were when the elements were created by the parser;
33        // two elements have the same attributes if all their parsed attributes
34        // can be paired such that the two attributes in each pair have
35        // identical names, namespaces, and values (the order of the attributes
36        // does not matter).
37        let mut count = 0;
38        let new_element = match &value {
39            ActiveFormattingElement::Element(node, token_and_info) => (node, token_and_info),
40            _ => {
41                unreachable!();
42            }
43        };
44
45        for element in self.items.iter().rev() {
46            let (node_in_element, token_and_info_in_element) = match &element {
47                ActiveFormattingElement::Marker => {
48                    break;
49                }
50                ActiveFormattingElement::Element(node, token_and_info) => {
51                    if get_namespace!(node) != get_namespace!(new_element.0)
52                        || get_tag_name!(node) != get_tag_name!(new_element.0)
53                    {
54                        continue;
55                    }
56
57                    (node.clone(), token_and_info)
58                }
59            };
60
61            let attributes_in_element = match &token_and_info_in_element.token {
62                Token::StartTag { attributes, .. } | Token::EndTag { attributes, .. } => attributes,
63                _ => {
64                    unreachable!()
65                }
66            };
67            let attributes_in_new_element = match &new_element.1.token {
68                Token::StartTag { attributes, .. } | Token::EndTag { attributes, .. } => attributes,
69                _ => {
70                    unreachable!()
71                }
72            };
73
74            if attributes_in_element.len() != attributes_in_new_element.len() {
75                continue;
76            }
77
78            let mut sorted_attributes_in_element = attributes_in_element.clone();
79
80            for attribute in &mut sorted_attributes_in_element {
81                attribute.span = Default::default();
82            }
83
84            let mut sorted_attributes_in_new_element = attributes_in_new_element.clone();
85
86            for attribute in &mut sorted_attributes_in_new_element {
87                attribute.span = Default::default();
88            }
89
90            sorted_attributes_in_element.sort();
91            sorted_attributes_in_new_element.sort();
92
93            if sorted_attributes_in_element != sorted_attributes_in_new_element {
94                continue;
95            }
96
97            count += 1;
98
99            if count == 3 {
100                self.remove(&node_in_element);
101
102                break;
103            }
104        }
105
106        // 2. Add element to the list of active formatting elements.
107        self.items.push(value);
108    }
109
110    pub fn remove(&mut self, node: &RcNode) {
111        let position = self.get_position(node);
112
113        if let Some(position) = position {
114            self.items.remove(position);
115        }
116    }
117
118    pub fn insert_marker(&mut self) {
119        self.items.push(ActiveFormattingElement::Marker);
120    }
121
122    pub fn get_position(&self, element: &RcNode) -> Option<usize> {
123        self.items.iter().position(|n| match *n {
124            ActiveFormattingElement::Marker => false,
125            ActiveFormattingElement::Element(ref handle, _) => is_same_node(handle, element),
126        })
127    }
128
129    pub fn clear_to_last_marker(&mut self) {
130        loop {
131            match self.items.pop() {
132                None | Some(ActiveFormattingElement::Marker) => break,
133                _ => (),
134            }
135        }
136    }
137}