1use app_units::Au;
6use atomic_refcell::{AtomicRef, AtomicRefCell};
7use style::properties::ComputedValues;
8use style::values::computed::CSSPixelLength;
9use style::values::computed::length_percentage::CalcLengthPercentage;
10use style::values::specified::align::AlignFlags;
11use style::values::specified::box_::DisplayInside;
12use style::{Atom, Zero};
13use taffy::style_helpers::{TaffyMaxContent, TaffyMinContent};
14use taffy::{AvailableSpace, MaybeMath, RequestedAxis, RunMode};
15
16use super::{
17 SpecificTaffyGridInfo, TaffyContainer, TaffyItemBox, TaffyItemBoxInner, TaffyStyloStyle,
18};
19use crate::cell::ArcRefCell;
20use crate::context::LayoutContext;
21use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
22use crate::fragment_tree::{
23 BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo,
24};
25use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize};
26use crate::layout_box_base::CacheableLayoutResult;
27use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
28use crate::sizing::{
29 ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, LazySize, SizeConstraint,
30};
31use crate::style_ext::LayoutStyle;
32use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize};
33
34const DUMMY_NODE_ID: taffy::NodeId = taffy::NodeId::new(u64::MAX);
35
36fn resolve_content_size(constraint: AvailableSpace, content_sizes: ContentSizes) -> f32 {
37 match constraint {
38 AvailableSpace::Definite(limit) => {
39 let min = content_sizes.min_content.to_f32_px();
40 let max = content_sizes.max_content.to_f32_px();
41 limit.min(max).max(min)
42 },
43 AvailableSpace::MinContent => content_sizes.min_content.to_f32_px(),
44 AvailableSpace::MaxContent => content_sizes.max_content.to_f32_px(),
45 }
46}
47
48#[inline(always)]
49fn with_independant_formatting_context<T>(
50 item: &mut TaffyItemBoxInner,
51 cb: impl FnOnce(&IndependentFormattingContext) -> T,
52) -> T {
53 match item {
54 TaffyItemBoxInner::InFlowBox(context) => cb(context),
55 TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(abspos_box) => {
56 cb(&AtomicRefCell::borrow(abspos_box).context)
57 },
58 }
59}
60
61struct TaffyContainerContext<'a> {
64 source_child_nodes: &'a [ArcRefCell<TaffyItemBox>],
65 layout_context: &'a LayoutContext<'a>,
66 positioning_context: &'a mut PositioningContext,
67 content_box_size_override: &'a ContainingBlock<'a>,
68 style: &'a ComputedValues,
69 specific_layout_info: Option<SpecificLayoutInfo>,
70
71 child_specific_layout_infos: Vec<Option<SpecificLayoutInfo>>,
73}
74
75struct ChildIter(std::ops::Range<usize>);
76impl Iterator for ChildIter {
77 type Item = taffy::NodeId;
78 fn next(&mut self) -> Option<Self::Item> {
79 self.0.next().map(taffy::NodeId::from)
80 }
81}
82
83impl taffy::TraversePartialTree for TaffyContainerContext<'_> {
84 type ChildIter<'a>
85 = ChildIter
86 where
87 Self: 'a;
88
89 fn child_ids(&self, _node_id: taffy::NodeId) -> Self::ChildIter<'_> {
90 ChildIter(0..self.source_child_nodes.len())
91 }
92
93 fn child_count(&self, _node_id: taffy::NodeId) -> usize {
94 self.source_child_nodes.len()
95 }
96
97 fn get_child_id(&self, _node_id: taffy::NodeId, index: usize) -> taffy::NodeId {
98 taffy::NodeId::from(index)
99 }
100}
101
102impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
103 type CustomIdent = Atom;
104
105 type CoreContainerStyle<'a>
106 = TaffyStyloStyle<&'a ComputedValues>
107 where
108 Self: 'a;
109
110 fn get_core_container_style(&self, _node_id: taffy::NodeId) -> Self::CoreContainerStyle<'_> {
111 TaffyStyloStyle::new(self.style, false )
112 }
113
114 fn set_unrounded_layout(&mut self, node_id: taffy::NodeId, layout: &taffy::Layout) {
115 let id = usize::from(node_id);
116 (*self.source_child_nodes[id]).borrow_mut().taffy_layout = *layout;
117 }
118
119 #[expect(unsafe_code)]
120 fn resolve_calc_value(&self, val: *const (), basis: f32) -> f32 {
121 let calc = unsafe { &*(val as *const CalcLengthPercentage) };
129 calc.resolve(CSSPixelLength::new(basis)).px()
130 }
131
132 fn compute_child_layout(
133 &mut self,
134 node_id: taffy::NodeId,
135 inputs: taffy::LayoutInput,
136 ) -> taffy::LayoutOutput {
137 let mut child = (*self.source_child_nodes[usize::from(node_id)]).borrow_mut();
138 let child = &mut *child;
139
140 with_independant_formatting_context(
141 &mut child.taffy_level_box,
142 |independent_context| -> taffy::LayoutOutput {
143 let containing_block = &self.content_box_size_override;
145 let style = independent_context.style();
146
147 let pbm = independent_context
149 .layout_style()
150 .padding_border_margin(containing_block);
151 let pb_sum = pbm.padding_border_sums.map(|v| v.to_f32_px());
152 let margin_sum = pbm.margin.auto_is(Au::zero).sum().map(|v| v.to_f32_px());
153 let content_box_inset = pb_sum + margin_sum;
154 let content_box_known_dimensions = taffy::Size {
155 width: inputs
156 .known_dimensions
157 .width
158 .map(|width| width - pb_sum.inline),
159 height: inputs
160 .known_dimensions
161 .height
162 .map(|height| height - pb_sum.block),
163 };
164 let preferred_aspect_ratio =
165 independent_context.preferred_aspect_ratio(&pbm.padding_border_sums);
166
167 let tentative_block_size = content_box_known_dimensions
169 .height
170 .map(Au::from_f32_px)
171 .map_or_else(SizeConstraint::default, SizeConstraint::Definite);
172
173 let inline_size = content_box_known_dimensions.width.unwrap_or_else(|| {
175 let constraint_space = ConstraintSpace {
176 block_size: tentative_block_size,
177 style,
178 preferred_aspect_ratio,
179 };
180
181 let result = independent_context
183 .inline_content_sizes(self.layout_context, &constraint_space);
184 let adjusted_available_space = inputs
185 .available_space
186 .width
187 .map_definite_value(|width| width - content_box_inset.inline);
188
189 resolve_content_size(adjusted_available_space, result.sizes)
190 });
191
192 if inputs.run_mode == RunMode::ComputeSize &&
194 inputs.axis == RequestedAxis::Horizontal
195 {
196 return taffy::LayoutOutput::from_outer_size(taffy::Size {
197 width: inline_size + pb_sum.inline,
198 height: 0.0,
200 });
201 }
202
203 let content_box_size_override = ContainingBlock {
204 size: ContainingBlockSize {
205 inline: Au::from_f32_px(inline_size),
206 block: tentative_block_size,
207 },
208 style,
209 };
210
211 let lazy_block_size = match content_box_known_dimensions.height {
212 None => LazySize::intrinsic(),
214 Some(height) => Au::from_f32_px(height).into(),
215 };
216
217 child.positioning_context = PositioningContext::default();
218 let layout = independent_context.layout(
219 self.layout_context,
220 &mut child.positioning_context,
221 &content_box_size_override,
222 containing_block,
223 preferred_aspect_ratio,
224 &lazy_block_size,
225 );
226
227 child.child_fragments = layout.fragments;
228 self.child_specific_layout_infos[usize::from(node_id)] =
229 layout.specific_layout_info;
230
231 let block_size = lazy_block_size
232 .resolve(|| layout.content_block_size)
233 .to_f32_px();
234
235 let computed_size = taffy::Size {
236 width: inline_size + pb_sum.inline,
237 height: block_size + pb_sum.block,
238 };
239 let size = inputs.known_dimensions.unwrap_or(computed_size);
240
241 taffy::LayoutOutput {
242 size,
243 first_baselines: taffy::Point {
244 x: None,
245 y: layout.baselines.first.map(|au| au.to_f32_px()),
246 },
247 ..taffy::LayoutOutput::DEFAULT
248 }
249 },
250 )
251 }
252}
253
254impl taffy::LayoutGridContainer for TaffyContainerContext<'_> {
255 type GridContainerStyle<'a>
256 = TaffyStyloStyle<&'a ComputedValues>
257 where
258 Self: 'a;
259
260 type GridItemStyle<'a>
261 = TaffyStyloStyle<AtomicRef<'a, ComputedValues>>
262 where
263 Self: 'a;
264
265 fn get_grid_container_style(
266 &self,
267 _node_id: taffy::prelude::NodeId,
268 ) -> Self::GridContainerStyle<'_> {
269 TaffyStyloStyle::new(self.style, false )
270 }
271
272 fn get_grid_child_style(
273 &self,
274 child_node_id: taffy::prelude::NodeId,
275 ) -> Self::GridItemStyle<'_> {
276 let id = usize::from(child_node_id);
277 let child = (*self.source_child_nodes[id]).borrow();
278 let is_replaced = child.is_in_flow_replaced();
280 let stylo_style = AtomicRef::map(child, |c| &*c.style);
281 TaffyStyloStyle::new(stylo_style, is_replaced)
282 }
283
284 fn set_detailed_grid_info(
285 &mut self,
286 _node_id: taffy::NodeId,
287 specific_layout_info: taffy::DetailedGridInfo,
288 ) {
289 self.specific_layout_info = Some(SpecificLayoutInfo::Grid(Box::new(
290 SpecificTaffyGridInfo::from_detailed_grid_layout(specific_layout_info),
291 )));
292 }
293}
294
295impl ComputeInlineContentSizes for TaffyContainer {
296 fn compute_inline_content_sizes(
297 &self,
298 layout_context: &LayoutContext,
299 _constraint_space: &ConstraintSpace,
300 ) -> InlineContentSizesResult {
301 let style = &self.style;
302
303 let max_content_inputs = taffy::LayoutInput {
304 run_mode: taffy::RunMode::ComputeSize,
305 sizing_mode: taffy::SizingMode::InherentSize,
306 axis: taffy::RequestedAxis::Horizontal,
307 vertical_margins_are_collapsible: taffy::Line::FALSE,
308
309 known_dimensions: taffy::Size::NONE,
310 parent_size: taffy::Size::NONE,
311 available_space: taffy::Size::MAX_CONTENT,
312 };
313
314 let min_content_inputs = taffy::LayoutInput {
315 available_space: taffy::Size::MIN_CONTENT,
316 ..max_content_inputs
317 };
318
319 let containing_block = &ContainingBlock {
320 size: ContainingBlockSize {
321 inline: Au::zero(),
322 block: SizeConstraint::default(),
323 },
324 style,
325 };
326
327 let mut grid_context = TaffyContainerContext {
328 layout_context,
329 positioning_context: &mut PositioningContext::default(),
330 content_box_size_override: containing_block,
331 style,
332 source_child_nodes: &self.children,
333 specific_layout_info: None,
334 child_specific_layout_infos: vec![None; self.children.len()],
335 };
336
337 let (max_content_output, min_content_output) = match style.clone_display().inside() {
338 DisplayInside::Grid => {
339 let max_content_output = taffy::compute_grid_layout(
340 &mut grid_context,
341 DUMMY_NODE_ID,
342 max_content_inputs,
343 );
344 let min_content_output = taffy::compute_grid_layout(
345 &mut grid_context,
346 DUMMY_NODE_ID,
347 min_content_inputs,
348 );
349 (max_content_output, min_content_output)
350 },
351 _ => panic!("Servo is only configured to use Taffy for CSS Grid layout"),
352 };
353
354 let pb_sums = self
355 .layout_style()
356 .padding_border_margin(containing_block)
357 .padding_border_sums;
358
359 InlineContentSizesResult {
360 sizes: ContentSizes {
361 max_content: Au::from_f32_px(max_content_output.size.width) - pb_sums.inline,
362 min_content: Au::from_f32_px(min_content_output.size.width) - pb_sums.inline,
363 },
364
365 depends_on_block_constraints: true,
370 }
371 }
372}
373
374impl TaffyContainer {
375 pub(crate) fn layout(
377 &self,
378 layout_context: &LayoutContext,
379 positioning_context: &mut PositioningContext,
380 content_box_size_override: &ContainingBlock,
381 containing_block: &ContainingBlock,
382 ) -> CacheableLayoutResult {
383 let mut container_ctx = TaffyContainerContext {
384 layout_context,
385 positioning_context,
386 content_box_size_override,
387 style: content_box_size_override.style,
388 source_child_nodes: &self.children,
389 specific_layout_info: None,
390 child_specific_layout_infos: vec![None; self.children.len()],
391 };
392
393 let container_style = &content_box_size_override.style;
394 let align_items = container_style.clone_align_items();
395 let justify_items = container_style.clone_justify_items();
396 let pbm = self.layout_style().padding_border_margin(containing_block);
397
398 let known_dimensions = taffy::Size {
399 width: Some(
400 (content_box_size_override.size.inline + pbm.padding_border_sums.inline)
401 .to_f32_px(),
402 ),
403 height: content_box_size_override
404 .size
405 .block
406 .to_definite()
407 .map(Au::to_f32_px)
408 .maybe_add(pbm.padding_border_sums.block.to_f32_px()),
409 };
410
411 let taffy_containing_block = taffy::Size {
412 width: Some(containing_block.size.inline.to_f32_px()),
413 height: containing_block.size.block.to_definite().map(Au::to_f32_px),
414 };
415
416 let layout_input = taffy::LayoutInput {
417 run_mode: taffy::RunMode::PerformLayout,
418 sizing_mode: taffy::SizingMode::InherentSize,
419 axis: taffy::RequestedAxis::Vertical,
420 vertical_margins_are_collapsible: taffy::Line::FALSE,
421
422 known_dimensions,
423 parent_size: taffy_containing_block,
424 available_space: taffy_containing_block.map(AvailableSpace::from),
425 };
426
427 let output = match container_ctx.style.clone_display().inside() {
428 DisplayInside::Grid => {
429 taffy::compute_grid_layout(&mut container_ctx, DUMMY_NODE_ID, layout_input)
430 },
431 _ => panic!("Servo is only configured to use Taffy for CSS Grid layout"),
432 };
433
434 let fragments: Vec<Fragment> = self
437 .children
438 .iter()
439 .map(|child| (**child).borrow_mut())
440 .enumerate()
441 .map(|(child_id, mut child)| {
442 fn rect_to_physical_sides<T>(rect: taffy::Rect<T>) -> PhysicalSides<T> {
443 PhysicalSides::new(rect.top, rect.right, rect.bottom, rect.left)
444 }
445
446 fn size_and_pos_to_logical_rect<T: Default>(
447 position: taffy::Point<T>,
448 size: taffy::Size<T>,
449 ) -> PhysicalRect<T> {
450 PhysicalRect::new(
451 PhysicalPoint::new(position.x, position.y),
452 PhysicalSize::new(size.width, size.height),
453 )
454 }
455
456 let layout = &child.taffy_layout;
457
458 let padding = rect_to_physical_sides(layout.padding.map(Au::from_f32_px));
459 let border = rect_to_physical_sides(layout.border.map(Au::from_f32_px));
460 let margin = rect_to_physical_sides(layout.margin.map(Au::from_f32_px));
461
462 let content_size = size_and_pos_to_logical_rect(
467 taffy::Point {
468 x: Au::from_f32_px(
469 layout.location.x + layout.padding.left + layout.border.left,
470 ) - pbm.padding.inline_start -
471 pbm.border.inline_start,
472 y: Au::from_f32_px(
473 layout.location.y + layout.padding.top + layout.border.top,
474 ) - pbm.padding.block_start -
475 pbm.border.block_start,
476 },
477 taffy::Size {
478 width: layout.size.width -
479 layout.padding.left -
480 layout.padding.right -
481 layout.border.left -
482 layout.border.right,
483 height: layout.size.height -
484 layout.padding.top -
485 layout.padding.bottom -
486 layout.border.top -
487 layout.border.bottom,
488 }
489 .map(Au::from_f32_px),
490 );
491
492 let child_specific_layout_info: Option<SpecificLayoutInfo> =
493 std::mem::take(&mut container_ctx.child_specific_layout_infos[child_id]);
494
495 let fragment = match &mut child.taffy_level_box {
496 TaffyItemBoxInner::InFlowBox(independent_box) => {
497 let mut fragment_info = independent_box.base_fragment_info();
498 fragment_info
499 .flags
500 .insert(FragmentFlags::IS_FLEX_OR_GRID_ITEM);
501 let mut box_fragment = BoxFragment::new(
502 fragment_info,
503 independent_box.style().clone(),
504 std::mem::take(&mut child.child_fragments),
505 content_size,
506 padding,
507 border,
508 margin,
509 child_specific_layout_info,
510 )
511 .with_baselines(Baselines {
512 first: output.first_baselines.y.map(Au::from_f32_px),
513 last: None,
514 });
515
516 child.positioning_context.layout_collected_children(
517 container_ctx.layout_context,
518 &mut box_fragment,
519 );
520 child
521 .positioning_context
522 .adjust_static_position_of_hoisted_fragments_with_offset(
523 &box_fragment.content_rect.origin.to_vector(),
524 PositioningContextLength::zero(),
525 );
526 container_ctx
527 .positioning_context
528 .append(std::mem::take(&mut child.positioning_context));
529
530 Fragment::Box(ArcRefCell::new(box_fragment))
531 },
532 TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(abs_pos_box) => {
533 fn resolve_alignment(value: AlignFlags, auto: AlignFlags) -> AlignFlags {
534 match value {
535 AlignFlags::AUTO => auto,
536 AlignFlags::NORMAL => AlignFlags::STRETCH,
537 value => value,
538 }
539 }
540
541 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
542 abs_pos_box.clone(),
543 PhysicalRect::from_size(PhysicalSize::new(
544 Au::from_f32_px(output.size.width),
545 Au::from_f32_px(output.size.height),
546 )),
547 LogicalVec2 {
548 inline: resolve_alignment(
549 child.style.clone_align_self().0,
550 align_items.0,
551 ),
552 block: resolve_alignment(
553 child.style.clone_justify_self().0,
554 justify_items.computed.0.0,
555 ),
556 },
557 container_ctx.style.writing_mode,
558 );
559 let hoisted_fragment = hoisted_box.fragment.clone();
560 container_ctx.positioning_context.push(hoisted_box);
561 Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)
562 },
563 };
564
565 if let TaffyItemBoxInner::InFlowBox(independent_formatting_context) =
566 &child.taffy_level_box
567 {
568 independent_formatting_context
569 .base
570 .set_fragment(fragment.clone());
571 }
572 fragment
573 })
574 .collect();
575
576 CacheableLayoutResult {
577 fragments,
578 content_block_size: Au::from_f32_px(output.size.height) - pbm.padding_border_sums.block,
579 content_inline_size_for_table: None,
580 baselines: Baselines::default(),
581
582 depends_on_block_constraints: true,
587 specific_layout_info: container_ctx.specific_layout_info,
588 collapsible_margins_in_children: CollapsedBlockMargins::zero(),
589 }
590 }
591
592 #[inline]
593 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
594 LayoutStyle::Default(&self.style)
595 }
596}