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