Skip to main content

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