Skip to main content

script/layout_dom/
servo_layout_node.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5#![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/// A wrapper around a `LayoutDom<Node>` which provides a safe interface that
50/// can be used during layout. This implements the `LayoutNode` trait.
51#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
52pub struct ServoLayoutNode<'dom> {
53    /// The wrapped private DOM node.
54    pub(super) node: LayoutDom<'dom, Node>,
55    /// The possibly nested [`PseudoElementChain`] for this node.
56    pub(super) pseudo_element_chain: PseudoElementChain,
57}
58
59/// Those are supposed to be sound, but they aren't because the entire system
60/// between script and layout so far has been designed to work around their
61/// absence. Switching the entire thing to the inert crate infra will help.
62unsafe impl Send for ServoLayoutNode<'_> {}
63unsafe impl Sync for ServoLayoutNode<'_> {}
64
65impl<'dom> ServoLayoutNode<'dom> {
66    /// Create a new [`ServoLayoutNode`] for this given [`TrustedNodeAddress`].
67    ///
68    /// # Safety
69    ///
70    /// The address pointed to by `address` should point to a valid node in memory.
71    pub unsafe fn new(address: &TrustedNodeAddress) -> Self {
72        unsafe { LayoutDom::from_trusted_node_address(*address) }.into()
73    }
74
75    /// Get the first child of this node.
76    ///
77    /// # Safety
78    ///
79    /// This node should never be exposed directly to the layout interface, as that may allow
80    /// mutating a node that is being laid out in another thread. Thus, this should *never* be
81    /// made public or exposed in the `LayoutNode` trait.
82    pub(super) unsafe fn dangerous_first_child(&self) -> Option<Self> {
83        self.node.first_child_ref().map(Into::into)
84    }
85
86    /// Get the next sibling of this node.
87    ///
88    /// # Safety
89    ///
90    /// This node should never be exposed directly to the layout interface, as that may allow
91    /// mutating a node that is being laid out in another thread. Thus, this should *never* be
92    /// made public or exposed in the `LayoutNode` trait.
93    pub(super) unsafe fn dangerous_next_sibling(&self) -> Option<Self> {
94        self.node.next_sibling_ref().map(Into::into)
95    }
96
97    /// Get the previous sibling of this node.
98    ///
99    /// # Safety
100    ///
101    /// This node should never be exposed directly to the layout interface, as that may allow
102    /// mutating a node that is being laid out in another thread. Thus, this should *never* be
103    /// made public or exposed in the `LayoutNode` trait.
104    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            // Text nodes are not styled during traversal,instead we simply
183            // return parent style here and do cascading during layout.
184            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        // This is a workaround for handling the `::selection` pseudos where it would not
197        // propagate to the children and Shadow DOM elements. For this case, UA widget
198        // inner elements should follow the originating element in terms of selection.
199        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        // Pseudo-elements do not ever have a selection.
255        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}