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;
9use std::ops::Range;
10
11use atomic_refcell::{AtomicRef, AtomicRefCell};
12use base::id::{BrowsingContextId, PipelineId};
13use fonts::TextByteRange;
14use html5ever::{LocalName, Namespace};
15use malloc_size_of_derive::MallocSizeOf;
16use net_traits::image_cache::Image;
17use pixels::ImageMetadata;
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    /// Get the text content of this node, if it is a text node.
205    ///
206    /// Note: This should only be called on text nodes.
207    fn text_content(self) -> Cow<'dom, str>;
208
209    /// If this node manages a selection, this returns the shared selection for the node.
210    fn selection(&self) -> Option<SharedSelection>;
211
212    /// If this is an image element, returns its URL. If this is not an image element, fails.
213    fn image_url(&self) -> Option<ServoUrl>;
214
215    /// If this is an image element, returns its current-pixel-density. If this is not an image element, fails.
216    fn image_density(&self) -> Option<f64>;
217
218    /// If this is an image element, returns its image data. Otherwise, returns `None`.
219    fn image_data(&self) -> Option<(Option<Image>, Option<ImageMetadata>)>;
220
221    /// Whether or not this is an image element that is showing a broken image icon.
222    fn showing_broken_image_icon(&self) -> bool;
223
224    fn canvas_data(&self) -> Option<HTMLCanvasData>;
225
226    fn svg_data(&self) -> Option<SVGElementData<'dom>>;
227
228    fn media_data(&self) -> Option<HTMLMediaData>;
229
230    /// If this node is an iframe element, returns its browsing context ID. If this node is
231    /// not an iframe element, fails. Returns None if there is no nested browsing context.
232    fn iframe_browsing_context_id(&self) -> Option<BrowsingContextId>;
233
234    /// If this node is an iframe element, returns its pipeline ID. If this node is
235    /// not an iframe element, fails. Returns None if there is no nested browsing context.
236    fn iframe_pipeline_id(&self) -> Option<PipelineId>;
237
238    fn get_span(&self) -> Option<u32>;
239    fn get_colspan(&self) -> Option<u32>;
240    fn get_rowspan(&self) -> Option<u32>;
241
242    fn pseudo_element_chain(&self) -> PseudoElementChain;
243
244    fn with_pseudo(&self, pseudo_element_type: PseudoElement) -> Option<Self> {
245        self.as_element()
246            .and_then(|element| element.with_pseudo(pseudo_element_type))
247            .as_ref()
248            .map(ThreadSafeLayoutElement::as_node)
249    }
250
251    fn with_pseudo_element_chain(&self, pseudo_element_chain: PseudoElementChain) -> Self;
252
253    /// Set whether or not this node has an active pseudo-element style with a `content`
254    /// attribute that uses `attr`.
255    ///
256    /// # Safety
257    ///
258    /// This function accesses and modifies the underlying DOM object and should
259    /// not be used by more than a single thread at once.
260    fn set_uses_content_attribute_with_attr(&self, _uses_content_attribute_with_attr: bool);
261}
262
263pub trait ThreadSafeLayoutElement<'dom>:
264    Clone + Copy + Sized + Debug + ::selectors::Element<Impl = SelectorImpl>
265{
266    type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'dom, ConcreteThreadSafeLayoutElement = Self>;
267
268    /// This type alias is just a work-around to avoid writing
269    ///
270    ///   <Self::ConcreteThreadSafeLayoutNode as ThreadSafeLayoutNode>::ConcreteElement
271    ///
272    type ConcreteElement: TElement;
273
274    fn as_node(&self) -> Self::ConcreteThreadSafeLayoutNode;
275
276    /// Creates a new `ThreadSafeLayoutElement` for the same `LayoutElement`
277    /// with a different pseudo-element type.
278    ///
279    /// Returns `None` if this pseudo doesn't apply to the given element for one of
280    /// the following reasons:
281    ///
282    ///  1. `pseudo` is eager and is not defined in the stylesheet. In this case, there
283    ///     is not reason to process the pseudo element at all.
284    ///  2. `pseudo` is for `::servo-details-summary` or `::servo-details-content` and
285    ///     it doesn't apply to this element, either because it isn't a details or is
286    ///     in the wrong state.
287    fn with_pseudo(&self, pseudo: PseudoElement) -> Option<Self>;
288
289    /// Returns the type ID of this node.
290    /// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
291    fn type_id(&self) -> Option<LayoutNodeType>;
292
293    /// Returns access to the underlying TElement. This is breaks the abstraction
294    /// barrier of ThreadSafeLayout wrapper layer, and can lead to races if not used
295    /// carefully.
296    ///
297    /// We need this so that the functions defined on this trait can call
298    /// lazily_compute_pseudo_element_style, which operates on TElement.
299    fn unsafe_get(self) -> Self::ConcreteElement;
300
301    /// Get the local name of this element. See
302    /// <https://dom.spec.whatwg.org/#concept-element-local-name>.
303    fn get_local_name(&self) -> &LocalName;
304
305    fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str>;
306
307    fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue>;
308
309    fn style_data(&self) -> AtomicRef<'_, ElementData>;
310
311    fn pseudo_element_chain(&self) -> PseudoElementChain;
312
313    /// Returns the style results for the given node. If CSS selector matching
314    /// has not yet been performed, fails.
315    ///
316    /// Unlike the version on TNode, this handles pseudo-elements.
317    #[inline]
318    fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
319        let get_style_for_pseudo_element =
320            |base_style: &Arc<ComputedValues>, pseudo_element: PseudoElement| {
321                // Precompute non-eagerly-cascaded pseudo-element styles if not
322                // cached before.
323                match pseudo_element.cascade_type() {
324                    // Already computed during the cascade.
325                    PseudoElementCascadeType::Eager => self
326                        .style_data()
327                        .styles
328                        .pseudos
329                        .get(&pseudo_element)
330                        .unwrap()
331                        .clone(),
332                    PseudoElementCascadeType::Precomputed => context
333                        .stylist
334                        .precomputed_values_for_pseudo::<Self::ConcreteElement>(
335                            &context.guards,
336                            &pseudo_element,
337                            Some(base_style),
338                        ),
339                    PseudoElementCascadeType::Lazy => {
340                        context
341                            .stylist
342                            .lazily_compute_pseudo_element_style(
343                                &context.guards,
344                                self.unsafe_get(),
345                                &pseudo_element,
346                                RuleInclusion::All,
347                                base_style,
348                                /* is_probe = */ false,
349                                /* matching_func = */ None,
350                            )
351                            .unwrap()
352                    },
353                }
354            };
355
356        let data = self.style_data();
357        let element_style = data.styles.primary();
358        let pseudo_element_chain = self.pseudo_element_chain();
359
360        let primary_pseudo_style = match pseudo_element_chain.primary {
361            Some(pseudo_element) => get_style_for_pseudo_element(element_style, pseudo_element),
362            None => return element_style.clone(),
363        };
364        match pseudo_element_chain.secondary {
365            Some(pseudo_element) => {
366                get_style_for_pseudo_element(&primary_pseudo_style, pseudo_element)
367            },
368            None => primary_pseudo_style,
369        }
370    }
371
372    fn is_shadow_host(&self) -> bool;
373
374    /// Returns whether this node is a body element of an html element root
375    /// in an HTML element document.
376    ///
377    /// Note that this does require accessing the parent, which this interface
378    /// technically forbids. But accessing the parent is only unsafe insofar as
379    /// it can be used to reach siblings and cousins. A simple immutable borrow
380    /// of the parent data is fine, since the bottom-up traversal will not process
381    /// the parent until all the children have been processed.
382    fn is_body_element_of_html_element_root(&self) -> bool;
383
384    /// Returns whether this node is the root element in an HTML document element.
385    ///
386    /// Note that, like `Self::is_body_element_of_html_element_root`, this accesses the parent.
387    /// As in that case, since this is an immutable borrow, we do not violate thread safety.
388    fn is_root(&self) -> bool;
389}
390
391/// A chain of pseudo-elements up to two levels deep. This is used to represent cases
392/// where a pseudo-element has its own child pseudo element (for instance
393/// `.div::after::marker`). If both [`Self::primary`] and [`Self::secondary`] are `None`,
394/// then this chain represents the element itself. Not all combinations of pseudo-elements
395/// are possible and we may not be able to calculate a style for all
396/// [`PseudoElementChain`]s.
397#[derive(Clone, Copy, Debug, Default, Eq, Hash, MallocSizeOf, PartialEq)]
398pub struct PseudoElementChain {
399    pub primary: Option<PseudoElement>,
400    pub secondary: Option<PseudoElement>,
401}
402
403impl PseudoElementChain {
404    pub fn unnested(pseudo_element: PseudoElement) -> Self {
405        Self {
406            primary: Some(pseudo_element),
407            secondary: None,
408        }
409    }
410
411    pub fn innermost(&self) -> Option<PseudoElement> {
412        self.secondary.or(self.primary)
413    }
414
415    /// Return a possibly nested [`PseudoElementChain`]. Currently only `::before` and
416    /// `::after` only support nesting. If the primary [`PseudoElement`] on the chain is
417    /// not `::before` or `::after` a single element chain is returned for the given
418    /// [`PseudoElement`].
419    pub fn with_pseudo(&self, pseudo_element: PseudoElement) -> Self {
420        match self.primary {
421            Some(primary) if primary.is_before_or_after() => Self {
422                primary: self.primary,
423                secondary: Some(pseudo_element),
424            },
425            _ => {
426                assert!(self.secondary.is_none());
427                Self::unnested(pseudo_element)
428            },
429        }
430    }
431
432    pub fn is_empty(&self) -> bool {
433        self.primary.is_none()
434    }
435}
436
437/// A selection shared between script and layout. This selection is managed by the DOM
438/// node that maintains it, and can be modified from script. Once modified, layout is
439/// expected to reflect the new selection visual on the next display list update.
440#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq)]
441pub struct ScriptSelection {
442    /// The range of this selection in the DOM node that manages it.
443    pub range: TextByteRange,
444    /// The character range of this selection in the DOM node that manages it.
445    pub character_range: Range<usize>,
446    /// Whether or not this selection is enabled. Selections may be disabled
447    /// when their node loses focus.
448    pub enabled: bool,
449}
450
451pub type SharedSelection = std::sync::Arc<AtomicRefCell<ScriptSelection>>;