script/layout_dom/
servo_layout_node.rs1#![expect(unsafe_code)]
6#![deny(missing_docs)]
7
8use std::fmt;
9
10use atomic_refcell::AtomicRef;
11use layout_api::{
12 GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutDataTrait, LayoutElement, LayoutNode,
13 LayoutNodeType, PseudoElementChain, SVGElementData, TrustedNodeAddress,
14};
15use net_traits::image_cache::Image;
16use pixels::ImageMetadata;
17use servo_arc::Arc;
18use servo_base::cross_process_instant::CrossProcessInstant;
19use servo_base::id::{BrowsingContextId, PipelineId};
20use servo_base::text::{RangeAny, Utf32CodeUnits};
21use servo_url::ServoUrl;
22use style;
23use style::context::SharedStyleContext;
24use style::dom::{LayoutIterator, NodeInfo};
25use style::properties::ComputedValues;
26use style::selector_parser::PseudoElement;
27
28use super::ServoLayoutElement;
29use crate::dom::bindings::root::LayoutDom;
30use crate::dom::element::Element;
31use crate::dom::layout_dom::NodeTypeIdWrapper;
32use crate::dom::node::{Node, NodeFlags};
33use crate::layout_dom::{
34 ServoDangerousStyleNode, ServoLayoutDomTypeBundle, ServoLayoutNodeChildrenIterator,
35};
36
37impl fmt::Debug for LayoutDom<'_, Node> {
38 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39 if let Some(element) = self.downcast::<Element>() {
40 element.fmt(f)
41 } else if self.is_text_node_for_layout() {
42 write!(f, "<text node> ({:#x})", self.opaque().0)
43 } else {
44 write!(f, "<non-text node> ({:#x})", self.opaque().0)
45 }
46 }
47}
48
49#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
52pub struct ServoLayoutNode<'dom> {
53 pub(super) node: LayoutDom<'dom, Node>,
55 pub(super) pseudo_element_chain: PseudoElementChain,
57}
58
59unsafe impl Send for ServoLayoutNode<'_> {}
63unsafe impl Sync for ServoLayoutNode<'_> {}
64
65impl<'dom> ServoLayoutNode<'dom> {
66 pub unsafe fn new(address: &TrustedNodeAddress) -> Self {
72 unsafe { LayoutDom::from_trusted_node_address(*address) }.into()
73 }
74
75 pub(super) unsafe fn dangerous_first_child(&self) -> Option<Self> {
83 self.node.first_child_ref().map(Into::into)
84 }
85
86 pub(super) unsafe fn dangerous_next_sibling(&self) -> Option<Self> {
94 self.node.next_sibling_ref().map(Into::into)
95 }
96
97 pub(super) unsafe fn dangerous_previous_sibling(&self) -> Option<Self> {
105 self.node.prev_sibling_ref().map(Into::into)
106 }
107}
108
109impl<'dom> From<LayoutDom<'dom, Node>> for ServoLayoutNode<'dom> {
110 fn from(node: LayoutDom<'dom, Node>) -> Self {
111 Self {
112 node,
113 pseudo_element_chain: Default::default(),
114 }
115 }
116}
117
118impl<'dom> LayoutNode<'dom> for ServoLayoutNode<'dom> {
119 type ConcreteTypeBundle = ServoLayoutDomTypeBundle<'dom>;
120
121 fn with_pseudo(&self, pseudo_element_type: PseudoElement) -> Option<Self> {
122 Some(
123 self.as_element()?
124 .with_pseudo(pseudo_element_type)?
125 .as_node(),
126 )
127 }
128
129 unsafe fn dangerous_style_node(self) -> ServoDangerousStyleNode<'dom> {
130 self.node.into()
131 }
132
133 unsafe fn dangerous_dom_parent(self) -> Option<Self> {
134 self.node.parent_node_ref().map(Into::into)
135 }
136
137 unsafe fn dangerous_flat_tree_parent(self) -> Option<Self> {
138 self.node
139 .traversal_parent()
140 .map(|parent_element| parent_element.upcast().into())
141 }
142
143 unsafe fn dangerous_flat_tree_parent_considering_pseudos(self) -> Option<Self> {
144 if let Some(pseudo_element_chain) = self.pseudo_element_chain.without_innermost() {
145 Some(ServoLayoutNode {
146 pseudo_element_chain,
147 node: self.node,
148 })
149 } else {
150 unsafe { self.dangerous_flat_tree_parent() }
151 }
152 }
153
154 fn is_connected(&self) -> bool {
155 unsafe { self.node.get_flag(NodeFlags::IS_CONNECTED) }
156 }
157
158 fn layout_data(&self) -> Option<&'dom GenericLayoutData> {
159 self.node.layout_data()
160 }
161
162 fn opaque(&self) -> style::dom::OpaqueNode {
163 self.node.opaque()
164 }
165
166 fn pseudo_element_chain(&self) -> PseudoElementChain {
167 self.pseudo_element_chain
168 }
169
170 fn type_id(&self) -> Option<LayoutNodeType> {
171 if self.pseudo_element_chain.is_empty() {
172 Some(NodeTypeIdWrapper(self.node.type_id_for_layout()).into())
173 } else {
174 None
175 }
176 }
177
178 fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
179 if let Some(element) = self.as_element() {
180 element.style(context)
181 } else {
182 debug_assert!(self.is_text_node());
185 self.parent_style(context)
186 }
187 }
188
189 fn parent_style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
190 unsafe { self.dangerous_flat_tree_parent_considering_pseudos() }
191 .unwrap()
192 .style(context)
193 }
194
195 fn selected_style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
196 if self.node.is_in_ua_widget() &&
200 let Some(shadow_root) = self.node.containing_shadow_root_for_layout()
201 {
202 return Self::from(shadow_root.get_host_for_layout().upcast()).selected_style(context);
203 }
204
205 let Some(element) = self.as_element() else {
206 return unsafe { self.dangerous_flat_tree_parent() }
207 .expect("Non-element should always have parent")
208 .selected_style(context);
209 };
210
211 let selected_style = element
212 .element_data()
213 .styles
214 .pseudos
215 .get(&PseudoElement::Selection)
216 .cloned();
217 selected_style.unwrap_or_else(|| self.style(context))
218 }
219
220 fn initialize_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self) {
221 if self.node.layout_data().is_none() {
222 unsafe {
223 self.node
224 .initialize_layout_data(Box::<RequestedLayoutDataType>::default());
225 }
226 }
227 }
228
229 fn flat_tree_children(&self) -> impl Iterator<Item = Self> {
230 LayoutIterator(ServoLayoutNodeChildrenIterator::new_for_flat_tree(*self))
231 }
232
233 fn dom_children(&self) -> impl Iterator<Item = Self> {
234 LayoutIterator(ServoLayoutNodeChildrenIterator::new_for_dom_tree(*self))
235 }
236
237 fn as_element(&self) -> Option<ServoLayoutElement<'dom>> {
238 self.node.downcast().map(|element| ServoLayoutElement {
239 element,
240 pseudo_element_chain: self.pseudo_element_chain,
241 })
242 }
243
244 fn as_html_element(&self) -> Option<ServoLayoutElement<'dom>> {
245 self.as_element()
246 .filter(|element| element.is_html_element())
247 }
248
249 fn text_content(self) -> AtomicRef<'dom, str> {
250 self.node.text_content()
251 }
252
253 fn text_node_selection(&self) -> Option<RangeAny<Utf32CodeUnits>> {
254 if !self.pseudo_element_chain.is_empty() {
256 return None;
257 }
258
259 self.node.selection_for_text_node()
260 }
261
262 fn text_node_paints_caret(&self) -> bool {
263 self.node.text_node_paints_caret()
264 }
265
266 fn text_node_needs_caret_placeholder(&self) -> bool {
267 self.node.text_node_needs_caret_placeholder()
268 }
269
270 fn replaced_is_selected(&self) -> bool {
271 self.node.replaced_is_selected()
272 }
273
274 fn image_url(&self) -> Option<ServoUrl> {
275 self.node.image_url()
276 }
277
278 fn image_density(&self) -> Option<f64> {
279 self.node.image_density()
280 }
281
282 fn showing_broken_image_icon(&self) -> bool {
283 self.node.showing_broken_image_icon()
284 }
285
286 fn image_data(&self) -> Option<(Option<Image>, Option<ImageMetadata>)> {
287 self.node.image_data()
288 }
289
290 fn load_time(&self) -> Option<CrossProcessInstant> {
291 self.node.load_time()
292 }
293
294 fn canvas_data(&self) -> Option<HTMLCanvasData> {
295 self.node.canvas_data()
296 }
297
298 fn media_data(&self) -> Option<HTMLMediaData> {
299 self.node.media_data()
300 }
301
302 fn svg_data(&self) -> Option<SVGElementData<'dom>> {
303 self.node.svg_data()
304 }
305
306 fn iframe_browsing_context_id(&self) -> Option<BrowsingContextId> {
307 self.node.iframe_browsing_context_id()
308 }
309
310 fn iframe_pipeline_id(&self) -> Option<PipelineId> {
311 self.node.iframe_pipeline_id()
312 }
313
314 fn table_span(&self) -> Option<u32> {
315 self.node
316 .downcast::<Element>()
317 .and_then(|element| element.get_span())
318 }
319
320 fn table_colspan(&self) -> Option<u32> {
321 self.node
322 .downcast::<Element>()
323 .and_then(|element| element.get_colspan())
324 }
325
326 fn table_rowspan(&self) -> Option<u32> {
327 self.node
328 .downcast::<Element>()
329 .and_then(|element| element.get_rowspan())
330 }
331
332 fn set_uses_content_attribute_with_attr(&self, uses_content_attribute_with_attr: bool) {
333 unsafe {
334 self.node.set_flag(
335 NodeFlags::USES_ATTR_IN_CONTENT_ATTRIBUTE,
336 uses_content_attribute_with_attr,
337 )
338 }
339 }
340
341 fn is_single_line_text_input(&self) -> bool {
342 self.pseudo_element_chain.is_empty() && self.node.is_text_container_of_single_line_input()
343 }
344
345 fn is_root_of_user_agent_widget(&self) -> bool {
346 self.node.is_root_of_user_agent_widget()
347 }
348}
349
350impl NodeInfo for ServoLayoutNode<'_> {
351 fn is_element(&self) -> bool {
352 self.node.is_element_for_layout()
353 }
354
355 fn is_text_node(&self) -> bool {
356 self.node.is_text_node_for_layout()
357 }
358}