Skip to main content

script/dom/node/
layout_dom.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//! Methods for layout of node
6
7use std::borrow::Cow;
8
9use layout_api::{
10    GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutElementType, LayoutNodeType,
11    SVGElementData, SharedSelection,
12};
13use net_traits::image_cache::Image;
14use pixels::ImageMetadata;
15use script_bindings::codegen::InheritTypes::{
16    ElementTypeId, HTMLElementTypeId, SVGElementTypeId, SVGGraphicsElementTypeId,
17};
18use servo_base::id::{BrowsingContextId, PipelineId};
19use servo_url::ServoUrl;
20use style::dom::OpaqueNode;
21use style::selector_parser::PseudoElement;
22
23use crate::dom::bindings::inheritance::{CharacterDataTypeId, NodeTypeId};
24use crate::dom::bindings::root::{LayoutDom, ToLayout};
25use crate::dom::document::Document;
26use crate::dom::element::Element;
27use crate::dom::html::htmlcanvaselement::HTMLCanvasElement;
28use crate::dom::html::htmliframeelement::HTMLIFrameElement;
29use crate::dom::html::htmlimageelement::HTMLImageElement;
30use crate::dom::html::htmlslotelement::HTMLSlotElement;
31use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
32use crate::dom::html::htmlvideoelement::HTMLVideoElement;
33use crate::dom::html::input_element::HTMLInputElement;
34use crate::dom::shadowroot::ShadowRoot;
35use crate::dom::svg::svgsvgelement::SVGSVGElement;
36use crate::dom::text::Text;
37use crate::dom::{Node, NodeFlags};
38
39impl<'dom> LayoutDom<'dom, Node> {
40    #[inline]
41    #[expect(unsafe_code)]
42    pub(crate) fn parent_node_ref(self) -> Option<LayoutDom<'dom, Node>> {
43        unsafe { self.unsafe_get().parent_node().get_inner_as_layout() }
44    }
45
46    #[inline]
47    pub(crate) fn type_id_for_layout(self) -> NodeTypeId {
48        self.unsafe_get().type_id()
49    }
50
51    #[inline]
52    pub(crate) fn is_element_for_layout(&self) -> bool {
53        (*self).is::<Element>()
54    }
55
56    pub(crate) fn is_text_node_for_layout(&self) -> bool {
57        matches!(
58            self.type_id_for_layout(),
59            NodeTypeId::CharacterData(CharacterDataTypeId::Text(..))
60        )
61    }
62
63    #[inline]
64    pub(crate) fn composed_parent_node_ref(self) -> Option<LayoutDom<'dom, Node>> {
65        let parent = self.parent_node_ref();
66        if let Some(parent) = parent &&
67            let Some(shadow_root) = parent.downcast::<ShadowRoot>()
68        {
69            return Some(shadow_root.get_host_for_layout().upcast());
70        }
71        parent
72    }
73
74    #[inline]
75    pub(crate) fn traversal_parent(self) -> Option<LayoutDom<'dom, Element>> {
76        if let Some(assigned_slot) = self.assigned_slot_for_layout() {
77            return Some(assigned_slot.upcast());
78        }
79        let parent = self.parent_node_ref()?;
80        if let Some(shadow) = parent.downcast::<ShadowRoot>() {
81            return Some(shadow.get_host_for_layout());
82        };
83        parent.downcast()
84    }
85
86    #[inline]
87    #[expect(unsafe_code)]
88    pub(crate) fn first_child_ref(self) -> Option<LayoutDom<'dom, Node>> {
89        unsafe { self.unsafe_get().first_child().get_inner_as_layout() }
90    }
91
92    #[inline]
93    #[expect(unsafe_code)]
94    pub(crate) fn last_child_ref(self) -> Option<LayoutDom<'dom, Node>> {
95        unsafe { self.unsafe_get().last_child().get_inner_as_layout() }
96    }
97
98    #[inline]
99    #[expect(unsafe_code)]
100    pub(crate) fn prev_sibling_ref(self) -> Option<LayoutDom<'dom, Node>> {
101        unsafe { self.unsafe_get().prev_sibling().get_inner_as_layout() }
102    }
103
104    #[inline]
105    #[expect(unsafe_code)]
106    pub(crate) fn next_sibling_ref(self) -> Option<LayoutDom<'dom, Node>> {
107        unsafe { self.unsafe_get().next_sibling().get_inner_as_layout() }
108    }
109
110    #[inline]
111    #[expect(unsafe_code)]
112    pub(crate) fn owner_doc_for_layout(self) -> LayoutDom<'dom, Document> {
113        unsafe {
114            self.unsafe_get()
115                .get_owner_doc()
116                .get_inner_as_layout()
117                .unwrap()
118        }
119    }
120
121    #[inline]
122    #[expect(unsafe_code)]
123    pub(crate) fn containing_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>> {
124        unsafe {
125            self.unsafe_get()
126                .get_rare_data()
127                .borrow_for_layout()
128                .as_ref()?
129                .containing_shadow_root
130                .as_ref()
131                .map(|sr| sr.to_layout())
132        }
133    }
134
135    #[inline]
136    #[expect(unsafe_code)]
137    pub(crate) fn assigned_slot_for_layout(self) -> Option<LayoutDom<'dom, HTMLSlotElement>> {
138        unsafe {
139            self.unsafe_get()
140                .get_rare_data()
141                .borrow_for_layout()
142                .as_ref()?
143                .slottable_data
144                .assigned_slot
145                .as_ref()
146                .map(|assigned_slot| assigned_slot.to_layout())
147        }
148    }
149
150    // FIXME(nox): get_flag/set_flag (especially the latter) are not safe because
151    // they mutate stuff while values of this type can be used from multiple
152    // threads at once, this should be revisited.
153
154    #[inline]
155    #[expect(unsafe_code)]
156    pub(crate) unsafe fn get_flag(self, flag: NodeFlags) -> bool {
157        (self.unsafe_get()).flags().get().contains(flag)
158    }
159
160    #[inline]
161    #[expect(unsafe_code)]
162    pub(crate) unsafe fn set_flag(self, flag: NodeFlags, value: bool) {
163        let this = self.unsafe_get();
164        let mut flags = (this).flags().get();
165
166        if value {
167            flags.insert(flag);
168        } else {
169            flags.remove(flag);
170        }
171
172        (this).flags().set(flags);
173    }
174
175    #[inline]
176    #[expect(unsafe_code)]
177    pub(crate) fn layout_data(self) -> Option<&'dom GenericLayoutData> {
178        unsafe {
179            self.unsafe_get()
180                .layout_data()
181                .borrow_for_layout()
182                .as_deref()
183        }
184    }
185
186    /// Initialize the style data of this node.
187    ///
188    /// # Safety
189    ///
190    /// This method is unsafe because it modifies the given node during
191    /// layout. Callers should ensure that no other layout thread is
192    /// attempting to read or modify the opaque layout data of this node.
193    #[inline]
194    #[expect(unsafe_code)]
195    pub(crate) unsafe fn initialize_layout_data(self, new_data: Box<GenericLayoutData>) {
196        let data = unsafe { self.unsafe_get().layout_data().borrow_mut_for_layout() };
197        debug_assert!(data.is_none());
198        *data = Some(new_data);
199    }
200
201    /// Clear the style and opaque layout data of this node.
202    ///
203    /// # Safety
204    ///
205    /// This method is unsafe because it modifies the given node during
206    /// layout. Callers should ensure that no other layout thread is
207    /// attempting to read or modify the opaque layout data of this node.
208    #[inline]
209    #[expect(unsafe_code)]
210    pub(crate) unsafe fn clear_layout_data(self) {
211        unsafe {
212            self.unsafe_get()
213                .layout_data()
214                .borrow_mut_for_layout()
215                .take();
216        }
217    }
218
219    /// Whether this element serve as a container of editable text for a text input
220    /// that is implemented as an UA widget.
221    pub(crate) fn is_single_line_text_inner_editor(&self) -> bool {
222        matches!(
223            self.implemented_pseudo_element(),
224            Some(PseudoElement::ServoTextControlInnerEditor)
225        )
226    }
227
228    /// Whether this element serve as a container of any text inside a text input
229    /// that is implemented as an UA widget.
230    pub(crate) fn is_text_container_of_single_line_input(&self) -> bool {
231        let is_single_line_text_inner_placeholder = matches!(
232            self.implemented_pseudo_element(),
233            Some(PseudoElement::Placeholder)
234        );
235        // Currently `::placeholder` is only implemented for single line text input element.
236        debug_assert!(
237            !is_single_line_text_inner_placeholder ||
238                self.containing_shadow_root_for_layout()
239                    .map(|root| root.get_host_for_layout())
240                    .map(|host| host.downcast::<HTMLInputElement>())
241                    .is_some()
242        );
243
244        self.is_single_line_text_inner_editor() || is_single_line_text_inner_placeholder
245    }
246
247    pub(crate) fn text_content(self) -> Cow<'dom, str> {
248        self.downcast::<Text>()
249            .expect("Called LayoutDom::text_content on non-Text node!")
250            .upcast()
251            .data_for_layout()
252            .into()
253    }
254
255    /// Get the selection for the given node. This only works for text nodes that are in
256    /// the shadow DOM of user agent widgets for form controls, specifically for `<input>`
257    /// and `<textarea>`.
258    ///
259    /// As we want to expose the selection on the inner text node of the widget's shadow
260    /// DOM, we must find the shadow root and then access the containing element itself.
261    pub(crate) fn selection(self) -> Option<SharedSelection> {
262        if let Some(input) = self.downcast::<HTMLInputElement>() {
263            return input.selection_for_layout();
264        }
265        if let Some(textarea) = self.downcast::<HTMLTextAreaElement>() {
266            return Some(textarea.selection_for_layout());
267        }
268
269        let shadow_root = self
270            .containing_shadow_root_for_layout()?
271            .get_host_for_layout();
272        if let Some(input) = shadow_root.downcast::<HTMLInputElement>() {
273            return input.selection_for_layout();
274        }
275        shadow_root
276            .downcast::<HTMLTextAreaElement>()
277            .map(|textarea| textarea.selection_for_layout())
278    }
279
280    pub(crate) fn image_url(self) -> Option<ServoUrl> {
281        self.downcast::<HTMLImageElement>()
282            .expect("not an image!")
283            .image_url()
284    }
285
286    pub(crate) fn image_data(self) -> Option<(Option<Image>, Option<ImageMetadata>)> {
287        self.downcast::<HTMLImageElement>().map(|e| e.image_data())
288    }
289
290    pub(crate) fn image_density(self) -> Option<f64> {
291        self.downcast::<HTMLImageElement>()
292            .expect("not an image!")
293            .image_density()
294    }
295
296    pub(crate) fn showing_broken_image_icon(self) -> bool {
297        self.downcast::<HTMLImageElement>()
298            .map(|image_element| image_element.showing_broken_image_icon())
299            .unwrap_or_default()
300    }
301
302    pub(crate) fn canvas_data(self) -> Option<HTMLCanvasData> {
303        self.downcast::<HTMLCanvasElement>()
304            .map(|canvas| canvas.data())
305    }
306
307    pub(crate) fn media_data(self) -> Option<HTMLMediaData> {
308        self.downcast::<HTMLVideoElement>()
309            .map(|media| media.data())
310    }
311
312    pub(crate) fn svg_data(self) -> Option<SVGElementData<'dom>> {
313        self.downcast::<SVGSVGElement>().map(|svg| svg.data())
314    }
315
316    pub(crate) fn iframe_browsing_context_id(self) -> Option<BrowsingContextId> {
317        self.downcast::<HTMLIFrameElement>()
318            .and_then(|iframe_element| iframe_element.browsing_context_id())
319    }
320
321    pub(crate) fn iframe_pipeline_id(self) -> Option<PipelineId> {
322        self.downcast::<HTMLIFrameElement>()
323            .and_then(|iframe_element| iframe_element.pipeline_id())
324    }
325
326    #[expect(unsafe_code)]
327    pub(crate) fn opaque(self) -> OpaqueNode {
328        unsafe { OpaqueNode(self.get_jsobject() as usize) }
329    }
330
331    #[expect(unsafe_code)]
332    pub(crate) fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
333        unsafe {
334            self.unsafe_get()
335                .get_rare_data()
336                .borrow_for_layout()
337                .as_ref()
338                .and_then(|rare_data| rare_data.implemented_pseudo_element)
339        }
340    }
341
342    pub(crate) fn is_in_ua_widget(&self) -> bool {
343        self.unsafe_get().is_in_ua_widget()
344    }
345
346    pub(crate) fn is_root_of_user_agent_widget(&self) -> bool {
347        self.downcast::<Element>().is_some_and(|element| {
348            element
349                .get_shadow_root_for_layout()
350                .is_some_and(|shadow_root| shadow_root.is_user_agent_widget())
351        })
352    }
353}
354
355pub(crate) struct NodeTypeIdWrapper(pub(crate) NodeTypeId);
356
357impl From<NodeTypeIdWrapper> for LayoutNodeType {
358    #[inline(always)]
359    fn from(node_type: NodeTypeIdWrapper) -> LayoutNodeType {
360        match node_type.0 {
361            NodeTypeId::Element(e) => LayoutNodeType::Element(ElementTypeIdWrapper(e).into()),
362            NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => LayoutNodeType::Text,
363            x => unreachable!("Layout should not traverse nodes of type {:?}", x),
364        }
365    }
366}
367
368struct ElementTypeIdWrapper(ElementTypeId);
369
370impl From<ElementTypeIdWrapper> for LayoutElementType {
371    #[inline(always)]
372    fn from(element_type: ElementTypeIdWrapper) -> LayoutElementType {
373        match element_type.0 {
374            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement) => {
375                LayoutElementType::HTMLBodyElement
376            },
377            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBRElement) => {
378                LayoutElementType::HTMLBRElement
379            },
380            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement) => {
381                LayoutElementType::HTMLCanvasElement
382            },
383            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLHtmlElement) => {
384                LayoutElementType::HTMLHtmlElement
385            },
386            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement) => {
387                LayoutElementType::HTMLIFrameElement
388            },
389            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement) => {
390                LayoutElementType::HTMLImageElement
391            },
392            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLMediaElement(_)) => {
393                LayoutElementType::HTMLMediaElement
394            },
395            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement) => {
396                LayoutElementType::HTMLInputElement
397            },
398            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptGroupElement) => {
399                LayoutElementType::HTMLOptGroupElement
400            },
401            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptionElement) => {
402                LayoutElementType::HTMLOptionElement
403            },
404            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement) => {
405                LayoutElementType::HTMLObjectElement
406            },
407            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLParagraphElement) => {
408                LayoutElementType::HTMLParagraphElement
409            },
410            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLPreElement) => {
411                LayoutElementType::HTMLPreElement
412            },
413            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement) => {
414                LayoutElementType::HTMLSelectElement
415            },
416            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableCellElement) => {
417                LayoutElementType::HTMLTableCellElement
418            },
419            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableColElement) => {
420                LayoutElementType::HTMLTableColElement
421            },
422            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement) => {
423                LayoutElementType::HTMLTableElement
424            },
425            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement) => {
426                LayoutElementType::HTMLTableRowElement
427            },
428            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableSectionElement) => {
429                LayoutElementType::HTMLTableSectionElement
430            },
431            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement) => {
432                LayoutElementType::HTMLTextAreaElement
433            },
434            ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
435                SVGGraphicsElementTypeId::SVGImageElement,
436            )) => LayoutElementType::SVGImageElement,
437            ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
438                SVGGraphicsElementTypeId::SVGSVGElement,
439            )) => LayoutElementType::SVGSVGElement,
440            _ => LayoutElementType::Element,
441        }
442    }
443}