script/layout_dom/
node.rs1#![expect(unsafe_code)]
6
7use std::borrow::Cow;
8use std::fmt;
9use std::iter::FusedIterator;
10
11use base::id::{BrowsingContextId, PipelineId};
12use layout_api::wrapper_traits::{
13 LayoutDataTrait, LayoutNode, PseudoElementChain, SharedSelection, ThreadSafeLayoutElement,
14 ThreadSafeLayoutNode,
15};
16use layout_api::{
17 GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutElementType, LayoutNodeType,
18 SVGElementData, StyleData, TrustedNodeAddress,
19};
20use net_traits::image_cache::Image;
21use pixels::ImageMetadata;
22use script_bindings::error::Fallible;
23use selectors::Element as _;
24use servo_arc::Arc;
25use servo_url::ServoUrl;
26use style;
27use style::context::SharedStyleContext;
28use style::dom::{NodeInfo, TElement, TNode, TShadowRoot};
29use style::dom_apis::{MayUseInvalidation, SelectorQuery, query_selector};
30use style::properties::ComputedValues;
31use style::selector_parser::{PseudoElement, SelectorParser};
32use style::stylesheets::UrlExtraData;
33use url::Url;
34
35use super::{
36 ServoLayoutDocument, ServoLayoutElement, ServoShadowRoot, ServoThreadSafeLayoutElement,
37};
38use crate::dom::bindings::error::Error;
39use crate::dom::bindings::inheritance::NodeTypeId;
40use crate::dom::bindings::root::LayoutDom;
41use crate::dom::element::{Element, LayoutElementHelpers};
42use crate::dom::node::{LayoutNodeHelpers, Node, NodeFlags, NodeTypeIdWrapper};
43
44#[derive(Clone, Copy, PartialEq)]
50#[repr(transparent)]
51pub struct ServoLayoutNode<'dom> {
52 pub(super) node: LayoutDom<'dom, Node>,
54}
55
56unsafe impl Send for ServoLayoutNode<'_> {}
63unsafe impl Sync for ServoLayoutNode<'_> {}
64
65impl fmt::Debug for ServoLayoutNode<'_> {
66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 if let Some(el) = self.as_element() {
68 el.fmt(f)
69 } else if self.is_text_node() {
70 write!(f, "<text node> ({:#x})", self.opaque().0)
71 } else {
72 write!(f, "<non-text node> ({:#x})", self.opaque().0)
73 }
74 }
75}
76
77impl<'dom> ServoLayoutNode<'dom> {
78 pub(crate) fn from_layout_dom(node: LayoutDom<'dom, Node>) -> Self {
79 ServoLayoutNode { node }
80 }
81
82 pub unsafe fn new(address: &TrustedNodeAddress) -> Self {
88 let node = unsafe { LayoutDom::from_trusted_node_address(*address) };
89 ServoLayoutNode::from_layout_dom(node)
90 }
91
92 pub(super) fn script_type_id(&self) -> NodeTypeId {
93 self.node.type_id_for_layout()
94 }
95
96 pub(crate) fn to_layout_dom(self) -> LayoutDom<'dom, Node> {
101 self.node
102 }
103
104 pub(crate) fn assigned_slot(self) -> Option<ServoLayoutElement<'dom>> {
105 self.node
106 .assigned_slot_for_layout()
107 .as_ref()
108 .map(LayoutDom::upcast)
109 .map(ServoLayoutElement::from_layout_dom)
110 }
111
112 pub(crate) fn scope_match_a_selectors_string<Query>(
114 self,
115 document_url: Arc<Url>,
116 selector: &str,
117 ) -> Fallible<Query::Output>
118 where
119 Query: SelectorQuery<ServoLayoutElement<'dom>>,
120 Query::Output: Default,
121 {
122 let mut result = Query::Output::default();
123
124 let selector_or_error =
126 SelectorParser::parse_author_origin_no_namespace(selector, &UrlExtraData(document_url));
127
128 let Ok(selector_list) = selector_or_error else {
130 return Err(Error::Syntax(None));
131 };
132
133 query_selector::<ServoLayoutElement<'dom>, Query>(
136 self,
137 &selector_list,
138 &mut result,
139 MayUseInvalidation::No,
140 );
141
142 Ok(result)
143 }
144}
145
146impl style::dom::NodeInfo for ServoLayoutNode<'_> {
147 fn is_element(&self) -> bool {
148 self.node.is_element_for_layout()
149 }
150
151 fn is_text_node(&self) -> bool {
152 self.node.is_text_node_for_layout()
153 }
154}
155
156impl<'dom> style::dom::TNode for ServoLayoutNode<'dom> {
157 type ConcreteDocument = ServoLayoutDocument<'dom>;
158 type ConcreteElement = ServoLayoutElement<'dom>;
159 type ConcreteShadowRoot = ServoShadowRoot<'dom>;
160
161 fn parent_node(&self) -> Option<Self> {
162 self.node.parent_node_ref().map(Self::from_layout_dom)
163 }
164
165 fn first_child(&self) -> Option<Self> {
166 self.node.first_child_ref().map(Self::from_layout_dom)
167 }
168
169 fn last_child(&self) -> Option<Self> {
170 self.node.last_child_ref().map(Self::from_layout_dom)
171 }
172
173 fn prev_sibling(&self) -> Option<Self> {
174 self.node.prev_sibling_ref().map(Self::from_layout_dom)
175 }
176
177 fn next_sibling(&self) -> Option<Self> {
178 self.node.next_sibling_ref().map(Self::from_layout_dom)
179 }
180
181 fn owner_doc(&self) -> Self::ConcreteDocument {
182 ServoLayoutDocument::from_layout_dom(self.node.owner_doc_for_layout())
183 }
184
185 fn traversal_parent(&self) -> Option<ServoLayoutElement<'dom>> {
186 if let Some(assigned_slot) = self.assigned_slot() {
187 return Some(assigned_slot);
188 }
189 let parent = self.parent_node()?;
190 if let Some(shadow) = parent.as_shadow_root() {
191 return Some(shadow.host());
192 };
193 parent.as_element()
194 }
195
196 fn opaque(&self) -> style::dom::OpaqueNode {
197 self.to_layout_dom().opaque()
198 }
199
200 fn debug_id(self) -> usize {
201 self.opaque().0
202 }
203
204 fn as_element(&self) -> Option<ServoLayoutElement<'dom>> {
205 self.node
206 .downcast()
207 .map(ServoLayoutElement::from_layout_dom)
208 }
209
210 fn as_document(&self) -> Option<ServoLayoutDocument<'dom>> {
211 self.node
212 .downcast()
213 .map(ServoLayoutDocument::from_layout_dom)
214 }
215
216 fn as_shadow_root(&self) -> Option<ServoShadowRoot<'dom>> {
217 self.node.downcast().map(ServoShadowRoot::from_layout_dom)
218 }
219
220 fn is_in_document(&self) -> bool {
221 unsafe { self.node.get_flag(NodeFlags::IS_IN_A_DOCUMENT_TREE) }
222 }
223}
224
225impl<'dom> LayoutNode<'dom> for ServoLayoutNode<'dom> {
226 type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'dom>;
227
228 fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode {
229 ServoThreadSafeLayoutNode::new(*self)
230 }
231
232 fn type_id(&self) -> LayoutNodeType {
233 NodeTypeIdWrapper(self.script_type_id()).into()
234 }
235
236 unsafe fn initialize_style_and_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self) {
237 let inner = self.to_layout_dom();
238 if inner.style_data().is_none() {
239 unsafe { inner.initialize_style_data() };
240 }
241 if inner.layout_data().is_none() {
242 unsafe { inner.initialize_layout_data(Box::<RequestedLayoutDataType>::default()) };
243 }
244 }
245
246 fn is_connected(&self) -> bool {
247 unsafe { self.node.get_flag(NodeFlags::IS_CONNECTED) }
248 }
249
250 fn style_data(&self) -> Option<&'dom StyleData> {
251 self.to_layout_dom().style_data()
252 }
253
254 fn layout_data(&self) -> Option<&'dom GenericLayoutData> {
255 self.to_layout_dom().layout_data()
256 }
257}
258
259#[derive(Clone, Copy, Debug, PartialEq)]
263pub struct ServoThreadSafeLayoutNode<'dom> {
264 pub(super) node: ServoLayoutNode<'dom>,
266
267 pub(super) pseudo_element_chain: PseudoElementChain,
269}
270
271impl<'dom> ServoThreadSafeLayoutNode<'dom> {
272 pub fn new(node: ServoLayoutNode<'dom>) -> Self {
274 ServoThreadSafeLayoutNode {
275 node,
276 pseudo_element_chain: Default::default(),
277 }
278 }
279
280 unsafe fn get_jsmanaged(&self) -> LayoutDom<'dom, Node> {
283 self.node.to_layout_dom()
284 }
285
286 unsafe fn dangerous_first_child(&self) -> Option<Self> {
289 let js_managed = unsafe { self.get_jsmanaged() };
290 js_managed
291 .first_child_ref()
292 .map(ServoLayoutNode::from_layout_dom)
293 .map(Self::new)
294 }
295
296 unsafe fn dangerous_next_sibling(&self) -> Option<Self> {
299 let js_managed = unsafe { self.get_jsmanaged() };
300 js_managed
301 .next_sibling_ref()
302 .map(ServoLayoutNode::from_layout_dom)
303 .map(Self::new)
304 }
305
306 pub fn is_single_line_text_input(&self) -> bool {
314 self.type_id() == Some(LayoutNodeType::Element(LayoutElementType::HTMLInputElement)) ||
315 (self.pseudo_element_chain.is_empty() &&
316 self.node.node.is_text_container_of_single_line_input())
317 }
318
319 pub fn selected_style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
320 let Some(element) = self.as_element() else {
321 debug_assert!(self.is_text_node());
323 return self.parent_style(context);
324 };
325
326 let style_data = &element.style_data().styles;
327 let get_selected_style = || {
328 if self.node.node.is_in_ua_widget() {
332 return Some(
333 element
334 .containing_shadow_host()?
335 .as_node()
336 .selected_style(context),
337 );
338 }
339 style_data.pseudos.get(&PseudoElement::Selection).cloned()
340 };
341
342 get_selected_style().unwrap_or_else(|| style_data.primary().clone())
343 }
344}
345
346impl style::dom::NodeInfo for ServoThreadSafeLayoutNode<'_> {
347 fn is_element(&self) -> bool {
348 self.node.is_element()
349 }
350
351 fn is_text_node(&self) -> bool {
352 self.node.is_text_node()
353 }
354}
355
356impl<'dom> ThreadSafeLayoutNode<'dom> for ServoThreadSafeLayoutNode<'dom> {
357 type ConcreteNode = ServoLayoutNode<'dom>;
358 type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'dom>;
359 type ConcreteElement = ServoLayoutElement<'dom>;
360 type ChildrenIterator = ServoThreadSafeLayoutNodeChildrenIterator<'dom>;
361
362 fn opaque(&self) -> style::dom::OpaqueNode {
363 unsafe { self.get_jsmanaged().opaque() }
364 }
365
366 fn pseudo_element_chain(&self) -> PseudoElementChain {
367 self.pseudo_element_chain
368 }
369
370 fn type_id(&self) -> Option<LayoutNodeType> {
371 if self.pseudo_element_chain.is_empty() {
372 Some(self.node.type_id())
373 } else {
374 None
375 }
376 }
377
378 fn parent_style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
379 if let Some(chain) = self.pseudo_element_chain.without_innermost() {
380 let mut parent = *self;
381 parent.pseudo_element_chain = chain;
382 return parent.style(context);
383 }
384 let parent_element = self.node.traversal_parent().unwrap();
385 let parent_data = parent_element.borrow_data().unwrap();
386 parent_data.styles.primary().clone()
387 }
388
389 fn initialize_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self) {
390 let inner = self.node.to_layout_dom();
391 if inner.layout_data().is_none() {
392 unsafe {
393 inner.initialize_layout_data(Box::<RequestedLayoutDataType>::default());
394 }
395 }
396 }
397
398 fn debug_id(self) -> usize {
399 self.node.debug_id()
400 }
401
402 fn children(&self) -> style::dom::LayoutIterator<Self::ChildrenIterator> {
403 style::dom::LayoutIterator(ServoThreadSafeLayoutNodeChildrenIterator::new(*self))
404 }
405
406 fn as_element(&self) -> Option<ServoThreadSafeLayoutElement<'dom>> {
407 self.node
408 .as_element()
409 .map(|el| ServoThreadSafeLayoutElement {
410 element: el,
411 pseudo_element_chain: self.pseudo_element_chain,
412 })
413 }
414
415 fn as_html_element(&self) -> Option<ServoThreadSafeLayoutElement<'dom>> {
416 self.as_element()
417 .filter(|element| element.element.is_html_element())
418 }
419
420 fn style_data(&self) -> Option<&'dom StyleData> {
421 self.node.style_data()
422 }
423
424 fn layout_data(&self) -> Option<&'dom GenericLayoutData> {
425 self.node.layout_data()
426 }
427
428 fn unsafe_get(self) -> Self::ConcreteNode {
429 self.node
430 }
431
432 fn text_content(self) -> Cow<'dom, str> {
433 unsafe { self.get_jsmanaged().text_content() }
434 }
435
436 fn selection(&self) -> Option<SharedSelection> {
437 let this = unsafe { self.get_jsmanaged() };
438 this.selection()
439 }
440
441 fn image_url(&self) -> Option<ServoUrl> {
442 let this = unsafe { self.get_jsmanaged() };
443 this.image_url()
444 }
445
446 fn image_density(&self) -> Option<f64> {
447 let this = unsafe { self.get_jsmanaged() };
448 this.image_density()
449 }
450
451 fn showing_broken_image_icon(&self) -> bool {
452 let this = unsafe { self.get_jsmanaged() };
453 this.showing_broken_image_icon()
454 }
455
456 fn image_data(&self) -> Option<(Option<Image>, Option<ImageMetadata>)> {
457 let this = unsafe { self.get_jsmanaged() };
458 this.image_data()
459 }
460
461 fn canvas_data(&self) -> Option<HTMLCanvasData> {
462 let this = unsafe { self.get_jsmanaged() };
463 this.canvas_data()
464 }
465
466 fn media_data(&self) -> Option<HTMLMediaData> {
467 let this = unsafe { self.get_jsmanaged() };
468 this.media_data()
469 }
470
471 fn svg_data(&self) -> Option<SVGElementData<'dom>> {
472 let this = unsafe { self.get_jsmanaged() };
473 this.svg_data()
474 }
475
476 fn iframe_browsing_context_id(&self) -> Option<BrowsingContextId> {
478 let this = unsafe { self.get_jsmanaged() };
479 this.iframe_browsing_context_id()
480 }
481
482 fn iframe_pipeline_id(&self) -> Option<PipelineId> {
484 let this = unsafe { self.get_jsmanaged() };
485 this.iframe_pipeline_id()
486 }
487
488 fn get_span(&self) -> Option<u32> {
489 unsafe {
490 self.get_jsmanaged()
491 .downcast::<Element>()
492 .unwrap()
493 .get_span()
494 }
495 }
496
497 fn get_colspan(&self) -> Option<u32> {
498 unsafe {
499 self.get_jsmanaged()
500 .downcast::<Element>()
501 .unwrap()
502 .get_colspan()
503 }
504 }
505
506 fn get_rowspan(&self) -> Option<u32> {
507 unsafe {
508 self.get_jsmanaged()
509 .downcast::<Element>()
510 .unwrap()
511 .get_rowspan()
512 }
513 }
514
515 fn with_pseudo_element_chain(&self, pseudo_element_chain: PseudoElementChain) -> Self {
516 Self {
517 node: self.node,
518 pseudo_element_chain,
519 }
520 }
521
522 fn set_uses_content_attribute_with_attr(&self, uses_content_attribute_with_attr: bool) {
527 unsafe {
528 self.node.node.set_flag(
529 NodeFlags::USES_ATTR_IN_CONTENT_ATTRIBUTE,
530 uses_content_attribute_with_attr,
531 )
532 }
533 }
534}
535
536pub enum ServoThreadSafeLayoutNodeChildrenIterator<'dom> {
537 Node(Option<ServoThreadSafeLayoutNode<'dom>>),
539 Slottables(<Vec<ServoLayoutNode<'dom>> as IntoIterator>::IntoIter),
541}
542
543impl<'dom> ServoThreadSafeLayoutNodeChildrenIterator<'dom> {
544 #[expect(unsafe_code)]
545 fn new(
546 parent: ServoThreadSafeLayoutNode<'dom>,
547 ) -> ServoThreadSafeLayoutNodeChildrenIterator<'dom> {
548 if let Some(element) = parent.as_element() {
549 if let Some(shadow) = element.shadow_root() {
550 return Self::new(shadow.as_node().to_threadsafe());
551 };
552
553 let slotted_nodes = element.slotted_nodes();
554 if !slotted_nodes.is_empty() {
555 #[expect(clippy::unnecessary_to_owned)] return Self::Slottables(slotted_nodes.to_owned().into_iter());
557 }
558 }
559
560 Self::Node(unsafe { parent.dangerous_first_child() })
561 }
562}
563
564impl<'dom> Iterator for ServoThreadSafeLayoutNodeChildrenIterator<'dom> {
565 type Item = ServoThreadSafeLayoutNode<'dom>;
566
567 fn next(&mut self) -> Option<Self::Item> {
568 match self {
569 Self::Node(node) => {
570 let next_sibling = unsafe { (*node)?.dangerous_next_sibling() };
571 std::mem::replace(node, next_sibling)
572 },
573 Self::Slottables(slots) => slots.next().map(|node| node.to_threadsafe()),
574 }
575 }
576}
577
578impl FusedIterator for ServoThreadSafeLayoutNodeChildrenIterator<'_> {}