Skip to main content

layout/taffy/
layout.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use app_units::Au;
6use atomic_refcell::{AtomicRef, AtomicRefCell};
7use style::logical_geometry::LogicalAxis;
8use style::properties::ComputedValues;
9use style::values::computed::CSSPixelLength;
10use style::values::computed::length_percentage::CalcLengthPercentage;
11use style::values::specified::align::AlignFlags;
12use style::values::specified::box_::DisplayInside;
13use style::{Atom, Zero};
14use taffy::style_helpers::{TaffyMaxContent, TaffyMinContent};
15use taffy::{AvailableSpace, MaybeMath, RequestedAxis, RunMode};
16
17use super::{
18    SpecificTaffyGridInfo, TaffyContainer, TaffyItemBox, TaffyItemBoxInner, TaffyStyloStyle,
19};
20use crate::cell::ArcRefCell;
21use crate::context::LayoutContext;
22use crate::dom::WeakLayoutBox;
23use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
24use crate::fragment_tree::{
25    BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo,
26};
27use crate::geom::{
28    LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, PhysicalVec,
29};
30use crate::layout_box_base::IndependentFormattingContextLayoutResult;
31use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
32use crate::sizing::{
33    ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, LazySize, Size,
34    SizeConstraint,
35};
36use crate::style_ext::{Clamp, ContentBoxSizesAndPBM, LayoutStyle};
37use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock};
38
39const DUMMY_NODE_ID: taffy::NodeId = taffy::NodeId::new(u64::MAX);
40
41fn resolve_content_size(constraint: AvailableSpace, content_sizes: ContentSizes) -> f32 {
42    match constraint {
43        AvailableSpace::Definite(limit) => {
44            let min = content_sizes.min_content.to_f32_px();
45            let max = content_sizes.max_content.to_f32_px();
46            limit.min(max).max(min)
47        },
48        AvailableSpace::MinContent => content_sizes.min_content.to_f32_px(),
49        AvailableSpace::MaxContent => content_sizes.max_content.to_f32_px(),
50    }
51}
52
53#[inline(always)]
54fn with_independent_formatting_context<T>(
55    item: &mut TaffyItemBoxInner,
56    cb: impl FnOnce(&IndependentFormattingContext) -> T,
57) -> T {
58    match item {
59        TaffyItemBoxInner::InFlowBox(context) => cb(context),
60        TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(abspos_box) => {
61            cb(&AtomicRefCell::borrow(abspos_box).context)
62        },
63    }
64}
65
66/// Layout parameters and intermediate results about a taffy container,
67/// grouped to avoid passing around many parameters
68struct TaffyContainerContext<'a> {
69    source_child_nodes: &'a [ArcRefCell<TaffyItemBox>],
70    layout_context: &'a LayoutContext<'a>,
71    positioning_context: &'a mut PositioningContext,
72    content_box_size_override: &'a ContainingBlock<'a>,
73    style: &'a ComputedValues,
74    fragment_flags: FragmentFlags,
75    specific_layout_info: Option<SpecificLayoutInfo>,
76
77    /// Temporary location for children specific info, which will be moved into child fragments
78    child_specific_layout_infos: Vec<Option<SpecificLayoutInfo>>,
79}
80
81struct ChildIter(std::ops::Range<usize>);
82impl Iterator for ChildIter {
83    type Item = taffy::NodeId;
84    fn next(&mut self) -> Option<Self::Item> {
85        self.0.next().map(taffy::NodeId::from)
86    }
87
88    fn size_hint(&self) -> (usize, Option<usize>) {
89        (self.0.len(), Some(self.0.len()))
90    }
91}
92
93impl taffy::TraversePartialTree for TaffyContainerContext<'_> {
94    type ChildIter<'a>
95        = ChildIter
96    where
97        Self: 'a;
98
99    fn child_ids(&self, _node_id: taffy::NodeId) -> Self::ChildIter<'_> {
100        ChildIter(0..self.source_child_nodes.len())
101    }
102
103    fn child_count(&self, _node_id: taffy::NodeId) -> usize {
104        self.source_child_nodes.len()
105    }
106
107    fn get_child_id(&self, _node_id: taffy::NodeId, index: usize) -> taffy::NodeId {
108        taffy::NodeId::from(index)
109    }
110}
111
112impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
113    type CustomIdent = Atom;
114
115    type CoreContainerStyle<'a>
116        = TaffyStyloStyle<&'a ComputedValues>
117    where
118        Self: 'a;
119
120    fn get_core_container_style(&self, _node_id: taffy::NodeId) -> Self::CoreContainerStyle<'_> {
121        TaffyStyloStyle::new(self.style, self.fragment_flags)
122    }
123
124    fn set_unrounded_layout(&mut self, node_id: taffy::NodeId, layout: &taffy::Layout) {
125        let id = usize::from(node_id);
126        (*self.source_child_nodes[id]).borrow_mut().taffy_layout = *layout;
127    }
128
129    #[expect(unsafe_code)]
130    fn resolve_calc_value(&self, val: *const (), basis: f32) -> f32 {
131        // SAFETY:
132        // - The calc `val` here is the same pointer we return to Taffy in `convert::length_percentage`
133        //   so it is safe to cast the type back to `*const CalcLengthPercentage`
134        // - Taffy guarantees that it never retains style values beyond the scope of it's style
135        //   computation methods, so we can be sure that the pointer we have passed it is still valid.
136        // - The reference we create here has a lifetime that does not escape this function, so it does
137        //   not matter if the pointer is later destroyed.
138        let calc = unsafe { &*(val as *const CalcLengthPercentage) };
139        calc.resolve(CSSPixelLength::new(basis)).px()
140    }
141
142    fn compute_child_layout(
143        &mut self,
144        node_id: taffy::NodeId,
145        inputs: taffy::LayoutInput,
146    ) -> taffy::LayoutOutput {
147        let mut child = (*self.source_child_nodes[usize::from(node_id)]).borrow_mut();
148        let child = &mut *child;
149
150        let output = with_independent_formatting_context(
151            &mut child.taffy_level_box,
152            |independent_context| -> taffy::LayoutOutput {
153                // TODO: re-evaluate sizing constraint conversions in light of recent layout changes
154                let containing_block = self.content_box_size_override;
155                let style = independent_context.style();
156
157                let child_containing_block = IndefiniteContainingBlock {
158                    size: LogicalVec2 {
159                        inline: inputs.parent_size.width.map(Au::from_f32_px),
160                        block: inputs.parent_size.height.map(Au::from_f32_px),
161                    },
162                    style: containing_block.style,
163                };
164
165                // Adjust known_dimensions from border box to content box
166                let ContentBoxSizesAndPBM {
167                    content_box_sizes,
168                    pbm,
169                    ..
170                } = independent_context
171                    .layout_style()
172                    .content_box_sizes_and_padding_border_margin(&child_containing_block);
173                let pb_sum = pbm.padding_border_sums.map(|v| v.to_f32_px());
174                let content_box_known_dimensions = taffy::Size {
175                    width: inputs
176                        .known_dimensions
177                        .width
178                        .map(|width| width - pb_sum.inline),
179                    height: inputs
180                        .known_dimensions
181                        .height
182                        .map(|height| height - pb_sum.block),
183                };
184                let preferred_aspect_ratio =
185                    independent_context.preferred_aspect_ratio(&pbm.padding_border_sums);
186
187                // TODO: pass min- and max- size
188                let tentative_block_size = content_box_known_dimensions
189                    .height
190                    .map(Au::from_f32_px)
191                    .map_or_else(SizeConstraint::default, SizeConstraint::Definite);
192
193                // Compute inline size
194                let inline_size = content_box_known_dimensions.width.unwrap_or_else(|| {
195                    let constraint_space = ConstraintSpace {
196                        block_size: tentative_block_size,
197                        style,
198                        preferred_aspect_ratio,
199                    };
200
201                    // TODO: pass min- and max- size
202                    let result = independent_context
203                        .inline_content_sizes(self.layout_context, &constraint_space);
204                    let adjusted_available_space = inputs
205                        .available_space
206                        .width
207                        .map_definite_value(|width| (width - pb_sum.inline).max(0.0));
208
209                    let stretch_size = adjusted_available_space.into_option().map(Au::from_f32_px);
210                    let (_, min_size, max_size) = content_box_sizes.inline.resolve_each(
211                        Size::FitContent,
212                        Au::zero,
213                        stretch_size,
214                        || result.sizes,
215                        independent_context.layout_style().is_table(),
216                    );
217                    Au::from_f32_px(resolve_content_size(adjusted_available_space, result.sizes))
218                        .clamp_between_extremums(min_size, max_size)
219                        .to_f32_px()
220                });
221
222                // Return early if only inline content sizes are requested
223                if inputs.run_mode == RunMode::ComputeSize &&
224                    inputs.axis == RequestedAxis::Horizontal
225                {
226                    return taffy::LayoutOutput::from_outer_size(taffy::Size {
227                        width: inline_size + pb_sum.inline,
228                        // If RequestedAxis is Horizontal then height will be ignored.
229                        height: 0.0,
230                    });
231                }
232
233                let lazy_block_size = match content_box_known_dimensions.height {
234                    None => LazySize::new(
235                        &content_box_sizes.block,
236                        LogicalAxis::Block,
237                        Size::FitContent,
238                        Au::zero,
239                        None,
240                        independent_context.layout_style().is_table(),
241                    ),
242                    Some(height) => Au::from_f32_px(height).into(),
243                };
244
245                if inputs.run_mode == RunMode::ComputeSize {
246                    if let Some(height) = inputs.known_dimensions.height {
247                        return taffy::LayoutOutput::from_outer_size(taffy::Size {
248                            width: inline_size + pb_sum.inline,
249                            height,
250                        });
251                    }
252
253                    // When the height is unknown we can use a cached block size under default
254                    // constraints.
255                    //
256                    // Note: This throws away baselines, but that's okay because Taffy internally
257                    // performs a full layout of the item when sizing items for baseline alignment.
258                    // This is slow, but correct.
259                    let content_block_size = independent_context
260                        .content_block_size_under_default_constraint(
261                            self.layout_context,
262                            &mut PositioningContext::default(),
263                            Au::from_f32_px(inline_size),
264                            containing_block,
265                            preferred_aspect_ratio,
266                            &lazy_block_size,
267                        );
268                    let block_size = lazy_block_size.resolve(|| content_block_size).to_f32_px();
269                    return taffy::LayoutOutput::from_outer_size(taffy::Size {
270                        width: inline_size + pb_sum.inline,
271                        height: block_size + pb_sum.block,
272                    });
273                }
274
275                let content_box_size_override = ContainingBlock {
276                    size: ContainingBlockSize {
277                        inline: Au::from_f32_px(inline_size),
278                        block: tentative_block_size,
279                    },
280                    style,
281                };
282
283                child.positioning_context = PositioningContext::default();
284                let layout = independent_context.layout(
285                    self.layout_context,
286                    &mut child.positioning_context,
287                    &content_box_size_override,
288                    containing_block,
289                    preferred_aspect_ratio,
290                    &lazy_block_size,
291                );
292
293                child.child_fragments = layout.fragments;
294                self.child_specific_layout_infos[usize::from(node_id)] =
295                    layout.specific_layout_info;
296
297                let block_size = lazy_block_size
298                    .resolve(|| layout.content_block_size)
299                    .to_f32_px();
300
301                let computed_size = taffy::Size {
302                    width: inline_size + pb_sum.inline,
303                    height: block_size + pb_sum.block,
304                };
305                let size = inputs.known_dimensions.unwrap_or(computed_size);
306
307                taffy::LayoutOutput {
308                    size,
309                    baselines: taffy::Baselines {
310                        first: layout.baselines.first.map(|baseline| {
311                            (baseline + pbm.padding.block_start + pbm.border.block_start)
312                                .to_f32_px()
313                        }),
314                        last: layout.baselines.last.map(|baseline| {
315                            (baseline + pbm.padding.block_start + pbm.border.block_start)
316                                .to_f32_px()
317                        }),
318                    },
319                    ..taffy::LayoutOutput::DEFAULT
320                }
321            },
322        );
323        child.taffy_baselines = output.baselines;
324        output
325    }
326}
327
328impl taffy::LayoutGridContainer for TaffyContainerContext<'_> {
329    type GridContainerStyle<'a>
330        = TaffyStyloStyle<&'a ComputedValues>
331    where
332        Self: 'a;
333
334    type GridItemStyle<'a>
335        = TaffyStyloStyle<AtomicRef<'a, ComputedValues>>
336    where
337        Self: 'a;
338
339    fn get_grid_container_style(
340        &self,
341        _node_id: taffy::prelude::NodeId,
342    ) -> Self::GridContainerStyle<'_> {
343        TaffyStyloStyle::new(self.style, self.fragment_flags)
344    }
345
346    fn get_grid_child_style(
347        &self,
348        child_node_id: taffy::prelude::NodeId,
349    ) -> Self::GridItemStyle<'_> {
350        let id = usize::from(child_node_id);
351        let child = (*self.source_child_nodes[id]).borrow();
352        let fragment_flags = child.with_base(|base| base.base_fragment_info.flags);
353        let stylo_style = AtomicRef::map(child, |c| &*c.style);
354        TaffyStyloStyle::new(stylo_style, fragment_flags)
355    }
356
357    fn set_detailed_grid_info(
358        &mut self,
359        _node_id: taffy::NodeId,
360        specific_layout_info: taffy::DetailedGridInfo<Atom>,
361    ) {
362        self.specific_layout_info = Some(SpecificLayoutInfo::Grid(Box::new(
363            SpecificTaffyGridInfo::from_detailed_grid_layout(specific_layout_info),
364        )));
365    }
366}
367
368impl ComputeInlineContentSizes for TaffyContainer {
369    fn compute_inline_content_sizes(
370        &self,
371        layout_context: &LayoutContext,
372        _constraint_space: &ConstraintSpace,
373    ) -> InlineContentSizesResult {
374        let style = &self.style;
375
376        let max_content_inputs = taffy::LayoutInput {
377            run_mode: taffy::RunMode::ComputeSize,
378            sizing_mode: taffy::SizingMode::InherentSize,
379            axis: taffy::RequestedAxis::Horizontal,
380            vertical_margins_are_collapsible: taffy::Line::FALSE,
381
382            known_dimensions_are_definite: taffy::Size {
383                width: true,
384                height: true,
385            },
386            known_dimensions: taffy::Size::NONE,
387            parent_size: taffy::Size::NONE,
388            available_space: taffy::Size::MAX_CONTENT,
389        };
390
391        let min_content_inputs = taffy::LayoutInput {
392            available_space: taffy::Size::MIN_CONTENT,
393            ..max_content_inputs
394        };
395
396        let containing_block = &ContainingBlock {
397            size: ContainingBlockSize {
398                inline: Au::zero(),
399                block: SizeConstraint::default(),
400            },
401            style,
402        };
403
404        let mut grid_context = TaffyContainerContext {
405            layout_context,
406            positioning_context: &mut PositioningContext::default(),
407            content_box_size_override: containing_block,
408            style,
409            fragment_flags: self.fragment_flags,
410            source_child_nodes: &self.children,
411            specific_layout_info: None,
412            child_specific_layout_infos: vec![None; self.children.len()],
413        };
414
415        let (max_content_output, min_content_output) = match style.get_display().inside() {
416            DisplayInside::Grid => {
417                let max_content_output = taffy::compute_grid_layout(
418                    &mut grid_context,
419                    DUMMY_NODE_ID,
420                    max_content_inputs,
421                );
422                let min_content_output = taffy::compute_grid_layout(
423                    &mut grid_context,
424                    DUMMY_NODE_ID,
425                    min_content_inputs,
426                );
427                (max_content_output, min_content_output)
428            },
429            _ => panic!("Servo is only configured to use Taffy for CSS Grid layout"),
430        };
431
432        let pb_sums = self
433            .layout_style()
434            .padding_border_margin(containing_block)
435            .padding_border_sums;
436
437        InlineContentSizesResult {
438            sizes: ContentSizes {
439                max_content: Au::from_f32_px(max_content_output.size.width) - pb_sums.inline,
440                min_content: Au::from_f32_px(min_content_output.size.width) - pb_sums.inline,
441            },
442
443            // TODO: determine this accurately
444            //
445            // "true" is a safe default as it will prevent Servo from performing optimizations based
446            // on the assumption that the node's size does not depend on block constraints.
447            depends_on_block_constraints: true,
448        }
449    }
450}
451
452impl TaffyContainer {
453    /// <https://drafts.csswg.org/css-grid/#layout-algorithm>
454    pub(crate) fn layout(
455        &self,
456        layout_context: &LayoutContext,
457        positioning_context: &mut PositioningContext,
458        content_box_size_override: &ContainingBlock,
459        containing_block: &ContainingBlock,
460    ) -> IndependentFormattingContextLayoutResult {
461        let mut container_ctx = TaffyContainerContext {
462            layout_context,
463            positioning_context,
464            content_box_size_override,
465            style: content_box_size_override.style,
466            fragment_flags: self.fragment_flags,
467            source_child_nodes: &self.children,
468            specific_layout_info: None,
469            child_specific_layout_infos: vec![None; self.children.len()],
470        };
471
472        let container_style = &content_box_size_override.style;
473        let align_items = container_style.get_align_items();
474        let justify_items = container_style.get_justify_items();
475        let pbm = self.layout_style().padding_border_margin(containing_block);
476
477        let known_dimensions = taffy::Size {
478            width: Some(
479                (content_box_size_override.size.inline + pbm.padding_border_sums.inline)
480                    .to_f32_px(),
481            ),
482            height: content_box_size_override
483                .size
484                .block
485                .to_definite()
486                .map(Au::to_f32_px)
487                .maybe_add(pbm.padding_border_sums.block.to_f32_px()),
488        };
489
490        let taffy_containing_block = taffy::Size {
491            width: Some(containing_block.size.inline.to_f32_px()),
492            height: containing_block.size.block.to_definite().map(Au::to_f32_px),
493        };
494
495        let layout_input = taffy::LayoutInput {
496            run_mode: taffy::RunMode::PerformLayout,
497            sizing_mode: taffy::SizingMode::InherentSize,
498            axis: taffy::RequestedAxis::Vertical,
499            vertical_margins_are_collapsible: taffy::Line::FALSE,
500
501            known_dimensions_are_definite: taffy::Size {
502                width: true,
503                height: true,
504            },
505            known_dimensions,
506            parent_size: taffy_containing_block,
507            available_space: taffy::Size {
508                width: AvailableSpace::from(taffy_containing_block.width),
509                height: AvailableSpace::MaxContent,
510            },
511        };
512
513        let output = match container_ctx.style.get_display().inside() {
514            DisplayInside::Grid => {
515                taffy::compute_grid_layout(&mut container_ctx, DUMMY_NODE_ID, layout_input)
516            },
517            _ => panic!("Servo is only configured to use Taffy for CSS Grid layout"),
518        };
519
520        let writing_mode = containing_block.style.writing_mode;
521        let physical_padding = pbm.padding.to_physical(writing_mode);
522        let physical_border = pbm.border.to_physical(writing_mode);
523        let content_box_offset = PhysicalVec::new(
524            physical_padding.left + physical_border.left,
525            physical_padding.top + physical_border.top,
526        );
527        let mut out_of_flow_candidates = output.oof_candidates.iter();
528
529        // Convert `taffy::Layout` into Servo `Fragment`s
530        // with container_ctx.child_specific_layout_infos will also moved to the corresponding `Fragment`s
531        let fragments: Vec<Fragment> = self
532            .children
533            .iter()
534            .map(|child| (**child).borrow_mut())
535            .enumerate()
536            .map(|(child_id, mut child)| {
537                fn rect_to_physical_sides<T>(rect: taffy::Rect<T>) -> PhysicalSides<T> {
538                    PhysicalSides::new(rect.top, rect.right, rect.bottom, rect.left)
539                }
540
541                fn size_and_pos_to_logical_rect<T: Default>(
542                    position: taffy::Point<T>,
543                    size: taffy::Size<T>,
544                ) -> PhysicalRect<T> {
545                    PhysicalRect::new(
546                        PhysicalPoint::new(position.x, position.y),
547                        PhysicalSize::new(size.width, size.height),
548                    )
549                }
550
551                let layout = &child.taffy_layout;
552
553                let padding = rect_to_physical_sides(layout.padding.map(Au::from_f32_px));
554                let border = rect_to_physical_sides(layout.border.map(Au::from_f32_px));
555                let margin = rect_to_physical_sides(layout.margin.map(Au::from_f32_px));
556
557                // Compute content box size and position.
558                //
559                // For the x/y position we have to correct for the difference between the
560                // content box and the border box for both the parent and the child.
561                let content_size = size_and_pos_to_logical_rect(
562                    taffy::Point {
563                        x: Au::from_f32_px(
564                            layout.location.x + layout.padding.left + layout.border.left,
565                        ) - pbm.padding.inline_start -
566                            pbm.border.inline_start,
567                        y: Au::from_f32_px(
568                            layout.location.y + layout.padding.top + layout.border.top,
569                        ) - pbm.padding.block_start -
570                            pbm.border.block_start,
571                    },
572                    taffy::Size {
573                        width: layout.size.width -
574                            layout.padding.left -
575                            layout.padding.right -
576                            layout.border.left -
577                            layout.border.right,
578                        height: layout.size.height -
579                            layout.padding.top -
580                            layout.padding.bottom -
581                            layout.border.top -
582                            layout.border.bottom,
583                    }
584                    .map(Au::from_f32_px),
585                );
586
587                let child_specific_layout_info: Option<SpecificLayoutInfo> =
588                    std::mem::take(&mut container_ctx.child_specific_layout_infos[child_id]);
589
590                let fragment = match &mut child.taffy_level_box {
591                    TaffyItemBoxInner::InFlowBox(independent_box) => {
592                        let mut fragment_info = independent_box.base_fragment_info();
593                        fragment_info
594                            .flags
595                            .insert(FragmentFlags::IS_FLEX_OR_GRID_ITEM);
596                        let mut box_fragment = BoxFragment::new(
597                            fragment_info,
598                            independent_box.style().clone(),
599                            std::mem::take(&mut child.child_fragments),
600                            content_size,
601                            padding,
602                            border,
603                            margin,
604                            child_specific_layout_info,
605                        )
606                        .with_baselines(Baselines {
607                            first: child.taffy_baselines.first.map(|baseline| {
608                                Au::from_f32_px(baseline) - padding.top - border.top
609                            }),
610                            last: child.taffy_baselines.last.map(|baseline| {
611                                Au::from_f32_px(baseline) - padding.top - border.top
612                            }),
613                        });
614
615                        child.positioning_context.layout_collected_children(
616                            container_ctx.layout_context,
617                            &mut box_fragment,
618                        );
619
620                        child
621                            .positioning_context
622                            .adjust_static_position_of_hoisted_fragments_with_offset(
623                                &box_fragment.content_rect().origin.to_vector(),
624                                PositioningContextLength::zero(),
625                            );
626                        container_ctx
627                            .positioning_context
628                            .append(std::mem::take(&mut child.positioning_context));
629
630                        Fragment::Box(box_fragment.into())
631                    },
632                    TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(abs_pos_box) => {
633                        fn resolve_alignment(value: AlignFlags, auto: AlignFlags) -> AlignFlags {
634                            match value {
635                                AlignFlags::AUTO => auto,
636                                AlignFlags::NORMAL => AlignFlags::STRETCH,
637                                value => value,
638                            }
639                        }
640
641                        let candidate = out_of_flow_candidates.next();
642                        debug_assert_eq!(
643                            candidate.map(|candidate| usize::from(candidate.node)),
644                            Some(child_id)
645                        );
646                        let static_position_rect = candidate
647                            .map(|candidate| {
648                                let taffy::Point { x, y } = candidate.static_position;
649                                PhysicalRect::new(
650                                    PhysicalPoint::new(
651                                        Au::from_f32_px(x.area.start),
652                                        Au::from_f32_px(y.area.start),
653                                    ) - content_box_offset,
654                                    PhysicalSize::new(
655                                        Au::from_f32_px(x.area.end - x.area.start),
656                                        Au::from_f32_px(y.area.end - y.area.start),
657                                    ),
658                                )
659                            })
660                            .unwrap_or_default();
661
662                        let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
663                            abs_pos_box.clone(),
664                            static_position_rect,
665                            LogicalVec2 {
666                                inline: resolve_alignment(
667                                    child.style.get_justify_self().0,
668                                    justify_items.computed.0.0,
669                                ),
670                                block: resolve_alignment(
671                                    child.style.get_align_self().0,
672                                    align_items.0,
673                                ),
674                            },
675                            container_ctx.style.writing_mode,
676                        );
677                        let hoisted_fragment = hoisted_box.fragment.clone();
678                        container_ctx.positioning_context.push(hoisted_box);
679                        Fragment::AbsoluteOrFixedPositionedPlaceholder(hoisted_fragment)
680                    },
681                };
682
683                if let TaffyItemBoxInner::InFlowBox(independent_formatting_context) =
684                    &child.taffy_level_box
685                {
686                    independent_formatting_context
687                        .base
688                        .set_fragment(fragment.clone());
689                }
690                fragment
691            })
692            .collect();
693        debug_assert!(out_of_flow_candidates.next().is_none());
694
695        IndependentFormattingContextLayoutResult {
696            fragments,
697            content_block_size: Au::from_f32_px(output.size.height) - pbm.padding_border_sums.block,
698            content_inline_size_for_table: None,
699            baselines: Baselines {
700                first: output.baselines.first.map(|baseline| {
701                    Au::from_f32_px(baseline) - pbm.padding.block_start - pbm.border.block_start
702                }),
703                last: output.baselines.last.map(|baseline| {
704                    Au::from_f32_px(baseline) - pbm.padding.block_start - pbm.border.block_start
705                }),
706            },
707
708            // TODO: determine this accurately
709            //
710            // "true" is a safe default as it will prevent Servo from performing optimizations based
711            // on the assumption that the node's size does not depend on block constraints.
712            depends_on_block_constraints: true,
713            specific_layout_info: container_ctx.specific_layout_info,
714            collapsible_margins_in_children: CollapsedBlockMargins::zero(),
715        }
716    }
717
718    #[inline]
719    pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
720        LayoutStyle::Default(&self.style)
721    }
722
723    pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
724        for child in &self.children {
725            child.borrow_mut().with_base_mut(|base| {
726                base.parent_box.replace(layout_box.clone());
727            });
728        }
729    }
730
731    pub(crate) fn subtree_size(&self) -> usize {
732        self.children
733            .iter()
734            .map(|child| child.borrow().with_base(|base| base.subtree_size()))
735            .sum()
736    }
737}