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 atomic_refcell::AtomicRef;
8use layout_api::{
9    GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutElementType, LayoutNodeType,
10    SVGElementData, SharedSelection,
11};
12use net_traits::image_cache::Image;
13use pixels::ImageMetadata;
14use script_bindings::codegen::InheritTypes::{
15    ElementTypeId, HTMLElementTypeId, SVGElementTypeId, SVGGraphicsElementTypeId,
16};
17use servo_base::id::{BrowsingContextId, PipelineId};
18use servo_base::text::{RangeAny, Utf32CodeUnits};
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, ToLayoutOptional};
25use crate::dom::document::Document;
26use crate::dom::element::Element;
27use crate::dom::html::form_controls::htmlinputelement::HTMLInputElement;
28use crate::dom::html::htmlcanvaselement::HTMLCanvasElement;
29use crate::dom::html::htmliframeelement::HTMLIFrameElement;
30use crate::dom::html::htmlimageelement::HTMLImageElement;
31use crate::dom::html::htmlslotelement::HTMLSlotElement;
32use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
33use crate::dom::html::htmlvideoelement::HTMLVideoElement;
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().to_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().to_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().to_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().to_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().to_layout() }
108    }
109
110    #[inline]
111    #[expect(unsafe_code)]
112    pub(crate) fn owner_doc_for_layout(self) -> LayoutDom<'dom, Document> {
113        unsafe { self.unsafe_get().get_owner_doc().to_layout().unwrap() }
114    }
115
116    #[inline]
117    #[expect(unsafe_code)]
118    pub(crate) fn containing_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>> {
119        unsafe {
120            self.unsafe_get()
121                .get_rare_data()
122                .borrow_for_layout()
123                .as_ref()?
124                .containing_shadow_root
125                .as_ref()
126                .map(|sr| sr.to_layout())
127        }
128    }
129
130    #[inline]
131    #[expect(unsafe_code)]
132    pub(crate) fn assigned_slot_for_layout(self) -> Option<LayoutDom<'dom, HTMLSlotElement>> {
133        unsafe {
134            self.unsafe_get()
135                .get_rare_data()
136                .borrow_for_layout()
137                .as_ref()?
138                .slottable_data
139                .assigned_slot
140                .as_ref()
141                .map(|assigned_slot| assigned_slot.to_layout())
142        }
143    }
144
145    // FIXME(nox): get_flag/set_flag (especially the latter) are not safe because
146    // they mutate stuff while values of this type can be used from multiple
147    // threads at once, this should be revisited.
148
149    #[inline]
150    #[expect(unsafe_code)]
151    pub(crate) unsafe fn get_flag(self, flag: NodeFlags) -> bool {
152        (self.unsafe_get()).flags().get().contains(flag)
153    }
154
155    #[inline]
156    #[expect(unsafe_code)]
157    pub(crate) unsafe fn set_flag(self, flag: NodeFlags, value: bool) {
158        let this = self.unsafe_get();
159        let mut flags = (this).flags().get();
160
161        if value {
162            flags.insert(flag);
163        } else {
164            flags.remove(flag);
165        }
166
167        (this).flags().set(flags);
168    }
169
170    #[inline]
171    #[expect(unsafe_code)]
172    pub(crate) fn layout_data(self) -> Option<&'dom GenericLayoutData> {
173        unsafe {
174            self.unsafe_get()
175                .layout_data()
176                .borrow_for_layout()
177                .as_deref()
178        }
179    }
180
181    /// Initialize the style data of this node.
182    ///
183    /// # Safety
184    ///
185    /// This method is unsafe because it modifies the given node during
186    /// layout. Callers should ensure that no other layout thread is
187    /// attempting to read or modify the opaque layout data of this node.
188    #[inline]
189    #[expect(unsafe_code)]
190    pub(crate) unsafe fn initialize_layout_data(self, new_data: Box<GenericLayoutData>) {
191        let data = unsafe { self.unsafe_get().layout_data().borrow_mut_for_layout() };
192        debug_assert!(data.is_none());
193        *data = Some(new_data);
194    }
195
196    /// Clear the style and opaque layout data of this node.
197    ///
198    /// # Safety
199    ///
200    /// This method is unsafe because it modifies the given node during
201    /// layout. Callers should ensure that no other layout thread is
202    /// attempting to read or modify the opaque layout data of this node.
203    #[inline]
204    #[expect(unsafe_code)]
205    pub(crate) unsafe fn clear_layout_data(self) {
206        unsafe {
207            self.unsafe_get()
208                .layout_data()
209                .borrow_mut_for_layout()
210                .take();
211        }
212    }
213
214    /// Whether this element serve as a container of editable text for a text input
215    /// that is implemented as an UA widget.
216    pub(crate) fn is_single_line_text_inner_editor(&self) -> bool {
217        matches!(
218            self.implemented_pseudo_element(),
219            Some(PseudoElement::ServoTextControlInnerEditor)
220        )
221    }
222
223    /// Whether this element serve as a container of any text inside a text input
224    /// that is implemented as an UA widget.
225    pub(crate) fn is_text_container_of_single_line_input(&self) -> bool {
226        let is_single_line_text_inner_placeholder = matches!(
227            self.implemented_pseudo_element(),
228            Some(PseudoElement::Placeholder)
229        );
230        // Currently `::placeholder` is only implemented for single line text input element.
231        debug_assert!(
232            !is_single_line_text_inner_placeholder ||
233                self.containing_shadow_root_for_layout()
234                    .map(|root| root.get_host_for_layout())
235                    .map(|host| host.downcast::<HTMLInputElement>())
236                    .is_some()
237        );
238
239        self.is_single_line_text_inner_editor() || is_single_line_text_inner_placeholder
240    }
241
242    pub(crate) fn text_content(self) -> AtomicRef<'dom, str> {
243        self.downcast::<Text>()
244            .expect("Called LayoutDom::text_content on non-Text node!")
245            .upcast()
246            .data_for_layout()
247    }
248
249    #[expect(unsafe_code)]
250    pub(crate) fn document_selection_in_text_node(&self) -> Option<RangeAny<Utf32CodeUnits>> {
251        let unsafe_self = self.unsafe_get();
252        if !unsafe_self.get_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION) {
253            return None;
254        }
255
256        let text_node = self.downcast::<Text>()?;
257        let text = text_node.upcast().data_for_layout();
258        let range = self
259            .owner_doc_for_layout()
260            .selection_for_layout()?
261            .range_for_layout()?
262            .unsafe_get();
263
264        let range_start = range.start();
265        let range_end = range.end();
266
267        // Text nodes are always the same node when projected into the flat tree, so
268        // it is fine to do the following check against the original unprojected nodes.
269        let is_start_node = unsafe { range_start.node().to_layout() } == *self;
270        let is_end_node = unsafe { range_end.node().to_layout() } == *self;
271
272        let start = is_start_node.then(|| range_start.offset().to_utf32_code_units_in(&text));
273        let end = is_end_node.then(|| range_end.offset().to_utf32_code_units_in(&text));
274        Some(RangeAny { start, end })
275    }
276
277    /// Get the selection for the given node. This only works for text nodes that are in
278    /// the shadow DOM of user agent widgets for form controls, specifically for `<input>`
279    /// and `<textarea>`.
280    ///
281    /// As we want to expose the selection on the inner text node of the widget's shadow
282    /// DOM, we must find the shadow root and then access the containing element itself.
283    pub(crate) fn form_control_selection_in_text_node(self) -> Option<SharedSelection> {
284        let shadow_root = self
285            .containing_shadow_root_for_layout()?
286            .get_host_for_layout();
287        if let Some(input) = shadow_root.downcast::<HTMLInputElement>() {
288            return input.selection_for_layout();
289        }
290        shadow_root
291            .downcast::<HTMLTextAreaElement>()
292            .map(|textarea| textarea.selection_for_layout())
293    }
294
295    pub(crate) fn image_url(self) -> Option<ServoUrl> {
296        self.downcast::<HTMLImageElement>()
297            .expect("not an image!")
298            .image_url()
299    }
300
301    pub(crate) fn image_data(self) -> Option<(Option<Image>, Option<ImageMetadata>)> {
302        self.downcast::<HTMLImageElement>().map(|e| e.image_data())
303    }
304
305    pub(crate) fn image_density(self) -> Option<f64> {
306        self.downcast::<HTMLImageElement>()
307            .expect("not an image!")
308            .image_density()
309    }
310
311    pub(crate) fn showing_broken_image_icon(self) -> bool {
312        self.downcast::<HTMLImageElement>()
313            .map(|image_element| image_element.showing_broken_image_icon())
314            .unwrap_or_default()
315    }
316
317    pub(crate) fn canvas_data(self) -> Option<HTMLCanvasData> {
318        self.downcast::<HTMLCanvasElement>()
319            .map(|canvas| canvas.data())
320    }
321
322    pub(crate) fn media_data(self) -> Option<HTMLMediaData> {
323        self.downcast::<HTMLVideoElement>()
324            .map(|media| media.data())
325    }
326
327    pub(crate) fn svg_data(self) -> Option<SVGElementData<'dom>> {
328        self.downcast::<SVGSVGElement>().map(|svg| svg.data())
329    }
330
331    pub(crate) fn iframe_browsing_context_id(self) -> Option<BrowsingContextId> {
332        self.downcast::<HTMLIFrameElement>()
333            .and_then(|iframe_element| iframe_element.browsing_context_id())
334    }
335
336    pub(crate) fn iframe_pipeline_id(self) -> Option<PipelineId> {
337        self.downcast::<HTMLIFrameElement>()
338            .and_then(|iframe_element| iframe_element.pipeline_id())
339    }
340
341    #[expect(unsafe_code)]
342    pub(crate) fn opaque(self) -> OpaqueNode {
343        unsafe { OpaqueNode(self.get_jsobject() as usize) }
344    }
345
346    #[expect(unsafe_code)]
347    pub(crate) fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
348        unsafe {
349            self.unsafe_get()
350                .get_rare_data()
351                .borrow_for_layout()
352                .as_ref()
353                .and_then(|rare_data| rare_data.implemented_pseudo_element)
354        }
355    }
356
357    pub(crate) fn is_in_ua_widget(&self) -> bool {
358        self.unsafe_get().is_in_ua_widget()
359    }
360
361    pub(crate) fn is_root_of_user_agent_widget(&self) -> bool {
362        self.downcast::<Element>().is_some_and(|element| {
363            element
364                .get_shadow_root_for_layout()
365                .is_some_and(|shadow_root| shadow_root.is_user_agent_widget())
366        })
367    }
368
369    pub(crate) fn children_count(&self) -> u32 {
370        self.unsafe_get().children_count()
371    }
372}
373
374pub(crate) struct NodeTypeIdWrapper(pub(crate) NodeTypeId);
375
376impl From<NodeTypeIdWrapper> for LayoutNodeType {
377    #[inline(always)]
378    fn from(node_type: NodeTypeIdWrapper) -> LayoutNodeType {
379        match node_type.0 {
380            NodeTypeId::Element(e) => LayoutNodeType::Element(ElementTypeIdWrapper(e).into()),
381            NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => LayoutNodeType::Text,
382            x => unreachable!("Layout should not traverse nodes of type {:?}", x),
383        }
384    }
385}
386
387struct ElementTypeIdWrapper(ElementTypeId);
388
389impl From<ElementTypeIdWrapper> for LayoutElementType {
390    #[inline(always)]
391    fn from(element_type: ElementTypeIdWrapper) -> LayoutElementType {
392        match element_type.0 {
393            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement) => {
394                LayoutElementType::HTMLBodyElement
395            },
396            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement) => {
397                LayoutElementType::HTMLButtonElement
398            },
399            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBRElement) => {
400                LayoutElementType::HTMLBRElement
401            },
402            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement) => {
403                LayoutElementType::HTMLCanvasElement
404            },
405            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLHtmlElement) => {
406                LayoutElementType::HTMLHtmlElement
407            },
408            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement) => {
409                LayoutElementType::HTMLIFrameElement
410            },
411            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement) => {
412                LayoutElementType::HTMLImageElement
413            },
414            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLMediaElement(_)) => {
415                LayoutElementType::HTMLMediaElement
416            },
417            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement) => {
418                LayoutElementType::HTMLInputElement
419            },
420            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptGroupElement) => {
421                LayoutElementType::HTMLOptGroupElement
422            },
423            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptionElement) => {
424                LayoutElementType::HTMLOptionElement
425            },
426            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement) => {
427                LayoutElementType::HTMLObjectElement
428            },
429            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLParagraphElement) => {
430                LayoutElementType::HTMLParagraphElement
431            },
432            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLPreElement) => {
433                LayoutElementType::HTMLPreElement
434            },
435            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement) => {
436                LayoutElementType::HTMLSelectElement
437            },
438            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableCellElement) => {
439                LayoutElementType::HTMLTableCellElement
440            },
441            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableColElement) => {
442                LayoutElementType::HTMLTableColElement
443            },
444            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement) => {
445                LayoutElementType::HTMLTableElement
446            },
447            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement) => {
448                LayoutElementType::HTMLTableRowElement
449            },
450            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableSectionElement) => {
451                LayoutElementType::HTMLTableSectionElement
452            },
453            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement) => {
454                LayoutElementType::HTMLTextAreaElement
455            },
456            ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
457                SVGGraphicsElementTypeId::SVGImageElement,
458            )) => LayoutElementType::SVGImageElement,
459            ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
460                SVGGraphicsElementTypeId::SVGSVGElement,
461            )) => LayoutElementType::SVGSVGElement,
462            _ => LayoutElementType::Element,
463        }
464    }
465}