layout_api/wrapper_traits.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
7use std::borrow::Cow;
8use std::fmt::Debug;
9
10use atomic_refcell::AtomicRef;
11use base::id::{BrowsingContextId, PipelineId};
12use fonts_traits::ByteIndex;
13use html5ever::{LocalName, Namespace};
14use malloc_size_of_derive::MallocSizeOf;
15use net_traits::image_cache::Image;
16use pixels::ImageMetadata;
17use range::Range;
18use servo_arc::Arc;
19use servo_url::ServoUrl;
20use style::attr::AttrValue;
21use style::context::SharedStyleContext;
22use style::data::ElementData;
23use style::dom::{LayoutIterator, NodeInfo, OpaqueNode, TElement, TNode};
24use style::properties::ComputedValues;
25use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl};
26use style::stylist::RuleInclusion;
27
28use crate::{
29 GenericLayoutData, GenericLayoutDataTrait, HTMLCanvasData, HTMLMediaData, LayoutNodeType,
30 SVGElementData, StyleData,
31};
32
33pub trait LayoutDataTrait: GenericLayoutDataTrait + Default + Send + Sync + 'static {}
34
35/// A wrapper so that layout can access only the methods that it should have access to. Layout must
36/// only ever see these and must never see instances of `LayoutDom`.
37/// FIXME(mrobinson): `Send + Sync` is required here for Layout 2020, but eventually it
38/// should stop sending LayoutNodes to other threads and rely on ThreadSafeLayoutNode
39/// or some other mechanism to ensure thread safety.
40pub trait LayoutNode<'dom>: Copy + Debug + TNode + Send + Sync {
41 type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'dom>;
42 fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode;
43
44 /// Returns the type ID of this node.
45 fn type_id(&self) -> LayoutNodeType;
46
47 /// Initialize this node with empty style and opaque layout data.
48 ///
49 /// # Safety
50 ///
51 /// This method is unsafe because it modifies the given node during
52 /// layout. Callers should ensure that no other layout thread is
53 /// attempting to read or modify the opaque layout data of this node.
54 unsafe fn initialize_style_and_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self);
55
56 /// Get the [`StyleData`] for this node. Returns None if the node is unstyled.
57 fn style_data(&self) -> Option<&'dom StyleData>;
58
59 /// Get the layout data of this node, attempting to downcast it to the desired type.
60 /// Returns None if there is no layout data or it isn't of the desired type.
61 fn layout_data(&self) -> Option<&'dom GenericLayoutData>;
62
63 fn rev_children(self) -> LayoutIterator<ReverseChildrenIterator<Self>> {
64 LayoutIterator(ReverseChildrenIterator {
65 current: self.last_child(),
66 })
67 }
68
69 fn traverse_preorder(self) -> TreeIterator<Self> {
70 TreeIterator::new(self)
71 }
72
73 /// Returns whether the node is connected.
74 fn is_connected(&self) -> bool;
75}
76
77pub struct ReverseChildrenIterator<ConcreteNode> {
78 current: Option<ConcreteNode>,
79}
80
81impl<'dom, ConcreteNode> Iterator for ReverseChildrenIterator<ConcreteNode>
82where
83 ConcreteNode: LayoutNode<'dom>,
84{
85 type Item = ConcreteNode;
86 fn next(&mut self) -> Option<ConcreteNode> {
87 let node = self.current;
88 self.current = node.and_then(|node| node.prev_sibling());
89 node
90 }
91}
92
93pub struct TreeIterator<ConcreteNode> {
94 stack: Vec<ConcreteNode>,
95}
96
97impl<'dom, ConcreteNode> TreeIterator<ConcreteNode>
98where
99 ConcreteNode: LayoutNode<'dom>,
100{
101 fn new(root: ConcreteNode) -> TreeIterator<ConcreteNode> {
102 let stack = vec![root];
103 TreeIterator { stack }
104 }
105
106 pub fn next_skipping_children(&mut self) -> Option<ConcreteNode> {
107 self.stack.pop()
108 }
109}
110
111impl<'dom, ConcreteNode> Iterator for TreeIterator<ConcreteNode>
112where
113 ConcreteNode: LayoutNode<'dom>,
114{
115 type Item = ConcreteNode;
116 fn next(&mut self) -> Option<ConcreteNode> {
117 let ret = self.stack.pop();
118 if let Some(node) = ret {
119 self.stack.extend(node.rev_children())
120 }
121 ret
122 }
123}
124
125/// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout
126/// node does not allow any parents or siblings of nodes to be accessed, to avoid races.
127pub trait ThreadSafeLayoutNode<'dom>: Clone + Copy + Debug + NodeInfo + PartialEq + Sized {
128 type ConcreteNode: LayoutNode<'dom, ConcreteThreadSafeLayoutNode = Self>;
129 type ConcreteElement: TElement;
130
131 type ConcreteThreadSafeLayoutElement: ThreadSafeLayoutElement<'dom, ConcreteThreadSafeLayoutNode = Self>
132 + ::selectors::Element<Impl = SelectorImpl>;
133 type ChildrenIterator: Iterator<Item = Self> + Sized;
134
135 /// Converts self into an `OpaqueNode`.
136 fn opaque(&self) -> OpaqueNode;
137
138 /// Returns the type ID of this node.
139 /// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
140 fn type_id(&self) -> Option<LayoutNodeType>;
141
142 /// Returns the style for a text node. This is computed on the fly from the
143 /// parent style to avoid traversing text nodes in the style system.
144 ///
145 /// Note that this does require accessing the parent, which this interface
146 /// technically forbids. But accessing the parent is only unsafe insofar as
147 /// it can be used to reach siblings and cousins. A simple immutable borrow
148 /// of the parent data is fine, since the bottom-up traversal will not process
149 /// the parent until all the children have been processed.
150 fn parent_style(&self) -> Arc<ComputedValues>;
151
152 /// Initialize this node with empty opaque layout data.
153 ///
154 /// # Safety
155 ///
156 /// This method is unsafe because it modifies the given node during
157 /// layout. Callers should ensure that no other layout thread is
158 /// attempting to read or modify the opaque layout data of this node.
159 fn initialize_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self);
160
161 fn debug_id(self) -> usize;
162
163 /// Returns an iterator over this node's children.
164 fn children(&self) -> LayoutIterator<Self::ChildrenIterator>;
165
166 /// Returns a ThreadSafeLayoutElement if this is an element, None otherwise.
167 fn as_element(&self) -> Option<Self::ConcreteThreadSafeLayoutElement>;
168
169 /// Returns a ThreadSafeLayoutElement if this is an element in an HTML namespace, None otherwise.
170 fn as_html_element(&self) -> Option<Self::ConcreteThreadSafeLayoutElement>;
171
172 /// Get the [`StyleData`] for this node. Returns None if the node is unstyled.
173 fn style_data(&self) -> Option<&'dom StyleData>;
174
175 /// Get the layout data of this node, attempting to downcast it to the desired type.
176 /// Returns None if there is no layout data or it isn't of the desired type.
177 fn layout_data(&self) -> Option<&'dom GenericLayoutData>;
178
179 fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
180 if let Some(el) = self.as_element() {
181 el.style(context)
182 } else {
183 // Text nodes are not styled during traversal,instead we simply
184 // return parent style here and do cascading during layout.
185 debug_assert!(self.is_text_node());
186 self.parent_style()
187 }
188 }
189
190 /// Returns true if this node contributes content. This is used in the implementation of
191 /// `empty_cells` per CSS 2.1 ยง 17.6.1.1.
192 fn is_content(&self) -> bool {
193 self.type_id().is_some()
194 }
195
196 /// Returns access to the underlying LayoutNode. This is breaks the abstraction
197 /// barrier of ThreadSafeLayout wrapper layer, and can lead to races if not used
198 /// carefully.
199 ///
200 /// We need this because the implementation of some methods need to access the layout
201 /// data flags, and we have this annoying trait separation between script and layout :-(
202 fn unsafe_get(self) -> Self::ConcreteNode;
203
204 fn node_text_content(self) -> Cow<'dom, str>;
205
206 /// If selection intersects this node, return it. Otherwise, returns `None`.
207 fn selection(&self) -> Option<Range<ByteIndex>>;
208
209 /// If this is an image element, returns its URL. If this is not an image element, fails.
210 fn image_url(&self) -> Option<ServoUrl>;
211
212 /// If this is an image element, returns its current-pixel-density. If this is not an image element, fails.
213 fn image_density(&self) -> Option<f64>;
214
215 /// If this is an image element, returns its image data. Otherwise, returns `None`.
216 fn image_data(&self) -> Option<(Option<Image>, Option<ImageMetadata>)>;
217
218 /// Whether or not this is an image element that is showing a broken image icon.
219 fn showing_broken_image_icon(&self) -> bool;
220
221 fn canvas_data(&self) -> Option<HTMLCanvasData>;
222
223 fn svg_data(&self) -> Option<SVGElementData>;
224
225 fn media_data(&self) -> Option<HTMLMediaData>;
226
227 /// If this node is an iframe element, returns its browsing context ID. If this node is
228 /// not an iframe element, fails. Returns None if there is no nested browsing context.
229 fn iframe_browsing_context_id(&self) -> Option<BrowsingContextId>;
230
231 /// If this node is an iframe element, returns its pipeline ID. If this node is
232 /// not an iframe element, fails. Returns None if there is no nested browsing context.
233 fn iframe_pipeline_id(&self) -> Option<PipelineId>;
234
235 fn get_span(&self) -> Option<u32>;
236 fn get_colspan(&self) -> Option<u32>;
237 fn get_rowspan(&self) -> Option<u32>;
238
239 fn pseudo_element_chain(&self) -> PseudoElementChain;
240
241 fn with_pseudo(&self, pseudo_element_type: PseudoElement) -> Option<Self> {
242 self.as_element()
243 .and_then(|element| element.with_pseudo(pseudo_element_type))
244 .as_ref()
245 .map(ThreadSafeLayoutElement::as_node)
246 }
247
248 fn with_pseudo_element_chain(&self, pseudo_element_chain: PseudoElementChain) -> Self;
249
250 /// Set whether or not this node has an active pseudo-element style with a `content`
251 /// attribute that uses `attr`.
252 ///
253 /// # Safety
254 ///
255 /// This function accesses and modifies the underlying DOM object and should
256 /// not be used by more than a single thread at once.
257 fn set_uses_content_attribute_with_attr(&self, _uses_content_attribute_with_attr: bool);
258}
259
260pub trait ThreadSafeLayoutElement<'dom>:
261 Clone + Copy + Sized + Debug + ::selectors::Element<Impl = SelectorImpl>
262{
263 type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'dom, ConcreteThreadSafeLayoutElement = Self>;
264
265 /// This type alias is just a work-around to avoid writing
266 ///
267 /// <Self::ConcreteThreadSafeLayoutNode as ThreadSafeLayoutNode>::ConcreteElement
268 ///
269 type ConcreteElement: TElement;
270
271 fn as_node(&self) -> Self::ConcreteThreadSafeLayoutNode;
272
273 /// Creates a new `ThreadSafeLayoutElement` for the same `LayoutElement`
274 /// with a different pseudo-element type.
275 ///
276 /// Returns `None` if this pseudo doesn't apply to the given element for one of
277 /// the following reasons:
278 ///
279 /// 1. `pseudo` is eager and is not defined in the stylesheet. In this case, there
280 /// is not reason to process the pseudo element at all.
281 /// 2. `pseudo` is for `::servo-details-summary` or `::servo-details-content` and
282 /// it doesn't apply to this element, either because it isn't a details or is
283 /// in the wrong state.
284 fn with_pseudo(&self, pseudo: PseudoElement) -> Option<Self>;
285
286 /// Returns the type ID of this node.
287 /// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
288 fn type_id(&self) -> Option<LayoutNodeType>;
289
290 /// Returns access to the underlying TElement. This is breaks the abstraction
291 /// barrier of ThreadSafeLayout wrapper layer, and can lead to races if not used
292 /// carefully.
293 ///
294 /// We need this so that the functions defined on this trait can call
295 /// lazily_compute_pseudo_element_style, which operates on TElement.
296 fn unsafe_get(self) -> Self::ConcreteElement;
297
298 /// Get the local name of this element. See
299 /// <https://dom.spec.whatwg.org/#concept-element-local-name>.
300 fn get_local_name(&self) -> &LocalName;
301
302 fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str>;
303
304 fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue>;
305
306 fn style_data(&self) -> AtomicRef<'_, ElementData>;
307
308 fn pseudo_element_chain(&self) -> PseudoElementChain;
309
310 /// Returns the style results for the given node. If CSS selector matching
311 /// has not yet been performed, fails.
312 ///
313 /// Unlike the version on TNode, this handles pseudo-elements.
314 #[inline]
315 fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
316 let get_style_for_pseudo_element =
317 |base_style: &Arc<ComputedValues>, pseudo_element: PseudoElement| {
318 // Precompute non-eagerly-cascaded pseudo-element styles if not
319 // cached before.
320 match pseudo_element.cascade_type() {
321 // Already computed during the cascade.
322 PseudoElementCascadeType::Eager => self
323 .style_data()
324 .styles
325 .pseudos
326 .get(&pseudo_element)
327 .unwrap()
328 .clone(),
329 PseudoElementCascadeType::Precomputed => context
330 .stylist
331 .precomputed_values_for_pseudo::<Self::ConcreteElement>(
332 &context.guards,
333 &pseudo_element,
334 Some(base_style),
335 ),
336 PseudoElementCascadeType::Lazy => {
337 context
338 .stylist
339 .lazily_compute_pseudo_element_style(
340 &context.guards,
341 self.unsafe_get(),
342 &pseudo_element,
343 RuleInclusion::All,
344 base_style,
345 /* is_probe = */ false,
346 /* matching_func = */ None,
347 )
348 .unwrap()
349 },
350 }
351 };
352
353 let data = self.style_data();
354 let element_style = data.styles.primary();
355 let pseudo_element_chain = self.pseudo_element_chain();
356
357 let primary_pseudo_style = match pseudo_element_chain.primary {
358 Some(pseudo_element) => get_style_for_pseudo_element(element_style, pseudo_element),
359 None => return element_style.clone(),
360 };
361 match pseudo_element_chain.secondary {
362 Some(pseudo_element) => {
363 get_style_for_pseudo_element(&primary_pseudo_style, pseudo_element)
364 },
365 None => primary_pseudo_style,
366 }
367 }
368
369 fn is_shadow_host(&self) -> bool;
370
371 /// Returns whether this node is a body element of an html element root
372 /// in an HTML element document.
373 ///
374 /// Note that this does require accessing the parent, which this interface
375 /// technically forbids. But accessing the parent is only unsafe insofar as
376 /// it can be used to reach siblings and cousins. A simple immutable borrow
377 /// of the parent data is fine, since the bottom-up traversal will not process
378 /// the parent until all the children have been processed.
379 fn is_body_element_of_html_element_root(&self) -> bool;
380
381 /// Returns whether this node is the root element in an HTML document element.
382 ///
383 /// Note that, like `Self::is_body_element_of_html_element_root`, this accesses the parent.
384 /// As in that case, since this is an immutable borrow, we do not violate thread safety.
385 fn is_root(&self) -> bool;
386}
387
388/// A chain of pseudo-elements up to two levels deep. This is used to represent cases
389/// where a pseudo-element has its own child pseudo element (for instance
390/// `.div::after::marker`). If both [`Self::primary`] and [`Self::secondary`] are `None`,
391/// then this chain represents the element itself. Not all combinations of pseudo-elements
392/// are possible and we may not be able to calculate a style for all
393/// [`PseudoElementChain`]s.
394#[derive(Clone, Copy, Debug, Default, Eq, Hash, MallocSizeOf, PartialEq)]
395pub struct PseudoElementChain {
396 pub primary: Option<PseudoElement>,
397 pub secondary: Option<PseudoElement>,
398}
399
400impl PseudoElementChain {
401 pub fn unnested(pseudo_element: PseudoElement) -> Self {
402 Self {
403 primary: Some(pseudo_element),
404 secondary: None,
405 }
406 }
407
408 pub fn innermost(&self) -> Option<PseudoElement> {
409 self.secondary.or(self.primary)
410 }
411
412 /// Return a possibly nested [`PseudoElementChain`]. Currently only `::before` and
413 /// `::after` only support nesting. If the primary [`PseudoElement`] on the chain is
414 /// not `::before` or `::after` a single element chain is returned for the given
415 /// [`PseudoElement`].
416 pub fn with_pseudo(&self, pseudo_element: PseudoElement) -> Self {
417 match self.primary {
418 Some(primary) if primary.is_before_or_after() => Self {
419 primary: self.primary,
420 secondary: Some(pseudo_element),
421 },
422 _ => {
423 assert!(self.secondary.is_none());
424 Self::unnested(pseudo_element)
425 },
426 }
427 }
428
429 pub fn is_empty(&self) -> bool {
430 self.primary.is_none()
431 }
432}