layout_api/
layout_element.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 html5ever::{LocalName, Namespace};
11use servo_arc::Arc;
12use style::attr::AttrValue;
13use style::context::SharedStyleContext;
14use style::data::{ElementDataMut, ElementDataRef};
15use style::dom::TElement;
16use style::properties::ComputedValues;
17use style::selector_parser::{PseudoElement, SelectorImpl};
18
19use crate::{LayoutDataTrait, LayoutNode, LayoutNodeType, PseudoElementChain, StyleData};
20
21/// A trait that exposes a DOM element to layout. Implementors of this trait must abide by certain
22/// safety requirements. Layout will only ever access and mutate each element from a single thread
23/// at a time, though children may be used in parallel from other threads. That is why this trait
24/// does not allow access to parent nodes, as it would make it easy to cause race conditions and
25/// memory errors.
26///
27/// Note that the related [`DangerousStyleElement`] trait *may* access parent nodes, which is why
28/// that API is marked as `unsafe` here. In general [`DangerousStyleElement`] should only be used
29/// when interfacing with the `stylo` and `selectors`.
30pub trait LayoutElement<'dom>: Copy + Debug + Send + Sync {
31    /// An associated type that refers to the concrete implementation of [`DangerousStyleElement`]
32    /// implemented in `script`.
33    type ConcreteStyleElement: DangerousStyleElement<'dom>;
34    /// An associated type that refers to the concrete implementation of [`LayoutNode`]
35    /// implemented in `script`.
36    type ConcreteLayoutNode: LayoutNode<'dom>;
37
38    /// Creates a new `LayoutElement` for the same `LayoutElement`
39    /// with a different pseudo-element type.
40    ///
41    /// Returns `None` if this pseudo doesn't apply to the given element for one of
42    /// the following reasons:
43    ///
44    ///  1. `pseudo` is eager and is not defined in the stylesheet. In this case, there
45    ///     is not reason to process the pseudo element at all.
46    ///  2. `pseudo` is for `::servo-details-content` and
47    ///     it doesn't apply to this element, either because it isn't a details or is
48    ///     in the wrong state.
49    fn with_pseudo(&self, pseudo: PseudoElement) -> Option<Self>;
50
51    /// Returns the [`PseudoElementChain`] for this [`LayoutElement`].
52    fn pseudo_element_chain(&self) -> PseudoElementChain;
53
54    /// Return this [`LayoutElement`] as a [`LayoutNode`], preserving the internal
55    /// pseudo-element chain.
56    fn as_node(&self) -> Self::ConcreteLayoutNode;
57
58    /// Returns access to a version of this LayoutElement that can be used by stylo
59    /// and selectors. This is dangerous as it allows more access to ancestor nodes
60    /// that might be in the process of being read or written to in other threads.
61    /// This should *only* be used when handing a node to stylo 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_element(self) -> Self::ConcreteStyleElement;
69
70    /// Initialize this node with empty style and opaque layout data.
71    fn initialize_style_and_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self);
72
73    /// Unset the snapshot flags on the underlying DOM object for this element.
74    fn unset_snapshot_flags(&self);
75
76    /// Set the snapshot flags on the underlying DOM object for this element.
77    fn set_has_snapshot(&self);
78
79    /// Get the [`StyleData`] for this [`LayoutElement`].
80    fn style_data(self) -> Option<&'dom StyleData>;
81
82    /// Returns the type ID of this node.
83    /// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
84    fn type_id(&self) -> Option<LayoutNodeType>;
85
86    /// Get the local name of this element. See
87    /// <https://dom.spec.whatwg.org/#concept-element-local-name>.
88    fn local_name(&self) -> &LocalName;
89
90    /// Get the attribute with the given `namespace` and `name` as an [`AttrValue`] if it
91    /// exists, otherwise return `None`.
92    fn attribute(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue>;
93
94    /// Get the attribute with the given `namespace` and `name` as an [`AttrValue`] if it
95    /// exists and converting the result to a `&str`, otherwise return `None`.
96    fn attribute_as_str<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str>;
97
98    /// Get a reference to the inner [`ElementDataRef`] for this element's [`StyleData`]. This will
99    /// panic if the element is unstyled.
100    fn element_data(&self) -> ElementDataRef<'dom>;
101
102    /// Get a mutable reference to the inner [`ElementDataRef`] for this element's [`StyleData`].
103    /// This will panic if the element is unstyled.
104    fn element_data_mut(&self) -> ElementDataMut<'dom>;
105
106    /// Returns the computed style for the given element, properly handling pseudo-elements.
107    ///
108    /// # Panics
109    ///
110    /// Calling this method will panic if the element has no style data, whether because styling has
111    /// not run yet or was not run for this element.
112    fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues>;
113
114    /// Returns `true` if this [`LayoutElement`] is a shadow DOM host and `false` otherwise.
115    fn is_shadow_host(&self) -> bool;
116
117    /// Returns whether this node is a body element of an HTML element root
118    /// in an HTML document.
119    ///
120    /// Note that this does require accessing the parent, which this interface
121    /// technically forbids. But accessing the parent is only unsafe insofar as
122    /// it can be used to reach siblings and cousins. A simple immutable borrow
123    /// of the parent data is fine, since the bottom-up traversal will not process
124    /// the parent until all the children have been processed.
125    fn is_body_element_of_html_element_root(&self) -> bool;
126
127    /// Returns `true` if this [`LayoutNode`] is any kind of HTML element inside an HTML document
128    /// and `false` otherwise.
129    fn is_html_element_in_html_document(&self) -> bool;
130
131    /// Returns whether this node is the root element in an HTML document element.
132    ///
133    /// Note that, like `Self::is_body_element_of_html_element_root`, this accesses the parent.
134    /// As in that case, since this is an immutable borrow, we do not violate thread safety.
135    fn is_root(&self) -> bool;
136}
137
138/// An element that can be passed to `stylo` and `selectors` that allows accessing the
139/// parent node. We consider this to be too dangerous for normal layout, so it is
140/// reserved only for using `stylo` and `selectors`.
141///
142/// If you are not interfacing with `stylo` and `selectors` you *should not* use this
143/// type, unless you know what you are doing.
144pub trait DangerousStyleElement<'dom>:
145    TElement + ::selectors::Element<Impl = SelectorImpl> + Send + Sync
146{
147    /// The concrete implementation of [`LayoutElement`] implemented in `script`.
148    type ConcreteLayoutElement: LayoutElement<'dom>;
149    /// The concrete implementation of [`LayoutNode`] implemented in `script`.
150    /// Get a handle to the original "safe" version of this element, a [`LayoutElement`].
151    fn layout_element(&self) -> Self::ConcreteLayoutElement;
152}