kozan_core/dom/
element_data.rs1use std::cell::Cell;
9use std::collections::HashSet;
10
11use selectors::matching::ElementSelectorFlags;
12use servo_arc::Arc;
13use style::Atom;
14use style::data::ElementDataWrapper;
15use style::properties::declaration_block::PropertyDeclarationBlock;
16use style::properties::{Importance, PropertyDeclaration};
17use style::shared_lock::Locked;
18use style_dom::ElementState;
19use web_atoms::{LocalName, Namespace};
20
21use crate::dom::attribute::AttributeCollection;
22
23static HTML_NS: &str = "http://www.w3.org/1999/xhtml";
25
26pub struct ElementData {
28 pub(crate) tag_name: &'static str,
31
32 pub(crate) local_name: LocalName,
34
35 pub(crate) namespace: Namespace,
37
38 pub(crate) id: Option<Atom>,
40
41 pub(crate) classes: HashSet<Atom>,
44
45 pub(crate) element_state: ElementState,
48
49 #[allow(dead_code)]
51 pub(crate) is_focusable: bool,
52 #[allow(dead_code)]
53 pub(crate) tab_index: i32,
54
55 pub(crate) attributes: AttributeCollection,
58
59 pub(crate) inline_styles: PropertyDeclarationBlock,
69
70 pub(crate) style_attribute: Option<Arc<Locked<PropertyDeclarationBlock>>>,
72
73 pub(crate) inline_dirty: Cell<bool>,
75
76 pub(crate) stylo_data: ElementDataWrapper,
79
80 pub(crate) dirty_descendants: Cell<bool>,
82
83 pub(crate) has_snapshot: Cell<bool>,
85
86 pub(crate) handled_snapshot: Cell<bool>,
88
89 pub(crate) children_to_process: Cell<isize>,
91
92 pub(crate) selector_flags: Cell<ElementSelectorFlags>,
94}
95
96impl ElementData {
97 pub(crate) fn new(tag_name: &'static str, is_focusable: bool) -> Self {
98 let mut state = ElementState::DEFINED;
99 if is_focusable {
100 state.insert(ElementState::ENABLED);
101 }
102
103 Self {
104 tag_name,
105 local_name: LocalName::from(tag_name),
106 namespace: Namespace::from(HTML_NS),
107 id: None,
108 classes: HashSet::new(),
109 element_state: state,
110 is_focusable,
111 tab_index: if is_focusable { 0 } else { -1 },
112 attributes: AttributeCollection::new(),
113 inline_styles: PropertyDeclarationBlock::new(),
114 style_attribute: None,
115 inline_dirty: Cell::new(false),
116 stylo_data: ElementDataWrapper::default(),
117 dirty_descendants: Cell::new(true),
118 has_snapshot: Cell::new(false),
119 handled_snapshot: Cell::new(false),
120 children_to_process: Cell::new(0),
121 selector_flags: Cell::new(ElementSelectorFlags::empty()),
122 }
123 }
124
125 pub(crate) fn set_inline_property(&mut self, decl: PropertyDeclaration) {
130 self.inline_styles.push(decl, Importance::Normal);
131 self.inline_dirty.set(true);
132 }
133
134 pub(crate) fn set_inline_from_css(
136 &mut self,
137 value: &str,
138 guard: &style::shared_lock::SharedRwLock,
139 ) {
140 let url = url::Url::parse("kozan://inline").expect("hardcoded URL is always valid");
141 let url_data = style::stylesheets::UrlExtraData(Arc::new(url));
142 self.inline_styles = style::properties::parse_style_attribute(
143 value,
144 &url_data,
145 None,
146 selectors::matching::QuirksMode::NoQuirks,
147 style::stylesheets::CssRuleType::Style,
148 );
149 self.inline_dirty.set(true);
150 self.style_attribute = Some(Arc::new(guard.wrap(self.inline_styles.clone())));
152 self.inline_dirty.set(false);
153 }
154
155 pub(crate) fn clear_inline_styles(&mut self) {
157 self.inline_styles = PropertyDeclarationBlock::new();
158 self.style_attribute = None;
159 self.inline_dirty.set(false);
160 }
161
162 pub(crate) fn flush_inline_styles(&mut self, guard: &style::shared_lock::SharedRwLock) {
164 if !self.inline_dirty.get() {
165 return;
166 }
167 if self.inline_styles.is_empty() {
168 self.style_attribute = None;
169 } else {
170 self.style_attribute = Some(Arc::new(guard.wrap(self.inline_styles.clone())));
171 }
172 self.inline_dirty.set(false);
173 }
174
175 pub(crate) fn mark_for_restyle(&self) {
178 use style::invalidation::element::restyle_hints::RestyleHint;
179 let mut data = self.stylo_data.borrow_mut();
180 data.hint.insert(RestyleHint::RESTYLE_SELF);
181 }
182
183 pub(crate) fn on_attribute_set(
186 &mut self,
187 name: &str,
188 value: &str,
189 guard: &style::shared_lock::SharedRwLock,
190 ) -> bool {
191 match name {
192 "id" => {
193 self.id = Some(Atom::from(value));
194 true
195 }
196 "class" => {
197 self.classes = value.split_ascii_whitespace().map(Atom::from).collect();
198 true
199 }
200 "style" => {
201 self.set_inline_from_css(value, guard);
202 true
203 }
204 _ => false,
205 }
206 }
207
208 pub(crate) fn class_add(&mut self, name: &str) -> bool {
212 self.classes.insert(Atom::from(name))
213 }
214
215 pub(crate) fn class_remove(&mut self, name: &str) -> bool {
217 self.classes.remove(&Atom::from(name))
218 }
219
220 pub(crate) fn class_toggle(&mut self, name: &str) -> bool {
222 let atom = Atom::from(name);
223 if !self.classes.remove(&atom) {
224 self.classes.insert(atom);
225 true
226 } else {
227 false
228 }
229 }
230
231 pub(crate) fn class_contains(&self, name: &str) -> bool {
233 self.classes.contains(&Atom::from(name))
234 }
235
236 pub(crate) fn on_attribute_removed(&mut self, name: &str) -> bool {
237 match name {
238 "id" => {
239 self.id = None;
240 true
241 }
242 "class" => {
243 self.classes.clear();
244 true
245 }
246 "style" => {
247 self.clear_inline_styles();
248 true
249 }
250 _ => false,
251 }
252 }
253}