Skip to main content

layout_api/
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::Debug;
9
10use atomic_refcell::AtomicRef;
11use net_traits::image_cache::Image;
12use pixels::ImageMetadata;
13use servo_arc::Arc;
14use servo_base::id::{BrowsingContextId, PipelineId};
15use servo_base::text::{RangeAny, Utf32CodeUnits};
16use servo_url::ServoUrl;
17use style::context::SharedStyleContext;
18use style::dom::{NodeInfo, OpaqueNode, TNode};
19use style::properties::ComputedValues;
20use style::selector_parser::PseudoElement;
21
22use crate::layout_dom::{DangerousStyleNodeOf, LayoutElementOf, LayoutNodeOf};
23use crate::pseudo_element_chain::PseudoElementChain;
24use crate::{
25    GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutDataTrait, LayoutDomTypeBundle,
26    LayoutNodeType, SVGElementData, SharedSelection,
27};
28
29/// A trait that exposes a DOM nodes to layout. Implementors of this trait must abide by certain
30/// safety requirements. Layout will only ever access and mutate each node from a single thread
31/// at a time, though children may be used in parallel from other threads. That is why this trait
32/// does not allow access to parent nodes, as it would make it easy to cause race conditions and
33/// memory errors.
34///
35/// Note that the related [`DangerousStyleNode`] trait *may* access parent nodes, which is why
36/// that API is marked as `unsafe` here. In general [`DangerousStyleNode`] should only be used
37/// when interfacing with the `stylo` and `selectors`.
38pub trait LayoutNode<'dom>: Copy + Debug + NodeInfo + Send + Sync {
39    /// The concrete implementation of [`LayoutDomTypeBundle`] implemented in `script`.
40    type ConcreteTypeBundle: LayoutDomTypeBundle<'dom>;
41
42    /// Creates a new `LayoutNode` for the same `LayoutNode` with a different pseudo-element type.
43    ///
44    /// Returns `None` if this pseudo doesn't apply to the given element for one of
45    /// the following reasons:
46    ///
47    ///  1. This node is not an element.
48    ///  2. `pseudo` is eager and is not defined in the stylesheet. In this case, there
49    ///     is not reason to process the pseudo element at all.
50    ///  3. `pseudo` is for `::servo-details-content` and
51    ///     it doesn't apply to this element, either because it isn't a details or is
52    ///     in the wrong state.
53    fn with_pseudo(&self, pseudo_element_type: PseudoElement) -> Option<Self>;
54
55    /// Returns the [`PseudoElementChain`] for this [`LayoutElement`].
56    fn pseudo_element_chain(&self) -> PseudoElementChain;
57
58    /// Returns access to a version of this LayoutNode that can be used by stylo
59    /// and selectors. This is dangerous as it allows more access to ancestors nodes
60    /// than LayoutNode. This should *only* be used when handing a node to stylo
61    /// or selectors.
62    ///
63    /// # Safety
64    ///
65    /// This should only ever be called from the main script thread. It is never
66    /// okay to explicitly create a node for style while any layout worker threads
67    /// are running.
68    unsafe fn dangerous_style_node(self) -> DangerousStyleNodeOf<'dom, Self::ConcreteTypeBundle>;
69
70    /// Returns access to the DOM parent node of this node. This *does not* take
71    /// into account shadow tree children and slottables. For that use
72    /// [`Self::dangerous_flat_tree_parent`].
73    ///
74    /// # Safety
75    ///
76    /// This should only ever be called from the main script thread. It is never
77    /// okay to explicitly access the parent node while any layout worker threads
78    /// are running.
79    unsafe fn dangerous_dom_parent(self) -> Option<Self>;
80
81    /// Returns access to the flat tree parent node of this node. This takes
82    /// into account shadow tree children and slottables. For that use
83    /// [`Self::dangerous_flat_tree_parent`].
84    ///
85    /// # Safety
86    ///
87    /// This should only ever be called from the main script thread. It is never
88    /// okay to explicitly access the parent node while any layout worker threads
89    /// are running.
90    unsafe fn dangerous_flat_tree_parent(self) -> Option<Self>;
91
92    /// Get the layout data of this node, attempting to downcast it to the desired type.
93    /// Returns None if there is no layout data or it isn't of the desired type.
94    fn layout_data(&self) -> Option<&'dom GenericLayoutData>;
95
96    /// Returns whether the node is connected.
97    fn is_connected(&self) -> bool;
98
99    /// Converts self into an `OpaqueNode`.
100    fn opaque(&self) -> OpaqueNode;
101
102    /// Returns the type ID of this node. Returns `None` if this is a pseudo-element; otherwise,
103    /// returns `Some`.
104    fn type_id(&self) -> Option<LayoutNodeType>;
105
106    /// Initialize this node with empty opaque layout data.
107    fn initialize_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self);
108
109    /// Returns an iterator over this node's children in the [flat tree]. This
110    /// takes into account shadow tree children and slottables.
111    ///
112    /// [flat tree]: https://drafts.csswg.org/css-shadow-1/#flat-tree
113    fn flat_tree_children(&self) -> impl Iterator<Item = Self> + Sized;
114
115    /// Returns an iterator over this node's children in the DOM. This
116    /// *does not* take shadow roots and assigned slottables into account.
117    /// For that use [`Self::flat_tree_children`].
118    fn dom_children(&self) -> impl Iterator<Item = Self> + Sized;
119
120    /// Returns a [`LayoutElement`] if this is an element in the HTML namespace, None otherwise.
121    fn as_html_element(&self) -> Option<LayoutElementOf<'dom, Self::ConcreteTypeBundle>>;
122
123    /// Returns a [`LayoutElement`] if this is an element.
124    fn as_element(&self) -> Option<LayoutElementOf<'dom, Self::ConcreteTypeBundle>>;
125
126    /// Returns the computed style for the given node, properly handling pseudo-elements. For
127    /// elements this returns their style and for other nodes, this returns the style of the parent
128    /// element, if one exists.
129    ///
130    /// # Panics
131    ///
132    /// - Calling this method will panic it is an element has no style data, whether because
133    ///   styling has not run yet or was not run for this element.
134    /// - Calling this method will panic if it is a non-element node without a parent element.
135    fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues>;
136
137    /// Returns the style for a text node. This is computed on the fly from the
138    /// parent style to avoid traversing text nodes in the style system.
139    ///
140    /// # Safety
141    ///
142    /// Note that this does require accessing the parent, which this interface
143    /// technically forbids. But accessing the parent is only unsafe insofar as
144    /// it can be used to reach siblings and cousins. A simple immutable borrow
145    /// of the parent data is fine, since the bottom-up traversal will not process
146    /// the parent until all the children have been processed.
147    ///
148    /// # Panics
149    ///
150    /// - Calling this method will panic if the parent element has no style data, whether
151    ///   because styling has not run yet or was not run for this element.
152    /// - Calling this method will panic if it is a non-element node without a parent element.
153    fn parent_style(&self, context: &SharedStyleContext) -> Arc<ComputedValues>;
154
155    /// Returns the computed `:selected` style for the given node, properly handling
156    /// pseudo-elements. For elements this returns their style and for other nodes, this
157    /// returns the style of the parent element, if one exists.
158    ///
159    /// # Panics
160    ///
161    /// - Calling this method will panic it is an element has no style data, whether because
162    ///   styling has not run yet or was not run for this element.
163    /// - Calling this method will panic if it is a non-element node without a parent element.
164    fn selected_style(&self, context: &SharedStyleContext) -> Arc<ComputedValues>;
165
166    /// Get the text content of this node, if it is a text node.
167    ///
168    /// # Panics
169    ///
170    /// This method will panic if called on a node that is not a DOM text node.
171    fn text_content(self) -> AtomicRef<'dom, str>;
172
173    /// For a text node, returns which range of this text is part of the document selection
174    ///
175    /// Returned offsets are counted in `char`s in the `self.text_content()` string.
176    fn document_selection_in_text_node(&self) -> Option<RangeAny<Utf32CodeUnits>>;
177
178    /// For a text node, returns which range of this text is part of a form control selection.
179    fn form_control_selection_in_text_node(&self) -> Option<SharedSelection>;
180
181    /// If this is an image element, returns its URL. If this is not an image element, fails.
182    fn image_url(&self) -> Option<ServoUrl>;
183
184    /// If this is an image element, returns its current-pixel-density. If this is not an image element, fails.
185    fn image_density(&self) -> Option<f64>;
186
187    /// If this is an image element, returns its image data. Otherwise, returns `None`.
188    fn image_data(&self) -> Option<(Option<Image>, Option<ImageMetadata>)>;
189
190    /// Whether or not this is an image element that is showing a broken image icon.
191    fn showing_broken_image_icon(&self) -> bool;
192
193    /// Return the [`HTMLCanvas`] data for this node, if it is a canvas.
194    fn canvas_data(&self) -> Option<HTMLCanvasData>;
195
196    /// Return the [`SVGElementData`] for this node, if it is an SVG subtree.
197    fn svg_data(&self) -> Option<SVGElementData<'dom>>;
198
199    /// Return the [`HTMLMediaData`] for this node, if it is a media element.
200    fn media_data(&self) -> Option<HTMLMediaData>;
201
202    /// If this node is an iframe element, returns its browsing context ID. If this node is
203    /// not an iframe element, fails. Returns None if there is no nested browsing context.
204    fn iframe_browsing_context_id(&self) -> Option<BrowsingContextId>;
205
206    /// If this node is an iframe element, returns its pipeline ID. If this node is
207    /// not an iframe element, fails. Returns None if there is no nested browsing context.
208    fn iframe_pipeline_id(&self) -> Option<PipelineId>;
209
210    /// Return the table span property if it is an element that supports it.
211    fn table_span(&self) -> Option<u32>;
212
213    /// Return the table colspan property if it is an element that supports it.
214    fn table_colspan(&self) -> Option<u32>;
215
216    /// Return the table rowspan property if it is an element that supports it.
217    fn table_rowspan(&self) -> Option<u32>;
218
219    /// Whether this is a container for the text within a single-line text input. This
220    /// is used to solve the special case of line height for a text entry widget.
221    /// <https://html.spec.whatwg.org/multipage/#the-input-element-as-a-text-entry-widget>
222    fn is_single_line_text_input(&self) -> bool;
223
224    /// Whether or not this [`LayoutNode`] is in a user agent widget shadow DOM.
225    fn is_root_of_user_agent_widget(&self) -> bool;
226
227    /// Set whether or not this node has an active pseudo-element style with a `content`
228    /// attribute that uses `attr`.
229    fn set_uses_content_attribute_with_attr(&self, _uses_content_attribute_with_attr: bool);
230}
231
232/// A node that can be passed to `stylo` and `selectors` that allows accessing the
233/// parent node. We consider this to be too dangerous for normal layout, so it is
234/// reserved only for using `stylo` and `selectors`.
235///
236/// If you are not interfacing with `stylo` and `selectors` you *should not* use this
237/// type, unless you know what you are doing.
238pub trait DangerousStyleNode<'dom>: TNode + Sized + NodeInfo + Send + Sync {
239    /// The concrete implementation of [`LayoutDomTypeBundle`] implemented in `script`.
240    type ConcreteTypeBundle: LayoutDomTypeBundle<'dom>;
241    /// Get a handle to the original "safe" version of this node, a [`LayoutNode`] implementation.
242    fn layout_node(&self) -> LayoutNodeOf<'dom, Self::ConcreteTypeBundle>;
243}