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