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}