script/layout_dom/
node.rs1#![allow(unsafe_code)]
6
7use std::borrow::Cow;
8use std::fmt;
9use std::iter::FusedIterator;
10
11use base::id::{BrowsingContextId, PipelineId};
12use fonts_traits::ByteIndex;
13use layout_api::wrapper_traits::{
14 LayoutDataTrait, LayoutNode, PseudoElementChain, ThreadSafeLayoutElement, 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 range::Range;
23use selectors::Element as _;
24use servo_arc::Arc;
25use servo_url::ServoUrl;
26use style;
27use style::dom::{NodeInfo, TElement, TNode, TShadowRoot};
28use style::properties::ComputedValues;
29use style::selector_parser::PseudoElement;
30
31use super::{
32 ServoLayoutDocument, ServoLayoutElement, ServoShadowRoot, ServoThreadSafeLayoutElement,
33};
34use crate::dom::bindings::inheritance::NodeTypeId;
35use crate::dom::bindings::root::LayoutDom;
36use crate::dom::element::{Element, LayoutElementHelpers};
37use crate::dom::node::{LayoutNodeHelpers, Node, NodeFlags, NodeTypeIdWrapper};
38
39#[derive(Clone, Copy, PartialEq)]
45#[repr(transparent)]
46pub struct ServoLayoutNode<'dom> {
47 pub(super) node: LayoutDom<'dom, Node>,
49}
50
51unsafe impl Send for ServoLayoutNode<'_> {}
58unsafe impl Sync for ServoLayoutNode<'_> {}
59
60impl fmt::Debug for ServoLayoutNode<'_> {
61 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62 if let Some(el) = self.as_element() {
63 el.fmt(f)
64 } else if self.is_text_node() {
65 write!(f, "<text node> ({:#x})", self.opaque().0)
66 } else {
67 write!(f, "<non-text node> ({:#x})", self.opaque().0)
68 }
69 }
70}
71
72impl<'dom> ServoLayoutNode<'dom> {
73 pub(super) fn from_layout_js(n: LayoutDom<'dom, Node>) -> Self {
74 ServoLayoutNode { node: n }
75 }
76
77 pub unsafe fn new(address: &TrustedNodeAddress) -> Self {
83 let node = unsafe { LayoutDom::from_trusted_node_address(*address) };
84 ServoLayoutNode::from_layout_js(node)
85 }
86
87 pub(super) fn script_type_id(&self) -> NodeTypeId {
88 self.node.type_id_for_layout()
89 }
90
91 pub(crate) fn get_jsmanaged(self) -> LayoutDom<'dom, Node> {
93 self.node
94 }
95
96 pub(crate) fn assigned_slot(self) -> Option<ServoLayoutElement<'dom>> {
97 self.node
98 .assigned_slot_for_layout()
99 .as_ref()
100 .map(LayoutDom::upcast)
101 .map(ServoLayoutElement::from_layout_js)
102 }
103}
104
105impl style::dom::NodeInfo for ServoLayoutNode<'_> {
106 fn is_element(&self) -> bool {
107 self.node.is_element_for_layout()
108 }
109
110 fn is_text_node(&self) -> bool {
111 self.node.is_text_node_for_layout()
112 }
113}
114
115impl<'dom> style::dom::TNode for ServoLayoutNode<'dom> {
116 type ConcreteDocument = ServoLayoutDocument<'dom>;
117 type ConcreteElement = ServoLayoutElement<'dom>;
118 type ConcreteShadowRoot = ServoShadowRoot<'dom>;
119
120 fn parent_node(&self) -> Option<Self> {
121 self.node.parent_node_ref().map(Self::from_layout_js)
122 }
123
124 fn first_child(&self) -> Option<Self> {
125 self.node.first_child_ref().map(Self::from_layout_js)
126 }
127
128 fn last_child(&self) -> Option<Self> {
129 self.node.last_child_ref().map(Self::from_layout_js)
130 }
131
132 fn prev_sibling(&self) -> Option<Self> {
133 self.node.prev_sibling_ref().map(Self::from_layout_js)
134 }
135
136 fn next_sibling(&self) -> Option<Self> {
137 self.node.next_sibling_ref().map(Self::from_layout_js)
138 }
139
140 fn owner_doc(&self) -> Self::ConcreteDocument {
141 ServoLayoutDocument::from_layout_js(self.node.owner_doc_for_layout())
142 }
143
144 fn traversal_parent(&self) -> Option<ServoLayoutElement<'dom>> {
145 if let Some(assigned_slot) = self.assigned_slot() {
146 return Some(assigned_slot);
147 }
148 let parent = self.parent_node()?;
149 if let Some(shadow) = parent.as_shadow_root() {
150 return Some(shadow.host());
151 };
152 parent.as_element()
153 }
154
155 fn opaque(&self) -> style::dom::OpaqueNode {
156 self.get_jsmanaged().opaque()
157 }
158
159 fn debug_id(self) -> usize {
160 self.opaque().0
161 }
162
163 fn as_element(&self) -> Option<ServoLayoutElement<'dom>> {
164 self.node.downcast().map(ServoLayoutElement::from_layout_js)
165 }
166
167 fn as_document(&self) -> Option<ServoLayoutDocument<'dom>> {
168 self.node
169 .downcast()
170 .map(ServoLayoutDocument::from_layout_js)
171 }
172
173 fn as_shadow_root(&self) -> Option<ServoShadowRoot<'dom>> {
174 self.node.downcast().map(ServoShadowRoot::from_layout_js)
175 }
176
177 fn is_in_document(&self) -> bool {
178 unsafe { self.node.get_flag(NodeFlags::IS_IN_A_DOCUMENT_TREE) }
179 }
180}
181
182impl<'dom> LayoutNode<'dom> for ServoLayoutNode<'dom> {
183 type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'dom>;
184
185 fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode {
186 ServoThreadSafeLayoutNode::new(*self)
187 }
188
189 fn type_id(&self) -> LayoutNodeType {
190 NodeTypeIdWrapper(self.script_type_id()).into()
191 }
192
193 unsafe fn initialize_style_and_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self) {
194 let inner = self.get_jsmanaged();
195 if inner.style_data().is_none() {
196 unsafe { inner.initialize_style_data() };
197 }
198 if inner.layout_data().is_none() {
199 unsafe { inner.initialize_layout_data(Box::<RequestedLayoutDataType>::default()) };
200 }
201 }
202
203 fn is_connected(&self) -> bool {
204 unsafe { self.node.get_flag(NodeFlags::IS_CONNECTED) }
205 }
206
207 fn style_data(&self) -> Option<&'dom StyleData> {
208 self.get_jsmanaged().style_data()
209 }
210
211 fn layout_data(&self) -> Option<&'dom GenericLayoutData> {
212 self.get_jsmanaged().layout_data()
213 }
214}
215
216#[derive(Clone, Copy, Debug, PartialEq)]
220pub struct ServoThreadSafeLayoutNode<'dom> {
221 pub(super) node: ServoLayoutNode<'dom>,
223
224 pub(super) pseudo_element_chain: PseudoElementChain,
226}
227
228impl<'dom> ServoThreadSafeLayoutNode<'dom> {
229 pub fn new(node: ServoLayoutNode<'dom>) -> Self {
231 ServoThreadSafeLayoutNode {
232 node,
233 pseudo_element_chain: Default::default(),
234 }
235 }
236
237 unsafe fn get_jsmanaged(&self) -> LayoutDom<'dom, Node> {
240 self.node.get_jsmanaged()
241 }
242
243 unsafe fn dangerous_first_child(&self) -> Option<Self> {
246 let js_managed = unsafe { self.get_jsmanaged() };
247 js_managed
248 .first_child_ref()
249 .map(ServoLayoutNode::from_layout_js)
250 .map(Self::new)
251 }
252
253 unsafe fn dangerous_next_sibling(&self) -> Option<Self> {
256 let js_managed = unsafe { self.get_jsmanaged() };
257 js_managed
258 .next_sibling_ref()
259 .map(ServoLayoutNode::from_layout_js)
260 .map(Self::new)
261 }
262
263 pub fn is_single_line_text_input(&self) -> bool {
271 self.type_id() == Some(LayoutNodeType::Element(LayoutElementType::HTMLInputElement)) ||
272 (self.pseudo_element_chain.is_empty() &&
273 self.node.node.is_text_container_of_single_line_input())
274 }
275
276 pub fn is_text_input(&self) -> bool {
277 self.node.node.is_text_input()
278 }
279
280 pub fn selected_style(&self) -> Arc<ComputedValues> {
281 let Some(element) = self.as_element() else {
282 debug_assert!(self.is_text_node());
284 return self.parent_style();
285 };
286
287 let style_data = &element.style_data().styles;
288 let get_selected_style = || {
289 if self.node.node.is_in_ua_widget() {
293 return Some(element.containing_shadow_host()?.as_node().selected_style());
294 }
295 style_data.pseudos.get(&PseudoElement::Selection).cloned()
296 };
297
298 get_selected_style().unwrap_or_else(|| style_data.primary().clone())
299 }
300}
301
302impl style::dom::NodeInfo for ServoThreadSafeLayoutNode<'_> {
303 fn is_element(&self) -> bool {
304 self.node.is_element()
305 }
306
307 fn is_text_node(&self) -> bool {
308 self.node.is_text_node()
309 }
310}
311
312impl<'dom> ThreadSafeLayoutNode<'dom> for ServoThreadSafeLayoutNode<'dom> {
313 type ConcreteNode = ServoLayoutNode<'dom>;
314 type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'dom>;
315 type ConcreteElement = ServoLayoutElement<'dom>;
316 type ChildrenIterator = ServoThreadSafeLayoutNodeChildrenIterator<'dom>;
317
318 fn opaque(&self) -> style::dom::OpaqueNode {
319 unsafe { self.get_jsmanaged().opaque() }
320 }
321
322 fn pseudo_element_chain(&self) -> PseudoElementChain {
323 self.pseudo_element_chain
324 }
325
326 fn type_id(&self) -> Option<LayoutNodeType> {
327 if self.pseudo_element_chain.is_empty() {
328 Some(self.node.type_id())
329 } else {
330 None
331 }
332 }
333
334 fn parent_style(&self) -> Arc<ComputedValues> {
335 let parent_element = self.node.traversal_parent().unwrap();
336 let parent_data = parent_element.borrow_data().unwrap();
337 parent_data.styles.primary().clone()
338 }
339
340 fn initialize_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self) {
341 let inner = self.node.get_jsmanaged();
342 if inner.layout_data().is_none() {
343 unsafe {
344 inner.initialize_layout_data(Box::<RequestedLayoutDataType>::default());
345 }
346 }
347 }
348
349 fn debug_id(self) -> usize {
350 self.node.debug_id()
351 }
352
353 fn children(&self) -> style::dom::LayoutIterator<Self::ChildrenIterator> {
354 style::dom::LayoutIterator(ServoThreadSafeLayoutNodeChildrenIterator::new(*self))
355 }
356
357 fn as_element(&self) -> Option<ServoThreadSafeLayoutElement<'dom>> {
358 self.node
359 .as_element()
360 .map(|el| ServoThreadSafeLayoutElement {
361 element: el,
362 pseudo_element_chain: self.pseudo_element_chain,
363 })
364 }
365
366 fn as_html_element(&self) -> Option<ServoThreadSafeLayoutElement<'dom>> {
367 self.as_element()
368 .filter(|element| element.element.is_html_element())
369 }
370
371 fn style_data(&self) -> Option<&'dom StyleData> {
372 self.node.style_data()
373 }
374
375 fn layout_data(&self) -> Option<&'dom GenericLayoutData> {
376 self.node.layout_data()
377 }
378
379 fn unsafe_get(self) -> Self::ConcreteNode {
380 self.node
381 }
382
383 fn node_text_content(self) -> Cow<'dom, str> {
384 unsafe { self.get_jsmanaged().text_content() }
385 }
386
387 fn selection(&self) -> Option<Range<ByteIndex>> {
388 let this = unsafe { self.get_jsmanaged() };
389
390 this.selection().map(|range| {
391 Range::new(
392 ByteIndex(range.start as isize),
393 ByteIndex(range.len() as isize),
394 )
395 })
396 }
397
398 fn image_url(&self) -> Option<ServoUrl> {
399 let this = unsafe { self.get_jsmanaged() };
400 this.image_url()
401 }
402
403 fn image_density(&self) -> Option<f64> {
404 let this = unsafe { self.get_jsmanaged() };
405 this.image_density()
406 }
407
408 fn image_data(&self) -> Option<(Option<Image>, Option<ImageMetadata>)> {
409 let this = unsafe { self.get_jsmanaged() };
410 this.image_data()
411 }
412
413 fn canvas_data(&self) -> Option<HTMLCanvasData> {
414 let this = unsafe { self.get_jsmanaged() };
415 this.canvas_data()
416 }
417
418 fn media_data(&self) -> Option<HTMLMediaData> {
419 let this = unsafe { self.get_jsmanaged() };
420 this.media_data()
421 }
422
423 fn svg_data(&self) -> Option<SVGElementData> {
424 let this = unsafe { self.get_jsmanaged() };
425 this.svg_data()
426 }
427
428 fn iframe_browsing_context_id(&self) -> Option<BrowsingContextId> {
430 let this = unsafe { self.get_jsmanaged() };
431 this.iframe_browsing_context_id()
432 }
433
434 fn iframe_pipeline_id(&self) -> Option<PipelineId> {
436 let this = unsafe { self.get_jsmanaged() };
437 this.iframe_pipeline_id()
438 }
439
440 fn get_span(&self) -> Option<u32> {
441 unsafe {
442 self.get_jsmanaged()
443 .downcast::<Element>()
444 .unwrap()
445 .get_span()
446 }
447 }
448
449 fn get_colspan(&self) -> Option<u32> {
450 unsafe {
451 self.get_jsmanaged()
452 .downcast::<Element>()
453 .unwrap()
454 .get_colspan()
455 }
456 }
457
458 fn get_rowspan(&self) -> Option<u32> {
459 unsafe {
460 self.get_jsmanaged()
461 .downcast::<Element>()
462 .unwrap()
463 .get_rowspan()
464 }
465 }
466
467 fn with_pseudo_element_chain(&self, pseudo_element_chain: PseudoElementChain) -> Self {
468 Self {
469 node: self.node,
470 pseudo_element_chain,
471 }
472 }
473}
474
475pub enum ServoThreadSafeLayoutNodeChildrenIterator<'dom> {
476 Node(Option<ServoThreadSafeLayoutNode<'dom>>),
478 Slottables(<Vec<ServoLayoutNode<'dom>> as IntoIterator>::IntoIter),
480}
481
482impl<'dom> ServoThreadSafeLayoutNodeChildrenIterator<'dom> {
483 #[allow(unsafe_code)]
484 fn new(
485 parent: ServoThreadSafeLayoutNode<'dom>,
486 ) -> ServoThreadSafeLayoutNodeChildrenIterator<'dom> {
487 if let Some(element) = parent.as_element() {
488 if let Some(shadow) = element.shadow_root() {
489 return Self::new(shadow.as_node().to_threadsafe());
490 };
491
492 let slotted_nodes = element.slotted_nodes();
493 if !slotted_nodes.is_empty() {
494 #[allow(clippy::unnecessary_to_owned)] return Self::Slottables(slotted_nodes.to_owned().into_iter());
496 }
497 }
498
499 Self::Node(unsafe { parent.dangerous_first_child() })
500 }
501}
502
503impl<'dom> Iterator for ServoThreadSafeLayoutNodeChildrenIterator<'dom> {
504 type Item = ServoThreadSafeLayoutNode<'dom>;
505
506 fn next(&mut self) -> Option<Self::Item> {
507 match self {
508 Self::Node(node) => {
509 let next_sibling = unsafe { (*node)?.dangerous_next_sibling() };
510 std::mem::replace(node, next_sibling)
511 },
512 Self::Slottables(slots) => slots.next().map(|node| node.to_threadsafe()),
513 }
514 }
515}
516
517impl FusedIterator for ServoThreadSafeLayoutNodeChildrenIterator<'_> {}