script/layout_dom/
servo_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;
9use std::hash::Hash;
10
11use html5ever::{LocalName, Namespace, local_name, ns};
12use layout_api::{
13    LayoutDataTrait, LayoutElement, LayoutNode, LayoutNodeType, PseudoElementChain, StyleData,
14};
15use servo_arc::Arc;
16use style::attr::AttrValue;
17use style::context::SharedStyleContext;
18use style::data::{ElementDataMut, ElementDataRef};
19use style::properties::ComputedValues;
20use style::selector_parser::{PseudoElement, PseudoElementCascadeType};
21use style::stylist::RuleInclusion;
22
23use crate::dom::bindings::root::LayoutDom;
24use crate::dom::element::Element;
25use crate::dom::node::{Node, NodeFlags};
26use crate::layout_dom::{
27    ServoDangerousStyleElement, ServoDangerousStyleShadowRoot, ServoLayoutNode,
28};
29
30impl fmt::Debug for LayoutDom<'_, Element> {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        write!(f, "<{}", self.local_name())?;
33        if let Some(id) = unsafe { (*self.id_attribute()).as_ref() } {
34            write!(f, " id={id}")?;
35        }
36        write!(f, "> ({:#x})", self.upcast::<Node>().opaque().0)
37    }
38}
39
40/// An implementation of [`LayoutElement`] for Servo's `script` crate.
41#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
42pub struct ServoLayoutElement<'dom> {
43    /// The wrapped private DOM Element.
44    pub(super) element: LayoutDom<'dom, Element>,
45    /// The possibly nested [`PseudoElementChain`] for this element.
46    pub(super) pseudo_element_chain: PseudoElementChain,
47}
48
49unsafe impl Send for ServoLayoutElement<'_> {}
50unsafe impl Sync for ServoLayoutElement<'_> {}
51
52impl<'dom> ServoLayoutElement<'dom> {
53    pub(super) fn is_html_element(&self) -> bool {
54        self.element.is_html_element()
55    }
56
57    /// The shadow root that this [`ServoLayoutElement`] is a host of, if it has one.
58    ///
59    /// Note: This should *not* be exposed to layout as it allows access to an ancestor element.
60    pub(super) fn shadow_root(&self) -> Option<ServoDangerousStyleShadowRoot<'dom>> {
61        self.element.get_shadow_root_for_layout().map(Into::into)
62    }
63}
64
65impl<'dom> From<LayoutDom<'dom, Element>> for ServoLayoutElement<'dom> {
66    fn from(element: LayoutDom<'dom, Element>) -> Self {
67        Self {
68            element,
69            pseudo_element_chain: Default::default(),
70        }
71    }
72}
73
74impl<'dom> LayoutElement<'dom> for ServoLayoutElement<'dom> {
75    type ConcreteLayoutNode = ServoLayoutNode<'dom>;
76    type ConcreteStyleElement = ServoDangerousStyleElement<'dom>;
77
78    fn with_pseudo(&self, pseudo_element: PseudoElement) -> Option<Self> {
79        if pseudo_element.is_eager() &&
80            self.element_data()
81                .styles
82                .pseudos
83                .get(&pseudo_element)
84                .is_none()
85        {
86            return None;
87        }
88
89        if pseudo_element == PseudoElement::DetailsContent &&
90            (self.element.local_name() != &local_name!("details") ||
91                self.element.namespace() != &ns!(html) ||
92                self.attribute(&ns!(), &local_name!("open")).is_none())
93        {
94            return None;
95        }
96
97        // These pseudo-element type cannot be nested.
98        if !self.pseudo_element_chain.is_empty() {
99            assert!(!pseudo_element.is_eager());
100            assert!(pseudo_element != PseudoElement::DetailsContent);
101        }
102
103        Some(Self {
104            element: self.element,
105            pseudo_element_chain: self.pseudo_element_chain.with_pseudo(pseudo_element),
106        })
107    }
108
109    fn pseudo_element_chain(&self) -> PseudoElementChain {
110        self.pseudo_element_chain
111    }
112
113    fn as_node(&self) -> ServoLayoutNode<'dom> {
114        ServoLayoutNode {
115            node: self.element.upcast(),
116            pseudo_element_chain: self.pseudo_element_chain,
117        }
118    }
119
120    fn initialize_style_and_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self) {
121        if self.element.style_data().is_none() {
122            unsafe { self.element.initialize_style_data() };
123        }
124
125        let node = self.element.upcast::<Node>();
126        if node.layout_data().is_none() {
127            unsafe { node.initialize_layout_data(Box::<RequestedLayoutDataType>::default()) };
128        }
129    }
130
131    fn unset_snapshot_flags(&self) {
132        unsafe {
133            self.as_node()
134                .node
135                .set_flag(NodeFlags::HAS_SNAPSHOT | NodeFlags::HANDLED_SNAPSHOT, false);
136        }
137    }
138
139    fn set_has_snapshot(&self) {
140        unsafe {
141            self.as_node().node.set_flag(NodeFlags::HAS_SNAPSHOT, true);
142        }
143    }
144
145    fn style_data(self) -> Option<&'dom StyleData> {
146        self.element.style_data()
147    }
148
149    fn element_data(&self) -> ElementDataRef<'dom> {
150        self.style_data()
151            .expect("Unstyled layout node?")
152            .element_data
153            .borrow()
154    }
155
156    fn element_data_mut(&self) -> ElementDataMut<'dom> {
157        self.style_data()
158            .expect("Unstyled layout node?")
159            .element_data
160            .borrow_mut()
161    }
162
163    fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
164        let get_style_for_pseudo_element =
165            |data: &ElementDataRef<'_>,
166             base_style: &Arc<ComputedValues>,
167             pseudo_element: PseudoElement| {
168                // Precompute non-eagerly-cascaded pseudo-element styles if not
169                // cached before.
170                match pseudo_element.cascade_type() {
171                    // Already computed during the cascade.
172                    PseudoElementCascadeType::Eager => {
173                        data.styles.pseudos.get(&pseudo_element).unwrap().clone()
174                    },
175                    PseudoElementCascadeType::Precomputed => context
176                        .stylist
177                        .precomputed_values_for_pseudo::<Self::ConcreteStyleElement>(
178                        &context.guards,
179                        &pseudo_element,
180                        Some(base_style),
181                    ),
182                    PseudoElementCascadeType::Lazy => {
183                        context
184                            .stylist
185                            .lazily_compute_pseudo_element_style(
186                                &context.guards,
187                                unsafe { self.dangerous_style_element() },
188                                &pseudo_element,
189                                RuleInclusion::All,
190                                base_style,
191                                /* is_probe = */ false,
192                                /* matching_func = */ None,
193                            )
194                            .unwrap()
195                    },
196                }
197            };
198
199        let data = self.element_data();
200        let element_style = data.styles.primary();
201        let pseudo_element_chain = self.pseudo_element_chain();
202
203        let primary_pseudo_style = match pseudo_element_chain.primary {
204            Some(pseudo_element) => {
205                get_style_for_pseudo_element(&data, element_style, pseudo_element)
206            },
207            None => return element_style.clone(),
208        };
209        match pseudo_element_chain.secondary {
210            Some(pseudo_element) => {
211                get_style_for_pseudo_element(&data, &primary_pseudo_style, pseudo_element)
212            },
213            None => primary_pseudo_style,
214        }
215    }
216
217    fn type_id(&self) -> Option<LayoutNodeType> {
218        self.as_node().type_id()
219    }
220
221    unsafe fn dangerous_style_element(self) -> ServoDangerousStyleElement<'dom> {
222        self.element.into()
223    }
224
225    fn local_name(&self) -> &LocalName {
226        self.element.local_name()
227    }
228
229    fn attribute(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
230        self.element.get_attr_for_layout(namespace, name)
231    }
232
233    fn attribute_as_str<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str> {
234        self.element.get_attr_val_for_layout(namespace, name)
235    }
236
237    fn is_shadow_host(&self) -> bool {
238        self.element.get_shadow_root_for_layout().is_some()
239    }
240
241    fn is_body_element_of_html_element_root(&self) -> bool {
242        self.element.is_body_element_of_html_element_root()
243    }
244
245    fn is_html_element_in_html_document(&self) -> bool {
246        self.element.is_html_element() &&
247            self.element
248                .upcast::<Node>()
249                .owner_doc_for_layout()
250                .is_html_document_for_layout()
251    }
252
253    fn is_root(&self) -> bool {
254        self.element.is_root()
255    }
256}