script/layout_dom/
servo_layout_node.rs1#![expect(unsafe_code)]
6#![deny(missing_docs)]
7
8use std::fmt;
9
10use atomic_refcell::AtomicRef;
11use layout_api::{
12 GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutDataTrait, LayoutElement, LayoutNode,
13 LayoutNodeType, PseudoElementChain, SVGElementData, TrustedNodeAddress,
14};
15use net_traits::image_cache::Image;
16use pixels::ImageMetadata;
17use servo_arc::Arc;
18use servo_base::id::{BrowsingContextId, PipelineId};
19use servo_base::text::{RangeAny, Utf32CodeUnits};
20use servo_url::ServoUrl;
21use style;
22use style::context::SharedStyleContext;
23use style::dom::{LayoutIterator, NodeInfo};
24use style::properties::ComputedValues;
25use style::selector_parser::PseudoElement;
26
27use super::ServoLayoutElement;
28use crate::dom::bindings::root::LayoutDom;
29use crate::dom::element::Element;
30use crate::dom::layout_dom::NodeTypeIdWrapper;
31use crate::dom::node::{Node, NodeFlags};
32use crate::layout_dom::{
33 ServoDangerousStyleNode, ServoLayoutDomTypeBundle, ServoLayoutNodeChildrenIterator,
34};
35
36impl fmt::Debug for LayoutDom<'_, Node> {
37 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38 if let Some(element) = self.downcast::<Element>() {
39 element.fmt(f)
40 } else if self.is_text_node_for_layout() {
41 write!(f, "<text node> ({:#x})", self.opaque().0)
42 } else {
43 write!(f, "<non-text node> ({:#x})", self.opaque().0)
44 }
45 }
46}
47
48#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
51pub struct ServoLayoutNode<'dom> {
52 pub(super) node: LayoutDom<'dom, Node>,
54 pub(super) pseudo_element_chain: PseudoElementChain,
56}
57
58unsafe impl Send for ServoLayoutNode<'_> {}
62unsafe impl Sync for ServoLayoutNode<'_> {}
63
64impl<'dom> ServoLayoutNode<'dom> {
65 pub unsafe fn new(address: &TrustedNodeAddress) -> Self {
71 unsafe { LayoutDom::from_trusted_node_address(*address) }.into()
72 }
73
74 pub(super) unsafe fn dangerous_first_child(&self) -> Option<Self> {
82 self.node.first_child_ref().map(Into::into)
83 }
84
85 pub(super) unsafe fn dangerous_next_sibling(&self) -> Option<Self> {
93 self.node.next_sibling_ref().map(Into::into)
94 }
95
96 pub(super) unsafe fn dangerous_previous_sibling(&self) -> Option<Self> {
104 self.node.prev_sibling_ref().map(Into::into)
105 }
106}
107
108impl<'dom> From<LayoutDom<'dom, Node>> for ServoLayoutNode<'dom> {
109 fn from(node: LayoutDom<'dom, Node>) -> Self {
110 Self {
111 node,
112 pseudo_element_chain: Default::default(),
113 }
114 }
115}
116
117impl<'dom> LayoutNode<'dom> for ServoLayoutNode<'dom> {
118 type ConcreteTypeBundle = ServoLayoutDomTypeBundle<'dom>;
119
120 fn with_pseudo(&self, pseudo_element_type: PseudoElement) -> Option<Self> {
121 Some(
122 self.as_element()?
123 .with_pseudo(pseudo_element_type)?
124 .as_node(),
125 )
126 }
127
128 unsafe fn dangerous_style_node(self) -> ServoDangerousStyleNode<'dom> {
129 self.node.into()
130 }
131
132 unsafe fn dangerous_dom_parent(self) -> Option<Self> {
133 self.node.parent_node_ref().map(Into::into)
134 }
135
136 unsafe fn dangerous_flat_tree_parent(self) -> Option<Self> {
137 self.node
138 .traversal_parent()
139 .map(|parent_element| parent_element.upcast().into())
140 }
141
142 fn is_connected(&self) -> bool {
143 unsafe { self.node.get_flag(NodeFlags::IS_CONNECTED) }
144 }
145
146 fn layout_data(&self) -> Option<&'dom GenericLayoutData> {
147 self.node.layout_data()
148 }
149
150 fn opaque(&self) -> style::dom::OpaqueNode {
151 self.node.opaque()
152 }
153
154 fn pseudo_element_chain(&self) -> PseudoElementChain {
155 self.pseudo_element_chain
156 }
157
158 fn type_id(&self) -> Option<LayoutNodeType> {
159 if self.pseudo_element_chain.is_empty() {
160 Some(NodeTypeIdWrapper(self.node.type_id_for_layout()).into())
161 } else {
162 None
163 }
164 }
165
166 fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
167 if let Some(element) = self.as_element() {
168 element.style(context)
169 } else {
170 debug_assert!(self.is_text_node());
173 self.parent_style(context)
174 }
175 }
176
177 fn parent_style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
178 if let Some(chain) = self.pseudo_element_chain.without_innermost() {
179 let mut parent = *self;
180 parent.pseudo_element_chain = chain;
181 return parent.style(context);
182 }
183 unsafe { self.dangerous_flat_tree_parent() }
184 .unwrap()
185 .style(context)
186 }
187
188 fn selected_style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
189 if self.node.is_in_ua_widget() &&
193 let Some(shadow_root) = self.node.containing_shadow_root_for_layout()
194 {
195 return Self::from(shadow_root.get_host_for_layout().upcast()).selected_style(context);
196 }
197
198 let Some(element) = self.as_element() else {
199 return unsafe { self.dangerous_flat_tree_parent() }
200 .expect("Non-element should always have parent")
201 .selected_style(context);
202 };
203
204 let selected_style = element
205 .element_data()
206 .styles
207 .pseudos
208 .get(&PseudoElement::Selection)
209 .cloned();
210 selected_style.unwrap_or_else(|| self.style(context))
211 }
212
213 fn initialize_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self) {
214 if self.node.layout_data().is_none() {
215 unsafe {
216 self.node
217 .initialize_layout_data(Box::<RequestedLayoutDataType>::default());
218 }
219 }
220 }
221
222 fn flat_tree_children(&self) -> impl Iterator<Item = Self> {
223 LayoutIterator(ServoLayoutNodeChildrenIterator::new_for_flat_tree(*self))
224 }
225
226 fn dom_children(&self) -> impl Iterator<Item = Self> {
227 LayoutIterator(ServoLayoutNodeChildrenIterator::new_for_dom_tree(*self))
228 }
229
230 fn as_element(&self) -> Option<ServoLayoutElement<'dom>> {
231 self.node.downcast().map(|element| ServoLayoutElement {
232 element,
233 pseudo_element_chain: self.pseudo_element_chain,
234 })
235 }
236
237 fn as_html_element(&self) -> Option<ServoLayoutElement<'dom>> {
238 self.as_element()
239 .filter(|element| element.is_html_element())
240 }
241
242 fn text_content(self) -> AtomicRef<'dom, str> {
243 self.node.text_content()
244 }
245
246 fn text_node_selection(&self) -> Option<RangeAny<Utf32CodeUnits>> {
247 if !self.pseudo_element_chain.is_empty() {
249 return None;
250 }
251
252 self.node.selection_for_text_node()
253 }
254
255 fn text_node_paints_caret(&self) -> bool {
256 self.node.text_node_paints_caret()
257 }
258
259 fn text_node_needs_caret_placeholder(&self) -> bool {
260 self.node.text_node_needs_caret_placeholder()
261 }
262
263 fn replaced_is_selected(&self) -> bool {
264 self.node.replaced_is_selected()
265 }
266
267 fn image_url(&self) -> Option<ServoUrl> {
268 self.node.image_url()
269 }
270
271 fn image_density(&self) -> Option<f64> {
272 self.node.image_density()
273 }
274
275 fn showing_broken_image_icon(&self) -> bool {
276 self.node.showing_broken_image_icon()
277 }
278
279 fn image_data(&self) -> Option<(Option<Image>, Option<ImageMetadata>)> {
280 self.node.image_data()
281 }
282
283 fn canvas_data(&self) -> Option<HTMLCanvasData> {
284 self.node.canvas_data()
285 }
286
287 fn media_data(&self) -> Option<HTMLMediaData> {
288 self.node.media_data()
289 }
290
291 fn svg_data(&self) -> Option<SVGElementData<'dom>> {
292 self.node.svg_data()
293 }
294
295 fn iframe_browsing_context_id(&self) -> Option<BrowsingContextId> {
296 self.node.iframe_browsing_context_id()
297 }
298
299 fn iframe_pipeline_id(&self) -> Option<PipelineId> {
300 self.node.iframe_pipeline_id()
301 }
302
303 fn table_span(&self) -> Option<u32> {
304 self.node
305 .downcast::<Element>()
306 .and_then(|element| element.get_span())
307 }
308
309 fn table_colspan(&self) -> Option<u32> {
310 self.node
311 .downcast::<Element>()
312 .and_then(|element| element.get_colspan())
313 }
314
315 fn table_rowspan(&self) -> Option<u32> {
316 self.node
317 .downcast::<Element>()
318 .and_then(|element| element.get_rowspan())
319 }
320
321 fn set_uses_content_attribute_with_attr(&self, uses_content_attribute_with_attr: bool) {
322 unsafe {
323 self.node.set_flag(
324 NodeFlags::USES_ATTR_IN_CONTENT_ATTRIBUTE,
325 uses_content_attribute_with_attr,
326 )
327 }
328 }
329
330 fn is_single_line_text_input(&self) -> bool {
331 self.pseudo_element_chain.is_empty() && self.node.is_text_container_of_single_line_input()
332 }
333
334 fn is_root_of_user_agent_widget(&self) -> bool {
335 self.node.is_root_of_user_agent_widget()
336 }
337}
338
339impl NodeInfo for ServoLayoutNode<'_> {
340 fn is_element(&self) -> bool {
341 self.node.is_element_for_layout()
342 }
343
344 fn is_text_node(&self) -> bool {
345 self.node.is_text_node_for_layout()
346 }
347}