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::cross_process_instant::CrossProcessInstant;
18use servo_base::id::{BrowsingContextId, PipelineId};
19use servo_base::text::{RangeAny, Utf16CodeUnits, Utf32CodeUnits};
20use servo_url::ServoUrl;
21use style::dom::OpaqueNode;
22use style::selector_parser::PseudoElement;
23
24use crate::dom::bindings::inheritance::{CharacterDataTypeId, NodeTypeId};
25use crate::dom::bindings::root::{LayoutDom, ToLayout, ToLayoutOptional};
26use crate::dom::document::Document;
27use crate::dom::element::Element;
28use crate::dom::html::form_controls::htmlinputelement::HTMLInputElement;
29use crate::dom::html::htmlcanvaselement::HTMLCanvasElement;
30use crate::dom::html::htmliframeelement::HTMLIFrameElement;
31use crate::dom::html::htmlimageelement::HTMLImageElement;
32use crate::dom::html::htmlslotelement::HTMLSlotElement;
33use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
34use crate::dom::html::htmlvideoelement::HTMLVideoElement;
35use crate::dom::shadowroot::shadowroot::ShadowRoot;
36use crate::dom::svg::svgsvgelement::SVGSVGElement;
37use crate::dom::text::Text;
38use crate::dom::{Node, NodeFlags};
39
40impl<'dom> LayoutDom<'dom, Node> {
41 #[inline]
42 #[expect(unsafe_code)]
43 pub(crate) fn parent_node_ref(self) -> Option<LayoutDom<'dom, Node>> {
44 unsafe { self.unsafe_get().parent_node().to_layout() }
45 }
46
47 #[inline]
48 pub(crate) fn type_id_for_layout(self) -> NodeTypeId {
49 self.unsafe_get().type_id()
50 }
51
52 #[inline]
53 pub(crate) fn is_element_for_layout(&self) -> bool {
54 (*self).is::<Element>()
55 }
56
57 pub(crate) fn is_text_node_for_layout(&self) -> bool {
58 matches!(
59 self.type_id_for_layout(),
60 NodeTypeId::CharacterData(CharacterDataTypeId::Text(..))
61 )
62 }
63
64 #[inline]
65 pub(crate) fn composed_parent_node_ref(self) -> Option<LayoutDom<'dom, Node>> {
66 let parent = self.parent_node_ref();
67 if let Some(parent) = parent &&
68 let Some(shadow_root) = parent.downcast::<ShadowRoot>()
69 {
70 return Some(shadow_root.get_host_for_layout().upcast());
71 }
72 parent
73 }
74
75 #[inline]
76 pub(crate) fn traversal_parent(self) -> Option<LayoutDom<'dom, Element>> {
77 if let Some(assigned_slot) = self.assigned_slot_for_layout() {
78 return Some(assigned_slot.upcast());
79 }
80 let parent = self.parent_node_ref()?;
81 if let Some(shadow) = parent.downcast::<ShadowRoot>() {
82 return Some(shadow.get_host_for_layout());
83 };
84 parent.downcast()
85 }
86
87 #[inline]
88 #[expect(unsafe_code)]
89 pub(crate) fn first_child_ref(self) -> Option<LayoutDom<'dom, Node>> {
90 unsafe { self.unsafe_get().first_child().to_layout() }
91 }
92
93 #[inline]
94 #[expect(unsafe_code)]
95 pub(crate) fn last_child_ref(self) -> Option<LayoutDom<'dom, Node>> {
96 unsafe { self.unsafe_get().last_child().to_layout() }
97 }
98
99 #[inline]
100 #[expect(unsafe_code)]
101 pub(crate) fn prev_sibling_ref(self) -> Option<LayoutDom<'dom, Node>> {
102 unsafe { self.unsafe_get().prev_sibling().to_layout() }
103 }
104
105 #[inline]
106 #[expect(unsafe_code)]
107 pub(crate) fn next_sibling_ref(self) -> Option<LayoutDom<'dom, Node>> {
108 unsafe { self.unsafe_get().next_sibling().to_layout() }
109 }
110
111 #[inline]
112 #[expect(unsafe_code)]
113 pub(crate) fn owner_doc_for_layout(self) -> LayoutDom<'dom, Document> {
114 unsafe { self.unsafe_get().get_owner_doc().to_layout().unwrap() }
115 }
116
117 #[inline]
118 #[expect(unsafe_code)]
119 pub(crate) fn containing_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>> {
120 unsafe {
121 self.unsafe_get()
122 .get_rare_data()
123 .borrow_for_layout()
124 .as_ref()?
125 .containing_shadow_root
126 .as_ref()
127 .map(|sr| sr.to_layout())
128 }
129 }
130
131 #[inline]
132 #[expect(unsafe_code)]
133 pub(crate) fn assigned_slot_for_layout(self) -> Option<LayoutDom<'dom, HTMLSlotElement>> {
134 unsafe {
135 self.unsafe_get()
136 .get_rare_data()
137 .borrow_for_layout()
138 .as_ref()?
139 .slottable_data
140 .assigned_slot
141 .as_ref()
142 .map(|assigned_slot| assigned_slot.to_layout())
143 }
144 }
145
146 #[inline]
151 #[expect(unsafe_code)]
152 pub(crate) unsafe fn get_flag(self, flag: NodeFlags) -> bool {
153 (self.unsafe_get()).flags().get().contains(flag)
154 }
155
156 #[inline]
157 #[expect(unsafe_code)]
158 pub(crate) unsafe fn set_flag(self, flag: NodeFlags, value: bool) {
159 let this = self.unsafe_get();
160 let mut flags = (this).flags().get();
161
162 if value {
163 flags.insert(flag);
164 } else {
165 flags.remove(flag);
166 }
167
168 (this).flags().set(flags);
169 }
170
171 #[inline]
172 #[expect(unsafe_code)]
173 pub(crate) fn layout_data(self) -> Option<&'dom GenericLayoutData> {
174 unsafe {
175 self.unsafe_get()
176 .layout_data()
177 .borrow_for_layout()
178 .as_deref()
179 }
180 }
181
182 #[inline]
190 #[expect(unsafe_code)]
191 pub(crate) unsafe fn initialize_layout_data(self, new_data: Box<GenericLayoutData>) {
192 let data = unsafe { self.unsafe_get().layout_data().borrow_mut_for_layout() };
193 debug_assert!(data.is_none());
194 *data = Some(new_data);
195 }
196
197 #[inline]
205 #[expect(unsafe_code)]
206 pub(crate) unsafe fn clear_layout_data(self) {
207 unsafe {
208 self.unsafe_get()
209 .layout_data()
210 .borrow_mut_for_layout()
211 .take();
212 }
213 }
214
215 pub(crate) fn is_single_line_text_inner_editor(&self) -> bool {
218 matches!(
219 self.implemented_pseudo_element(),
220 Some(PseudoElement::ServoTextControlInnerEditor)
221 )
222 }
223
224 pub(crate) fn is_text_container_of_single_line_input(&self) -> bool {
227 let is_single_line_text_inner_placeholder = matches!(
228 self.implemented_pseudo_element(),
229 Some(PseudoElement::Placeholder)
230 );
231 debug_assert!(
233 !is_single_line_text_inner_placeholder ||
234 self.containing_shadow_root_for_layout()
235 .map(|root| root.get_host_for_layout())
236 .map(|host| host.downcast::<HTMLInputElement>())
237 .is_some()
238 );
239
240 self.is_single_line_text_inner_editor() || is_single_line_text_inner_placeholder
241 }
242
243 pub(crate) fn text_content(self) -> AtomicRef<'dom, str> {
244 self.downcast::<Text>()
245 .expect("Called LayoutDom::text_content on non-Text node!")
246 .upcast()
247 .data_for_layout()
248 }
249
250 #[expect(unsafe_code)]
251 pub(crate) fn selection_for_text_node(&self) -> Option<RangeAny<Utf32CodeUnits>> {
252 if let Some(shadow_root) = self.containing_shadow_root_for_layout() {
253 let host = shadow_root.get_host_for_layout();
254 if let Some(input) = host.downcast::<HTMLInputElement>() {
255 return input.selection_for_layout();
256 }
257 if let Some(textarea) = host.downcast::<HTMLTextAreaElement>() {
258 return textarea.selection_for_layout();
259 }
260 }
261
262 let unsafe_self = self.unsafe_get();
263 if !unsafe_self.get_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION) ||
264 unsafe_self.get_flag(NodeFlags::SELECTION_INHIBITED)
265 {
266 return None;
267 }
268
269 let text_node = self.downcast::<Text>()?;
270 let text = text_node.upcast().data_for_layout();
271 let selection = self.owner_doc_for_layout().visible_selection_for_layout()?;
272 let range = selection.range_for_layout();
273
274 let range = range.as_ref()?;
275 let is_start_node = unsafe { range.start.container.to_layout() } == *self;
276 let is_end_node = unsafe { range.end.container.to_layout() } == *self;
277
278 let start =
279 is_start_node.then(|| Utf16CodeUnits(range.start.offset).to_utf32_code_units_in(&text));
280 let end =
281 is_end_node.then(|| Utf16CodeUnits(range.end.offset).to_utf32_code_units_in(&text));
282 Some(RangeAny::new(start, end))
283 }
284
285 pub(crate) fn text_node_paints_caret(&self) -> bool {
286 self.parent_node_ref()
288 .is_some_and(|parent| parent.is_text_container_of_single_line_input())
289 }
290
291 pub(crate) fn text_node_needs_caret_placeholder(&self) -> bool {
292 self.parent_node_ref()
294 .is_some_and(|parent| parent.is_text_container_of_single_line_input())
295 }
296
297 pub(crate) fn replaced_is_selected(&self) -> bool {
298 let unsafe_self = self.unsafe_get();
299 unsafe_self.get_flag(NodeFlags::OVERLAPS_DOCUMENT_SELECTION) &&
300 !unsafe_self.get_flag(NodeFlags::SELECTION_INHIBITED)
301 }
302
303 pub(crate) fn image_url(self) -> Option<ServoUrl> {
304 self.downcast::<HTMLImageElement>()
305 .expect("not an image!")
306 .image_url()
307 }
308
309 pub(crate) fn image_data(self) -> Option<(Option<Image>, Option<ImageMetadata>)> {
310 self.downcast::<HTMLImageElement>().map(|e| e.image_data())
311 }
312
313 pub(crate) fn load_time(self) -> Option<CrossProcessInstant> {
314 self.downcast::<HTMLImageElement>()
315 .and_then(|image_element| image_element.load_time())
316 }
317
318 pub(crate) fn image_density(self) -> Option<f64> {
319 self.downcast::<HTMLImageElement>()
320 .expect("not an image!")
321 .image_density()
322 }
323
324 pub(crate) fn showing_broken_image_icon(self) -> bool {
325 self.downcast::<HTMLImageElement>()
326 .map(|image_element| image_element.showing_broken_image_icon())
327 .unwrap_or_default()
328 }
329
330 pub(crate) fn canvas_data(self) -> Option<HTMLCanvasData> {
331 self.downcast::<HTMLCanvasElement>()
332 .map(|canvas| canvas.data())
333 }
334
335 pub(crate) fn media_data(self) -> Option<HTMLMediaData> {
336 self.downcast::<HTMLVideoElement>()
337 .map(|media| media.data())
338 }
339
340 pub(crate) fn svg_data(self) -> Option<SVGElementData<'dom>> {
341 self.downcast::<SVGSVGElement>().map(|svg| svg.data())
342 }
343
344 pub(crate) fn iframe_browsing_context_id(self) -> Option<BrowsingContextId> {
345 self.downcast::<HTMLIFrameElement>()
346 .and_then(|iframe_element| iframe_element.browsing_context_id())
347 }
348
349 pub(crate) fn iframe_pipeline_id(self) -> Option<PipelineId> {
350 self.downcast::<HTMLIFrameElement>()
351 .and_then(|iframe_element| iframe_element.pipeline_id())
352 }
353
354 #[expect(unsafe_code)]
355 pub(crate) fn opaque(self) -> OpaqueNode {
356 unsafe { OpaqueNode(self.get_jsobject() as usize) }
357 }
358
359 #[expect(unsafe_code)]
360 pub(crate) fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
361 unsafe {
362 self.unsafe_get()
363 .get_rare_data()
364 .borrow_for_layout()
365 .as_ref()
366 .and_then(|rare_data| rare_data.implemented_pseudo_element)
367 }
368 }
369
370 pub(crate) fn is_in_ua_widget(&self) -> bool {
371 self.unsafe_get().is_in_ua_widget()
372 }
373
374 pub(crate) fn is_root_of_user_agent_widget(&self) -> bool {
375 self.downcast::<Element>().is_some_and(|element| {
376 element
377 .get_shadow_root_for_layout()
378 .is_some_and(|shadow_root| shadow_root.is_user_agent_widget())
379 })
380 }
381
382 pub(crate) fn children_count(&self) -> u32 {
383 self.unsafe_get().children_count()
384 }
385}
386
387pub(crate) struct NodeTypeIdWrapper(pub(crate) NodeTypeId);
388
389impl From<NodeTypeIdWrapper> for LayoutNodeType {
390 #[inline(always)]
391 fn from(node_type: NodeTypeIdWrapper) -> LayoutNodeType {
392 match node_type.0 {
393 NodeTypeId::Element(e) => LayoutNodeType::Element(ElementTypeIdWrapper(e).into()),
394 NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => LayoutNodeType::Text,
395 x => unreachable!("Layout should not traverse nodes of type {:?}", x),
396 }
397 }
398}
399
400struct ElementTypeIdWrapper(ElementTypeId);
401
402impl From<ElementTypeIdWrapper> for LayoutElementType {
403 #[inline(always)]
404 fn from(element_type: ElementTypeIdWrapper) -> LayoutElementType {
405 match element_type.0 {
406 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement) => {
407 LayoutElementType::HTMLBodyElement
408 },
409 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement) => {
410 LayoutElementType::HTMLButtonElement
411 },
412 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBRElement) => {
413 LayoutElementType::HTMLBRElement
414 },
415 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement) => {
416 LayoutElementType::HTMLCanvasElement
417 },
418 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLHtmlElement) => {
419 LayoutElementType::HTMLHtmlElement
420 },
421 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement) => {
422 LayoutElementType::HTMLIFrameElement
423 },
424 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement) => {
425 LayoutElementType::HTMLImageElement
426 },
427 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLMediaElement(_)) => {
428 LayoutElementType::HTMLMediaElement
429 },
430 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement) => {
431 LayoutElementType::HTMLInputElement
432 },
433 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptGroupElement) => {
434 LayoutElementType::HTMLOptGroupElement
435 },
436 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptionElement) => {
437 LayoutElementType::HTMLOptionElement
438 },
439 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement) => {
440 LayoutElementType::HTMLObjectElement
441 },
442 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLParagraphElement) => {
443 LayoutElementType::HTMLParagraphElement
444 },
445 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLPreElement) => {
446 LayoutElementType::HTMLPreElement
447 },
448 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement) => {
449 LayoutElementType::HTMLSelectElement
450 },
451 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableCellElement) => {
452 LayoutElementType::HTMLTableCellElement
453 },
454 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableColElement) => {
455 LayoutElementType::HTMLTableColElement
456 },
457 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement) => {
458 LayoutElementType::HTMLTableElement
459 },
460 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement) => {
461 LayoutElementType::HTMLTableRowElement
462 },
463 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableSectionElement) => {
464 LayoutElementType::HTMLTableSectionElement
465 },
466 ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement) => {
467 LayoutElementType::HTMLTextAreaElement
468 },
469 ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
470 SVGGraphicsElementTypeId::SVGImageElement,
471 )) => LayoutElementType::SVGImageElement,
472 ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
473 SVGGraphicsElementTypeId::SVGSVGElement,
474 )) => LayoutElementType::SVGSVGElement,
475 _ => LayoutElementType::Element,
476 }
477 }
478}