script/layout_dom/
servo_layout_element.rs1#![expect(unsafe_code)]
6#![deny(missing_docs)]
7
8use std::fmt;
9use std::hash::Hash;
10
11use html5ever::{LocalName, Namespace, local_name, ns};
12use layout_api::{
13 LayoutDataTrait, LayoutElement, LayoutNode, LayoutNodeType, PseudoElementChain, StyleData,
14};
15use servo_arc::Arc;
16use style::attr::AttrValue;
17use style::context::SharedStyleContext;
18use style::data::{ElementDataMut, ElementDataRef};
19use style::properties::ComputedValues;
20use style::selector_parser::{PseudoElement, PseudoElementCascadeType};
21use style::stylist::RuleInclusion;
22
23use crate::dom::bindings::root::LayoutDom;
24use crate::dom::element::Element;
25use crate::dom::node::{Node, NodeFlags};
26use crate::layout_dom::{
27 ServoDangerousStyleElement, ServoDangerousStyleShadowRoot, ServoLayoutNode,
28};
29
30impl fmt::Debug for LayoutDom<'_, Element> {
31 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32 write!(f, "<{}", self.local_name())?;
33 if let Some(id) = unsafe { (*self.id_attribute()).as_ref() } {
34 write!(f, " id={id}")?;
35 }
36 write!(f, "> ({:#x})", self.upcast::<Node>().opaque().0)
37 }
38}
39
40#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
42pub struct ServoLayoutElement<'dom> {
43 pub(super) element: LayoutDom<'dom, Element>,
45 pub(super) pseudo_element_chain: PseudoElementChain,
47}
48
49unsafe impl Send for ServoLayoutElement<'_> {}
50unsafe impl Sync for ServoLayoutElement<'_> {}
51
52impl<'dom> ServoLayoutElement<'dom> {
53 pub(super) fn is_html_element(&self) -> bool {
54 self.element.is_html_element()
55 }
56
57 pub(super) fn shadow_root(&self) -> Option<ServoDangerousStyleShadowRoot<'dom>> {
61 self.element.get_shadow_root_for_layout().map(Into::into)
62 }
63}
64
65impl<'dom> From<LayoutDom<'dom, Element>> for ServoLayoutElement<'dom> {
66 fn from(element: LayoutDom<'dom, Element>) -> Self {
67 Self {
68 element,
69 pseudo_element_chain: Default::default(),
70 }
71 }
72}
73
74impl<'dom> LayoutElement<'dom> for ServoLayoutElement<'dom> {
75 type ConcreteLayoutNode = ServoLayoutNode<'dom>;
76 type ConcreteStyleElement = ServoDangerousStyleElement<'dom>;
77
78 fn with_pseudo(&self, pseudo_element: PseudoElement) -> Option<Self> {
79 if pseudo_element.is_eager() &&
80 self.element_data()
81 .styles
82 .pseudos
83 .get(&pseudo_element)
84 .is_none()
85 {
86 return None;
87 }
88
89 if pseudo_element == PseudoElement::DetailsContent &&
90 (self.element.local_name() != &local_name!("details") ||
91 self.element.namespace() != &ns!(html) ||
92 self.attribute(&ns!(), &local_name!("open")).is_none())
93 {
94 return None;
95 }
96
97 if !self.pseudo_element_chain.is_empty() {
99 assert!(!pseudo_element.is_eager());
100 assert!(pseudo_element != PseudoElement::DetailsContent);
101 }
102
103 Some(Self {
104 element: self.element,
105 pseudo_element_chain: self.pseudo_element_chain.with_pseudo(pseudo_element),
106 })
107 }
108
109 fn pseudo_element_chain(&self) -> PseudoElementChain {
110 self.pseudo_element_chain
111 }
112
113 fn as_node(&self) -> ServoLayoutNode<'dom> {
114 ServoLayoutNode {
115 node: self.element.upcast(),
116 pseudo_element_chain: self.pseudo_element_chain,
117 }
118 }
119
120 fn initialize_style_and_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self) {
121 if self.element.style_data().is_none() {
122 unsafe { self.element.initialize_style_data() };
123 }
124
125 let node = self.element.upcast::<Node>();
126 if node.layout_data().is_none() {
127 unsafe { node.initialize_layout_data(Box::<RequestedLayoutDataType>::default()) };
128 }
129 }
130
131 fn unset_snapshot_flags(&self) {
132 unsafe {
133 self.as_node()
134 .node
135 .set_flag(NodeFlags::HAS_SNAPSHOT | NodeFlags::HANDLED_SNAPSHOT, false);
136 }
137 }
138
139 fn set_has_snapshot(&self) {
140 unsafe {
141 self.as_node().node.set_flag(NodeFlags::HAS_SNAPSHOT, true);
142 }
143 }
144
145 fn style_data(self) -> Option<&'dom StyleData> {
146 self.element.style_data()
147 }
148
149 fn element_data(&self) -> ElementDataRef<'dom> {
150 self.style_data()
151 .expect("Unstyled layout node?")
152 .element_data
153 .borrow()
154 }
155
156 fn element_data_mut(&self) -> ElementDataMut<'dom> {
157 self.style_data()
158 .expect("Unstyled layout node?")
159 .element_data
160 .borrow_mut()
161 }
162
163 fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
164 let get_style_for_pseudo_element =
165 |data: &ElementDataRef<'_>,
166 base_style: &Arc<ComputedValues>,
167 pseudo_element: PseudoElement| {
168 match pseudo_element.cascade_type() {
171 PseudoElementCascadeType::Eager => {
173 data.styles.pseudos.get(&pseudo_element).unwrap().clone()
174 },
175 PseudoElementCascadeType::Precomputed => context
176 .stylist
177 .precomputed_values_for_pseudo::<Self::ConcreteStyleElement>(
178 &context.guards,
179 &pseudo_element,
180 Some(base_style),
181 ),
182 PseudoElementCascadeType::Lazy => {
183 context
184 .stylist
185 .lazily_compute_pseudo_element_style(
186 &context.guards,
187 unsafe { self.dangerous_style_element() },
188 &pseudo_element,
189 RuleInclusion::All,
190 base_style,
191 false,
192 None,
193 )
194 .unwrap()
195 },
196 }
197 };
198
199 let data = self.element_data();
200 let element_style = data.styles.primary();
201 let pseudo_element_chain = self.pseudo_element_chain();
202
203 let primary_pseudo_style = match pseudo_element_chain.primary {
204 Some(pseudo_element) => {
205 get_style_for_pseudo_element(&data, element_style, pseudo_element)
206 },
207 None => return element_style.clone(),
208 };
209 match pseudo_element_chain.secondary {
210 Some(pseudo_element) => {
211 get_style_for_pseudo_element(&data, &primary_pseudo_style, pseudo_element)
212 },
213 None => primary_pseudo_style,
214 }
215 }
216
217 fn type_id(&self) -> Option<LayoutNodeType> {
218 self.as_node().type_id()
219 }
220
221 unsafe fn dangerous_style_element(self) -> ServoDangerousStyleElement<'dom> {
222 self.element.into()
223 }
224
225 fn local_name(&self) -> &LocalName {
226 self.element.local_name()
227 }
228
229 fn attribute(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
230 self.element.get_attr_for_layout(namespace, name)
231 }
232
233 fn attribute_as_str<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str> {
234 self.element.get_attr_val_for_layout(namespace, name)
235 }
236
237 fn is_shadow_host(&self) -> bool {
238 self.element.get_shadow_root_for_layout().is_some()
239 }
240
241 fn is_body_element_of_html_element_root(&self) -> bool {
242 self.element.is_body_element_of_html_element_root()
243 }
244
245 fn is_html_element_in_html_document(&self) -> bool {
246 self.element.is_html_element() &&
247 self.element
248 .upcast::<Node>()
249 .owner_doc_for_layout()
250 .is_html_document_for_layout()
251 }
252
253 fn is_root(&self) -> bool {
254 self.element.is_root()
255 }
256}