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