script/dom/node/
layout_dom.rs1use atomic_refcell::AtomicRef;
8use layout_api::{
9 GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutElementType, LayoutNodeType,
10 SVGElementData,
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, Utf16CodeUnits, 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 #[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 #[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 #[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 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 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 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 selection_for_text_node(&self) -> Option<RangeAny<Utf32CodeUnits>> {
251 if let Some(shadow_root) = self.containing_shadow_root_for_layout() {
252 let host = shadow_root.get_host_for_layout();
253 if let Some(input) = host.downcast::<HTMLInputElement>() {
254 return input.selection_for_layout();
255 }
256 if let Some(textarea) = host.downcast::<HTMLTextAreaElement>() {
257 return textarea.selection_for_layout();
258 }
259 }
260
261 let unsafe_self = self.unsafe_get();
262 if !unsafe_self.get_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION) ||
263 unsafe_self.get_flag(NodeFlags::SELECTION_INHIBITED)
264 {
265 return None;
266 }
267
268 let text_node = self.downcast::<Text>()?;
269 let text = text_node.upcast().data_for_layout();
270 let selection = self.owner_doc_for_layout().visible_selection_for_layout()?;
271 let range = selection.range_for_layout();
272
273 let range = range.as_ref()?;
274 let is_start_node = unsafe { range.start.container.to_layout() } == *self;
275 let is_end_node = unsafe { range.end.container.to_layout() } == *self;
276
277 let start =
278 is_start_node.then(|| Utf16CodeUnits(range.start.offset).to_utf32_code_units_in(&text));
279 let end =
280 is_end_node.then(|| Utf16CodeUnits(range.end.offset).to_utf32_code_units_in(&text));
281 Some(RangeAny::new(start, end))
282 }
283
284 pub(crate) fn text_node_paints_caret(&self) -> bool {
285 if let Some(shadow_root) = self.containing_shadow_root_for_layout() {
286 let host = shadow_root.get_host_for_layout();
287 if host.is::<HTMLInputElement>() || host.is::<HTMLTextAreaElement>() {
288 return true;
291 }
292 }
293 false
298 }
299
300 pub(crate) fn replaced_is_selected(&self) -> bool {
301 let unsafe_self = self.unsafe_get();
302 unsafe_self.get_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION) &&
303 !unsafe_self.get_flag(NodeFlags::SELECTION_INHIBITED)
304 }
305
306 pub(crate) fn image_url(self) -> Option<ServoUrl> {
307 self.downcast::<HTMLImageElement>()
308 .expect("not an image!")
309 .image_url()
310 }
311
312 pub(crate) fn image_data(self) -> Option<(Option<Image>, Option<ImageMetadata>)> {
313 self.downcast::<HTMLImageElement>().map(|e| e.image_data())
314 }
315
316 pub(crate) fn image_density(self) -> Option<f64> {
317 self.downcast::<HTMLImageElement>()
318 .expect("not an image!")
319 .image_density()
320 }
321
322 pub(crate) fn showing_broken_image_icon(self) -> bool {
323 self.downcast::<HTMLImageElement>()
324 .map(|image_element| image_element.showing_broken_image_icon())
325 .unwrap_or_default()
326 }
327
328 pub(crate) fn canvas_data(self) -> Option<HTMLCanvasData> {
329 self.downcast::<HTMLCanvasElement>()
330 .map(|canvas| canvas.data())
331 }
332
333 pub(crate) fn media_data(self) -> Option<HTMLMediaData> {
334 self.downcast::<HTMLVideoElement>()
335 .map(|media| media.data())
336 }
337
338 pub(crate) fn svg_data(self) -> Option<SVGElementData<'dom>> {
339 self.downcast::<SVGSVGElement>().map(|svg| svg.data())
340 }
341
342 pub(crate) fn iframe_browsing_context_id(self) -> Option<BrowsingContextId> {
343 self.downcast::<HTMLIFrameElement>()
344 .and_then(|iframe_element| iframe_element.browsing_context_id())
345 }
346
347 pub(crate) fn iframe_pipeline_id(self) -> Option<PipelineId> {
348 self.downcast::<HTMLIFrameElement>()
349 .and_then(|iframe_element| iframe_element.pipeline_id())
350 }
351
352 #[expect(unsafe_code)]
353 pub(crate) fn opaque(self) -> OpaqueNode {
354 unsafe { OpaqueNode(self.get_jsobject() as usize) }
355 }
356
357 #[expect(unsafe_code)]
358 pub(crate) fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
359 unsafe {
360 self.unsafe_get()
361 .get_rare_data()
362 .borrow_for_layout()
363 .as_ref()
364 .and_then(|rare_data| rare_data.implemented_pseudo_element)
365 }
366 }
367
368 pub(crate) fn is_in_ua_widget(&self) -> bool {
369 self.unsafe_get().is_in_ua_widget()
370 }
371
372 pub(crate) fn is_root_of_user_agent_widget(&self) -> bool {
373 self.downcast::<Element>().is_some_and(|element| {
374 element
375 .get_shadow_root_for_layout()
376 .is_some_and(|shadow_root| shadow_root.is_user_agent_widget())
377 })
378 }
379
380 pub(crate) fn children_count(&self) -> u32 {
381 self.unsafe_get().children_count()
382 }
383}
384
385pub(crate) struct NodeTypeIdWrapper(pub(crate) NodeTypeId);
386
387impl From<NodeTypeIdWrapper> for LayoutNodeType {
388 #[inline(always)]
389 fn from(node_type: NodeTypeIdWrapper) -> LayoutNodeType {
390 match node_type.0 {
391 NodeTypeId::Element(e) => LayoutNodeType::Element(ElementTypeIdWrapper(e).into()),
392 NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => LayoutNodeType::Text,
393 x => unreachable!("Layout should not traverse nodes of type {:?}", x),
394 }
395 }
396}
397
398struct ElementTypeIdWrapper(ElementTypeId);
399
400impl From<ElementTypeIdWrapper> for LayoutElementType {
401 #[inline(always)]
402 fn from(element_type: ElementTypeIdWrapper) -> LayoutElementType {
403 match element_type.0 {
404 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement) => {
405 LayoutElementType::HTMLBodyElement
406 },
407 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement) => {
408 LayoutElementType::HTMLButtonElement
409 },
410 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBRElement) => {
411 LayoutElementType::HTMLBRElement
412 },
413 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement) => {
414 LayoutElementType::HTMLCanvasElement
415 },
416 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLHtmlElement) => {
417 LayoutElementType::HTMLHtmlElement
418 },
419 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement) => {
420 LayoutElementType::HTMLIFrameElement
421 },
422 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement) => {
423 LayoutElementType::HTMLImageElement
424 },
425 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLMediaElement(_)) => {
426 LayoutElementType::HTMLMediaElement
427 },
428 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement) => {
429 LayoutElementType::HTMLInputElement
430 },
431 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptGroupElement) => {
432 LayoutElementType::HTMLOptGroupElement
433 },
434 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptionElement) => {
435 LayoutElementType::HTMLOptionElement
436 },
437 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement) => {
438 LayoutElementType::HTMLObjectElement
439 },
440 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLParagraphElement) => {
441 LayoutElementType::HTMLParagraphElement
442 },
443 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLPreElement) => {
444 LayoutElementType::HTMLPreElement
445 },
446 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement) => {
447 LayoutElementType::HTMLSelectElement
448 },
449 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableCellElement) => {
450 LayoutElementType::HTMLTableCellElement
451 },
452 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableColElement) => {
453 LayoutElementType::HTMLTableColElement
454 },
455 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement) => {
456 LayoutElementType::HTMLTableElement
457 },
458 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement) => {
459 LayoutElementType::HTMLTableRowElement
460 },
461 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableSectionElement) => {
462 LayoutElementType::HTMLTableSectionElement
463 },
464 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement) => {
465 LayoutElementType::HTMLTextAreaElement
466 },
467 ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
468 SVGGraphicsElementTypeId::SVGImageElement,
469 )) => LayoutElementType::SVGImageElement,
470 ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
471 SVGGraphicsElementTypeId::SVGSVGElement,
472 )) => LayoutElementType::SVGSVGElement,
473 _ => LayoutElementType::Element,
474 }
475 }
476}