1use std::mem;
6use std::ops::Range;
7use std::sync::Arc;
8
9use app_units::Au;
10use atomic_refcell::AtomicRef;
11use malloc_size_of_derive::MallocSizeOf;
12use rayon::iter::IntoParallelRefMutIterator;
13use rayon::prelude::{IndexedParallelIterator, ParallelIterator};
14use servo_arc::Arc as ServoArc;
15use style::Zero;
16use style::computed_values::position::T as Position;
17use style::logical_geometry::{Direction, WritingMode};
18use style::properties::ComputedValues;
19use style::values::specified::align::AlignFlags;
20
21use crate::cell::ArcRefCell;
22use crate::context::LayoutContext;
23use crate::dom_traversal::{Contents, NodeAndStyleInfo};
24use crate::formatting_contexts::IndependentFormattingContext;
25use crate::fragment_tree::{
26 BoxFragment, Fragment, FragmentFlags, HoistedSharedFragment, LayoutRootFragment,
27 SpecificLayoutInfo,
28};
29use crate::geom::{
30 AuOrAuto, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect,
31 PhysicalSides, PhysicalSize, PhysicalVec, ToLogical, ToLogicalWithContainingBlock,
32};
33use crate::layout_box_base::{IndependentFormattingContextLayoutResult, LayoutBoxBase};
34use crate::sizing::{LazySize, Size, SizeConstraint, Sizes};
35use crate::style_ext::{Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, DisplayInside};
36use crate::taffy::SpecificTaffyGridInfo;
37use crate::{
38 ConstraintSpace, ContainingBlock, ContainingBlockSize, DefiniteContainingBlock,
39 PropagatedBoxTreeData,
40};
41
42#[derive(Debug, MallocSizeOf)]
43pub(crate) struct AbsolutelyPositionedBox {
44 pub context: IndependentFormattingContext,
45}
46
47#[derive(Clone, MallocSizeOf)]
48pub(crate) struct HoistedAbsolutelyPositionedBox {
49 absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
50 pub fragment: ArcRefCell<HoistedSharedFragment>,
54 pub adjusted_static_position_rect: Option<PhysicalRect<Au>>,
64 pub resolved_alignment: LogicalVec2<AlignFlags>,
69 pub original_parent_writing_mode: WritingMode,
74}
75
76impl AbsolutelyPositionedBox {
77 pub fn new(context: IndependentFormattingContext) -> Self {
78 Self { context }
79 }
80
81 pub fn construct(
82 context: &LayoutContext,
83 node_info: &NodeAndStyleInfo,
84 display_inside: DisplayInside,
85 contents: Contents,
86 ) -> Self {
87 Self {
88 context: IndependentFormattingContext::construct(
89 context,
90 node_info,
91 display_inside,
92 contents,
93 PropagatedBoxTreeData::default(),
97 ),
98 }
99 }
100
101 pub(crate) fn to_hoisted(
102 absolutely_positioned_box: ArcRefCell<Self>,
103 static_position_rect: PhysicalRect<Au>,
104 resolved_alignment: LogicalVec2<AlignFlags>,
105 original_parent_writing_mode: WritingMode,
106 ) -> HoistedAbsolutelyPositionedBox {
107 HoistedAbsolutelyPositionedBox {
108 fragment: ArcRefCell::new(HoistedSharedFragment::new(static_position_rect)),
109 adjusted_static_position_rect: None,
110 resolved_alignment,
111 original_parent_writing_mode,
112 absolutely_positioned_box,
113 }
114 }
115}
116
117#[derive(Clone, Default, MallocSizeOf)]
118pub(crate) struct PositioningContext {
119 absolutes: Vec<HoistedAbsolutelyPositionedBox>,
120}
121
122impl PositioningContext {
123 #[inline]
124 pub(crate) fn is_empty(&self) -> bool {
125 self.absolutes.is_empty()
126 }
127
128 #[inline]
129 pub(crate) fn new_for_layout_box_base(layout_box_base: &LayoutBoxBase) -> Option<Self> {
130 Self::new_for_style_and_fragment_flags(
131 &layout_box_base.style,
132 &layout_box_base.base_fragment_info.flags,
133 )
134 }
135
136 fn new_for_style_and_fragment_flags(
137 style: &ComputedValues,
138 flags: &FragmentFlags,
139 ) -> Option<Self> {
140 if style.establishes_containing_block_for_absolute_descendants(*flags) {
141 Some(Self::default())
142 } else {
143 None
144 }
145 }
146
147 pub(crate) fn adjust_static_position_of_hoisted_fragments(
160 &mut self,
161 parent_fragment: &Fragment,
162 index: PositioningContextLength,
163 ) {
164 let Some(base) = parent_fragment.base() else {
165 return;
166 };
167 self.adjust_static_position_of_hoisted_fragments_with_offset(
168 &base.rect().origin.to_vector(),
169 index,
170 );
171 }
172
173 pub(crate) fn adjust_static_position_of_hoisted_fragments_with_offset(
175 &mut self,
176 offset: &PhysicalVec<Au>,
177 index: PositioningContextLength,
178 ) {
179 self.adjust_static_position_of_hoisted_fragments_in_range(offset, &(index..self.len()))
180 }
181
182 pub(crate) fn adjust_static_position_of_hoisted_fragments_in_range(
184 &mut self,
185 offset: &PhysicalVec<Au>,
186 range: &Range<PositioningContextLength>,
187 ) {
188 for hoisted_box in &mut self.absolutes[range.start.0..range.end.0] {
189 hoisted_box.adjust_static_position_with_offset(offset);
190 }
191 }
192
193 pub(crate) fn layout_maybe_position_relative_fragment(
197 &mut self,
198 layout_context: &LayoutContext,
199 containing_block: &ContainingBlock,
200 base: &LayoutBoxBase,
201 fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment,
202 ) -> BoxFragment {
203 let establishes_containing_block_for_absolutes = base
206 .style
207 .establishes_containing_block_for_absolute_descendants(base.base_fragment_info.flags);
208 if !establishes_containing_block_for_absolutes {
209 return fragment_layout_fn(self);
210 }
211
212 let mut new_context = PositioningContext::default();
213 let mut new_fragment = fragment_layout_fn(&mut new_context);
214
215 new_context.layout_collected_children(layout_context, &mut new_fragment);
218 self.append(new_context);
219
220 if base.style.get_box().position == Position::Relative {
221 new_fragment.base.translate_rect(
222 relative_adjustement(&base.style, containing_block)
223 .to_physical_vector(containing_block.style.writing_mode)
224 .into(),
225 );
226 }
227
228 new_fragment
229 }
230
231 fn forget_unhoisted_boxes(&mut self, fragment: &BoxFragment) {
232 let style = fragment.style();
233 debug_assert!(
234 style.establishes_containing_block_for_absolute_descendants(fragment.base.flags)
235 );
236 if style.establishes_containing_block_for_all_descendants(fragment.base.flags) {
237 self.absolutes.clear();
238 } else {
239 self.absolutes
240 .retain(|hoisted_box| hoisted_box.position() == Position::Fixed);
241 }
242 }
243
244 fn take_boxes_for_fragment(
245 &mut self,
246 new_fragment: &BoxFragment,
247 boxes_to_layout_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
248 boxes_to_continue_hoisting_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
249 ) {
250 let style = new_fragment.style();
251 debug_assert!(
252 style.establishes_containing_block_for_absolute_descendants(new_fragment.base.flags)
253 );
254
255 if style.establishes_containing_block_for_all_descendants(new_fragment.base.flags) {
256 boxes_to_layout_out.append(&mut self.absolutes);
257 return;
258 }
259
260 let (mut boxes_to_layout, mut boxes_to_continue_hoisting) = self
262 .absolutes
263 .drain(..)
264 .partition(|hoisted_box| hoisted_box.position() != Position::Fixed);
265 boxes_to_layout_out.append(&mut boxes_to_layout);
266 boxes_to_continue_hoisting_out.append(&mut boxes_to_continue_hoisting);
267 }
268
269 pub(crate) fn layout_collected_children(
272 &mut self,
273 layout_context: &LayoutContext,
274 new_fragment: &mut BoxFragment,
275 ) {
276 if self.absolutes.is_empty() {
277 return;
278 }
279
280 let style = new_fragment.style().clone();
289 if !style.establishes_containing_block_for_absolute_descendants(new_fragment.base.flags) {
290 return;
291 }
292
293 let padding_rect = PhysicalRect::new(
294 PhysicalPoint::origin(),
296 new_fragment.base.rect().size,
297 )
298 .outer_rect(new_fragment.padding);
299 let containing_block = DefiniteContainingBlock {
300 size: padding_rect.size.to_logical(style.writing_mode),
301 style: &style,
302 };
303
304 let rare_data = new_fragment
305 .rare_data
306 .get()
307 .map(|ref_cell| ref_cell.borrow());
308
309 let grid_info = rare_data.and_then(|rare_data| {
310 AtomicRef::filter_map(rare_data, |rare_data| {
311 match rare_data.specific_layout_info.as_ref() {
312 Some(SpecificLayoutInfo::Grid(grid_info)) => Some(&**grid_info),
313 _ => None,
314 }
315 })
316 });
317
318 let mut fixed_position_boxes_to_hoist = Vec::new();
319 let mut boxes_to_layout = Vec::new();
320 self.take_boxes_for_fragment(
321 new_fragment,
322 &mut boxes_to_layout,
323 &mut fixed_position_boxes_to_hoist,
324 );
325
326 while !boxes_to_layout.is_empty() {
332 HoistedAbsolutelyPositionedBox::layout_many(
333 layout_context,
334 std::mem::take(&mut boxes_to_layout),
335 &mut new_fragment.children,
336 &mut self.absolutes,
337 grid_info.as_deref(),
338 &containing_block,
339 new_fragment.padding,
340 new_fragment.border,
341 );
342
343 self.take_boxes_for_fragment(
344 new_fragment,
345 &mut boxes_to_layout,
346 &mut fixed_position_boxes_to_hoist,
347 );
348 }
349
350 self.absolutes = fixed_position_boxes_to_hoist;
354 }
355
356 pub(crate) fn push(&mut self, hoisted_box: HoistedAbsolutelyPositionedBox) {
357 debug_assert!(hoisted_box.position().is_absolutely_positioned());
358 self.absolutes.push(hoisted_box);
359 }
360
361 pub(crate) fn append(&mut self, mut other: Self) {
362 if other.absolutes.is_empty() {
363 return;
364 }
365 if self.absolutes.is_empty() {
366 self.absolutes = other.absolutes;
367 } else {
368 self.absolutes.append(&mut other.absolutes)
369 }
370 }
371
372 pub(crate) fn layout_initial_containing_block_children(
373 &mut self,
374 layout_context: &LayoutContext,
375 initial_containing_block: &DefiniteContainingBlock,
376 fragments: &mut Vec<Fragment>,
377 ) {
378 while !self.absolutes.is_empty() {
383 HoistedAbsolutelyPositionedBox::layout_many(
384 layout_context,
385 mem::take(&mut self.absolutes),
386 fragments,
387 &mut self.absolutes,
388 None,
389 initial_containing_block,
390 Default::default(),
391 Default::default(),
392 )
393 }
394 }
395
396 pub(crate) fn len(&self) -> PositioningContextLength {
398 PositioningContextLength(self.absolutes.len())
399 }
400
401 pub(crate) fn truncate(&mut self, length: &PositioningContextLength) {
405 self.absolutes.truncate(length.0)
406 }
407}
408
409#[derive(Clone, Copy, Debug, PartialEq)]
411pub(crate) struct PositioningContextLength(usize);
412
413impl Zero for PositioningContextLength {
414 fn zero() -> Self {
415 Self(0)
416 }
417
418 fn is_zero(&self) -> bool {
419 self.0.is_zero()
420 }
421}
422
423impl HoistedAbsolutelyPositionedBox {
424 fn position(&self) -> Position {
425 let position = self
426 .absolutely_positioned_box
427 .borrow()
428 .context
429 .style()
430 .get_box()
431 .position;
432 assert!(position.is_absolutely_positioned());
433 position
434 }
435
436 #[allow(clippy::too_many_arguments)]
437 pub(crate) fn layout_many(
438 layout_context: &LayoutContext,
439 mut boxes: Vec<Self>,
440 fragments: &mut Vec<Fragment>,
441 for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>,
442 grid_info: Option<&SpecificTaffyGridInfo>,
443 containing_block: &DefiniteContainingBlock,
444 containing_block_padding: PhysicalSides<Au>,
445 containing_block_border: PhysicalSides<Au>,
446 ) {
447 let job_sizes = boxes.iter().map(|hoisted_box| {
448 hoisted_box
449 .absolutely_positioned_box
450 .borrow()
451 .context
452 .subtree_size()
453 });
454 if layout_context.should_parallelize_layout(job_sizes) {
455 let mut new_fragments = Vec::new();
456 let mut new_hoisted_boxes = Vec::new();
457
458 boxes
459 .par_iter_mut()
460 .map(|hoisted_box| {
461 let mut new_hoisted_boxes: Vec<HoistedAbsolutelyPositionedBox> = Vec::new();
462 let new_fragment = hoisted_box.layout(
463 layout_context,
464 &mut new_hoisted_boxes,
465 grid_info,
466 containing_block,
467 containing_block_padding,
468 containing_block_border,
469 );
470 (new_fragment, new_hoisted_boxes)
471 })
472 .unzip_into_vecs(&mut new_fragments, &mut new_hoisted_boxes);
473
474 fragments.extend(new_fragments);
475 for_nearest_containing_block_for_all_descendants
476 .extend(new_hoisted_boxes.into_iter().flatten());
477 } else {
478 fragments.extend(boxes.iter_mut().map(|hoisted_box| {
479 hoisted_box.layout(
480 layout_context,
481 for_nearest_containing_block_for_all_descendants,
482 grid_info,
483 containing_block,
484 containing_block_padding,
485 containing_block_border,
486 )
487 }))
488 }
489 }
490
491 pub(crate) fn layout(
492 &mut self,
493 layout_context: &LayoutContext,
494 hoisted_absolutes_from_children: &mut Vec<HoistedAbsolutelyPositionedBox>,
495 grid_info: Option<&SpecificTaffyGridInfo>,
496 containing_block: &DefiniteContainingBlock,
497 containing_block_padding: PhysicalSides<Au>,
498 containing_block_border: PhysicalSides<Au>,
499 ) -> Fragment {
500 let absolutely_positioned_box = self.absolutely_positioned_box.borrow();
501 let independent_formatting_context = &absolutely_positioned_box.context;
502 let writing_mode = containing_block.style.writing_mode;
503
504 let grid_area = grid_info.map(|grid_info| {
507 grid_info.resolve_grid_area(
508 independent_formatting_context.style(),
509 containing_block,
510 containing_block_border,
511 )
512 });
513
514 let containing_block_origin = grid_area
517 .as_ref()
518 .map(|grid_area| grid_area.origin.to_vector())
519 .unwrap_or_default();
520
521 let containing_block = DefiniteContainingBlock {
523 size: grid_area
524 .as_ref()
525 .map(|grid_area| grid_area.size.to_logical(writing_mode))
526 .unwrap_or(containing_block.size),
527 style: containing_block.style,
528 };
529
530 let fully_adjusted_static_position_rect = {
536 let mut static_position_rect = self.static_position_rect().translate(
537 PhysicalVec::new(containing_block_padding.left, containing_block_padding.top) -
538 containing_block_origin,
539 );
540 static_position_rect.size = static_position_rect.size.max(PhysicalSize::zero());
541 static_position_rect.to_logical(&(&containing_block).into())
542 };
543
544 let (box_fragment, mut positioning_context) = independent_formatting_context
545 .layout_as_absolute(
546 layout_context,
547 &fully_adjusted_static_position_rect,
548 &containing_block,
549 containing_block_origin,
550 self.resolved_alignment,
551 self.original_parent_writing_mode,
552 );
553
554 let is_layout_root = positioning_context.is_empty();
559
560 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
566 &box_fragment.content_rect().origin.to_vector(),
567 PositioningContextLength::zero(),
568 );
569 hoisted_absolutes_from_children.extend(positioning_context.absolutes);
570
571 let fragment = Fragment::Box(box_fragment);
572 self.fragment.borrow_mut().fragment = Some(fragment.clone());
573
574 let fragment = match is_layout_root {
575 false => fragment,
576 true => Fragment::LayoutRoot(LayoutRootFragment {
577 fragment: self.fragment.clone(),
578 }),
579 };
580
581 independent_formatting_context
582 .base
583 .set_fragment(fragment.clone());
584
585 *independent_formatting_context
586 .layout_root_layout_inputs
587 .borrow_mut() = is_layout_root.then(|| {
588 Box::new(LayoutRootLayoutInputs {
589 fully_adjusted_static_position_rect,
590 resolved_alignment: self.resolved_alignment,
591 containing_block_size: containing_block.size,
592 containing_block_style: containing_block.style.clone(),
593 containing_block_origin,
594 original_parent_writing_mode: self.original_parent_writing_mode,
595 })
596 });
597
598 fragment
599 }
600
601 fn static_position_rect(&self) -> PhysicalRect<Au> {
602 self.adjusted_static_position_rect
603 .unwrap_or_else(|| self.fragment.borrow().original_static_position_rect)
604 }
605
606 fn adjust_static_position_with_offset(&mut self, offset: &PhysicalVec<Au>) {
607 self.adjusted_static_position_rect = Some(self.static_position_rect().translate(*offset));
608 }
609}
610
611impl IndependentFormattingContext {
612 pub(crate) fn layout_as_absolute(
613 &self,
614 layout_context: &LayoutContext,
615 static_position_rect: &LogicalRect<Au>,
616 containing_block: &DefiniteContainingBlock,
617 containing_block_origin: PhysicalVec<Au>,
618 resolved_alignment: LogicalVec2<AlignFlags>,
619 original_parent_writing_mode: WritingMode,
620 ) -> (Arc<BoxFragment>, PositioningContext) {
621 let cbis = containing_block.size.inline;
622 let cbbs = containing_block.size.block;
623 let containing_block_writing_mode = containing_block.style.writing_mode;
624 let style = self.style().clone();
625 let layout_style = self.layout_style();
626 let ContentBoxSizesAndPBM {
627 content_box_sizes,
628 pbm,
629 ..
630 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
631 let is_table = layout_style.is_table();
632 let is_table_or_replaced = is_table || self.is_replaced();
633 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
634
635 let box_offset = style.box_offsets(containing_block.style.writing_mode);
636
637 let inline_box_offsets = box_offset.inline_sides().percentages_relative_to(cbis);
640 let inline_alignment = match inline_box_offsets.either_specified() {
641 true => style.get_justify_self().0,
642 false => resolved_alignment.inline,
643 };
644
645 let inline_axis_solver = AbsoluteAxisSolver {
646 axis: Direction::Inline,
647 containing_size: cbis,
648 padding_border_sum: pbm.padding_border_sums.inline,
649 computed_margin_start: pbm.margin.inline_start,
650 computed_margin_end: pbm.margin.inline_end,
651 computed_sizes: content_box_sizes.inline,
652 avoid_negative_margin_start: true,
653 box_offsets: inline_box_offsets,
654 static_position_rect_axis: static_position_rect.get_axis(Direction::Inline),
655 alignment: inline_alignment,
656 flip_anchor: original_parent_writing_mode.is_bidi_ltr() !=
657 containing_block_writing_mode.is_bidi_ltr(),
658 is_table_or_replaced,
659 };
660
661 let block_box_offsets = box_offset.block_sides().percentages_relative_to(cbbs);
664 let block_alignment = match block_box_offsets.either_specified() {
665 true => style.get_align_self().0,
666 false => resolved_alignment.block,
667 };
668 let block_axis_solver = AbsoluteAxisSolver {
669 axis: Direction::Block,
670 containing_size: cbbs,
671 padding_border_sum: pbm.padding_border_sums.block,
672 computed_margin_start: pbm.margin.block_start,
673 computed_margin_end: pbm.margin.block_end,
674 computed_sizes: content_box_sizes.block,
675 avoid_negative_margin_start: false,
676 box_offsets: block_box_offsets,
677 static_position_rect_axis: static_position_rect.get_axis(Direction::Block),
678 alignment: block_alignment,
679 flip_anchor: false,
680 is_table_or_replaced,
681 };
682
683 let block_automatic_size = block_axis_solver.automatic_size();
686 let block_stretch_size = Some(block_axis_solver.stretch_size());
687 let inline_stretch_size = inline_axis_solver.stretch_size();
688 let tentative_block_content_size =
689 self.tentative_block_content_size(preferred_aspect_ratio, inline_stretch_size);
690 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
691 SizeConstraint::Definite(block_axis_solver.computed_sizes.resolve(
692 Direction::Block,
693 block_automatic_size,
694 Au::zero,
695 block_stretch_size,
696 || block_content_size,
697 is_table,
698 ))
699 } else {
700 block_axis_solver.computed_sizes.resolve_extrinsic(
701 block_automatic_size,
702 Au::zero(),
703 block_stretch_size,
704 )
705 };
706
707 let get_inline_content_size = || {
710 let constraint_space =
711 ConstraintSpace::new(tentative_block_size, &style, preferred_aspect_ratio);
712 self.inline_content_sizes(layout_context, &constraint_space)
713 .sizes
714 };
715 let inline_size = inline_axis_solver.computed_sizes.resolve(
716 Direction::Inline,
717 inline_axis_solver.automatic_size(),
718 Au::zero,
719 Some(inline_stretch_size),
720 get_inline_content_size,
721 is_table,
722 );
723
724 let containing_block_for_children = ContainingBlock {
725 size: ContainingBlockSize {
726 inline: inline_size,
727 block: tentative_block_size,
728 },
729 style: &style,
730 };
731 assert_eq!(
733 containing_block_writing_mode.is_horizontal(),
734 style.writing_mode.is_horizontal(),
735 "Mixed horizontal and vertical writing modes are not supported yet"
736 );
737
738 let mut positioning_context = PositioningContext::default();
739 let lazy_block_size = LazySize::new(
740 &block_axis_solver.computed_sizes,
741 Direction::Block,
742 block_automatic_size,
743 Au::zero,
744 block_stretch_size,
745 is_table,
746 );
747
748 let containing_block = &containing_block.into();
749 let (layout, is_cached) = self.layout_and_is_cached(
750 layout_context,
751 &mut positioning_context,
752 &containing_block_for_children,
753 containing_block,
754 preferred_aspect_ratio,
755 &lazy_block_size,
756 );
757 let IndependentFormattingContextLayoutResult {
758 content_inline_size_for_table,
759 content_block_size,
760 fragments,
761 specific_layout_info,
762 ..
763 } = layout;
764
765 let content_size = LogicalVec2 {
766 inline: content_inline_size_for_table.unwrap_or(inline_size),
768
769 block: lazy_block_size.resolve(|| content_block_size),
771 };
772
773 let inline_margins = inline_axis_solver.solve_margins(content_size.inline);
774 let block_margins = block_axis_solver.solve_margins(content_size.block);
775 let margin = LogicalSides {
776 inline_start: inline_margins.start,
777 inline_end: inline_margins.end,
778 block_start: block_margins.start,
779 block_end: block_margins.end,
780 };
781
782 let pb = pbm.padding + pbm.border;
783 let margin_rect_size = content_size + pbm.padding_border_sums + margin.sum();
784 let inline_origin = inline_axis_solver.origin_for_margin_box(
785 margin_rect_size.inline,
786 style.writing_mode,
787 original_parent_writing_mode,
788 containing_block_writing_mode,
789 );
790 let block_origin = block_axis_solver.origin_for_margin_box(
791 margin_rect_size.block,
792 style.writing_mode,
793 original_parent_writing_mode,
794 containing_block_writing_mode,
795 );
796 let content_rect = LogicalRect {
797 start_corner: LogicalVec2 {
798 inline: inline_origin + margin.inline_start + pb.inline_start,
799 block: block_origin + margin.block_start + pb.block_start,
800 },
801 size: content_size,
802 }
803 .as_physical(Some(containing_block))
804 .translate(containing_block_origin);
805
806 if is_cached &&
807 let Some(old_fragment) = self.base.fragments().first() &&
808 let Some(old_box_fragment) = old_fragment
809 .retrieve_box_fragment()
810 .map(|fragment| fragment.clone()) &&
811 content_rect == old_box_fragment.content_rect()
812 {
813 positioning_context.forget_unhoisted_boxes(&old_box_fragment);
816 return (old_box_fragment, positioning_context);
817 }
818
819 let mut new_box_fragment = BoxFragment::new(
820 self.base_fragment_info(),
821 style,
822 fragments,
823 content_rect,
824 pbm.padding.to_physical(containing_block_writing_mode),
825 pbm.border.to_physical(containing_block_writing_mode),
826 margin.to_physical(containing_block_writing_mode),
827 specific_layout_info,
828 );
829
830 positioning_context.layout_collected_children(layout_context, &mut new_box_fragment);
834 (new_box_fragment.into(), positioning_context)
835 }
836}
837
838#[derive(Clone, Copy, Debug)]
839struct RectAxis {
840 origin: Au,
841 length: Au,
842}
843
844impl LogicalRect<Au> {
845 fn get_axis(&self, axis: Direction) -> RectAxis {
846 match axis {
847 Direction::Block => RectAxis {
848 origin: self.start_corner.block,
849 length: self.size.block,
850 },
851 Direction::Inline => RectAxis {
852 origin: self.start_corner.inline,
853 length: self.size.inline,
854 },
855 }
856 }
857}
858
859struct AbsoluteAxisSolver {
860 axis: Direction,
861 containing_size: Au,
862 padding_border_sum: Au,
863 computed_margin_start: AuOrAuto,
864 computed_margin_end: AuOrAuto,
865 computed_sizes: Sizes,
866 avoid_negative_margin_start: bool,
867 box_offsets: LogicalSides1D<AuOrAuto>,
868 static_position_rect_axis: RectAxis,
869 alignment: AlignFlags,
870 flip_anchor: bool,
871 is_table_or_replaced: bool,
872}
873
874impl AbsoluteAxisSolver {
875 fn inset_sum(&self) -> Au {
880 match (
881 self.box_offsets.start.non_auto(),
882 self.box_offsets.end.non_auto(),
883 ) {
884 (None, None) => {
885 if self.flip_anchor {
886 self.containing_size -
887 self.static_position_rect_axis.origin -
888 self.static_position_rect_axis.length
889 } else {
890 self.static_position_rect_axis.origin
891 }
892 },
893 (Some(start), None) => start,
894 (None, Some(end)) => end,
895 (Some(start), Some(end)) => start + end,
896 }
897 }
898
899 #[inline]
902 fn available_space(&self) -> Au {
903 Au::zero().max(self.containing_size - self.inset_sum())
904 }
905
906 #[inline]
907 fn automatic_size(&self) -> Size<Au> {
908 match self.alignment.value() {
909 _ if self.box_offsets.either_auto() => Size::FitContent,
910 AlignFlags::NORMAL | AlignFlags::AUTO if !self.is_table_or_replaced => Size::Stretch,
911 AlignFlags::STRETCH => Size::Stretch,
912 _ => Size::FitContent,
913 }
914 }
915
916 #[inline]
917 fn stretch_size(&self) -> Au {
918 Au::zero().max(
919 self.available_space() -
920 self.padding_border_sum -
921 self.computed_margin_start.auto_is(Au::zero) -
922 self.computed_margin_end.auto_is(Au::zero),
923 )
924 }
925
926 fn solve_margins(&self, size: Au) -> LogicalSides1D<Au> {
927 if self.box_offsets.either_auto() {
928 LogicalSides1D::new(
929 self.computed_margin_start.auto_is(Au::zero),
930 self.computed_margin_end.auto_is(Au::zero),
931 )
932 } else {
933 let free_space = self.available_space() - self.padding_border_sum - size;
934 match (self.computed_margin_start, self.computed_margin_end) {
935 (AuOrAuto::Auto, AuOrAuto::Auto) => {
936 if self.avoid_negative_margin_start && free_space < Au::zero() {
937 LogicalSides1D::new(Au::zero(), free_space)
938 } else {
939 let margin_start = free_space / 2;
940 LogicalSides1D::new(margin_start, free_space - margin_start)
941 }
942 },
943 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
944 LogicalSides1D::new(free_space - end, end)
945 },
946 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => {
947 LogicalSides1D::new(start, free_space - start)
948 },
949 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
950 LogicalSides1D::new(start, end)
951 },
952 }
953 }
954 }
955
956 fn origin_for_margin_box(
957 &self,
958 size: Au,
959 self_writing_mode: WritingMode,
960 original_parent_writing_mode: WritingMode,
961 containing_block_writing_mode: WritingMode,
962 ) -> Au {
963 let (alignment_container, alignment_container_writing_mode, flip_anchor, offsets) = match (
964 self.box_offsets.start.non_auto(),
965 self.box_offsets.end.non_auto(),
966 ) {
967 (None, None) => (
968 self.static_position_rect_axis,
969 original_parent_writing_mode,
970 self.flip_anchor,
971 None,
972 ),
973 (Some(start), Some(end)) => {
974 let alignment_container = RectAxis {
975 origin: start,
976 length: self.available_space(),
977 };
978 (
979 alignment_container,
980 containing_block_writing_mode,
981 false,
982 Some(LogicalSides1D { start, end }),
983 )
984 },
985 (Some(start), None) => return start,
989 (None, Some(end)) => {
990 return self.containing_size - size - end;
991 },
992 };
993
994 assert_eq!(
995 self_writing_mode.is_horizontal(),
996 original_parent_writing_mode.is_horizontal(),
997 "Mixed horizontal and vertical writing modes are not supported yet"
998 );
999 assert_eq!(
1000 self_writing_mode.is_horizontal(),
1001 containing_block_writing_mode.is_horizontal(),
1002 "Mixed horizontal and vertical writing modes are not supported yet"
1003 );
1004 let self_value_matches_container = || {
1005 self.axis == Direction::Block ||
1006 self_writing_mode.is_bidi_ltr() == alignment_container_writing_mode.is_bidi_ltr()
1007 };
1008
1009 let alignment = match self.alignment.value() {
1014 AlignFlags::CENTER | AlignFlags::SPACE_AROUND | AlignFlags::SPACE_EVENLY => {
1018 AlignFlags::CENTER
1019 },
1020 AlignFlags::SELF_START if self_value_matches_container() => AlignFlags::START,
1022 AlignFlags::SELF_START => AlignFlags::END,
1023 AlignFlags::SELF_END if self_value_matches_container() => AlignFlags::END,
1025 AlignFlags::SELF_END => AlignFlags::START,
1026 AlignFlags::LEFT if alignment_container_writing_mode.is_bidi_ltr() => AlignFlags::START,
1028 AlignFlags::LEFT => AlignFlags::END,
1029 AlignFlags::RIGHT if alignment_container_writing_mode.is_bidi_ltr() => AlignFlags::END,
1031 AlignFlags::RIGHT => AlignFlags::START,
1032 AlignFlags::END | AlignFlags::FLEX_END | AlignFlags::LAST_BASELINE => AlignFlags::END,
1036 _ => AlignFlags::START,
1040 };
1041
1042 let alignment = match alignment {
1043 AlignFlags::START if flip_anchor => AlignFlags::END,
1044 AlignFlags::END if flip_anchor => AlignFlags::START,
1045 alignment => alignment,
1046 };
1047
1048 let free_space = alignment_container.length - size;
1049 let flags = self.alignment.flags();
1050 let alignment = if flags == AlignFlags::SAFE && free_space < Au::zero() {
1051 AlignFlags::START
1052 } else {
1053 alignment
1054 };
1055
1056 let origin = match alignment {
1057 AlignFlags::START => alignment_container.origin,
1058 AlignFlags::CENTER => alignment_container.origin + free_space / 2,
1059 AlignFlags::END => alignment_container.origin + free_space,
1060 _ => unreachable!(),
1061 };
1062 if matches!(flags, AlignFlags::SAFE | AlignFlags::UNSAFE) ||
1063 matches!(
1064 self.alignment,
1065 AlignFlags::NORMAL | AlignFlags::AUTO | AlignFlags::STRETCH
1066 )
1067 {
1068 return origin;
1069 }
1070 let Some(offsets) = offsets else {
1071 return origin;
1072 };
1073
1074 let min = Au::zero().min(offsets.start);
1077 let max = self.containing_size - Au::zero().min(offsets.end) - size;
1078 origin.clamp_between_extremums(min, Some(max))
1079 }
1080}
1081
1082pub(crate) fn relative_adjustement(
1084 style: &ComputedValues,
1085 containing_block: &ContainingBlock,
1086) -> LogicalVec2<Au> {
1087 let cbis = containing_block.size.inline;
1091 let cbbs = containing_block.size.block;
1092 let box_offsets = style
1093 .box_offsets(containing_block.style.writing_mode)
1094 .map_inline_and_block_axes(
1095 |value| value.map(|value| value.to_used_value(cbis)),
1096 |value| match cbbs {
1097 SizeConstraint::Definite(cbbs) => value.map(|value| value.to_used_value(cbbs)),
1098 _ => match value.non_auto().and_then(|value| value.to_length()) {
1099 Some(value) => AuOrAuto::LengthPercentage(value.into()),
1100 None => AuOrAuto::Auto,
1101 },
1102 },
1103 );
1104 fn adjust(start: AuOrAuto, end: AuOrAuto) -> Au {
1105 match (start, end) {
1106 (AuOrAuto::Auto, AuOrAuto::Auto) => Au::zero(),
1107 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => -end,
1108 (AuOrAuto::LengthPercentage(start), _) => start,
1109 }
1110 }
1111 LogicalVec2 {
1112 inline: adjust(box_offsets.inline_start, box_offsets.inline_end),
1113 block: adjust(box_offsets.block_start, box_offsets.block_end),
1114 }
1115}
1116
1117#[derive(MallocSizeOf)]
1122pub(crate) struct LayoutRootLayoutInputs {
1123 fully_adjusted_static_position_rect: LogicalRect<Au>,
1127 resolved_alignment: LogicalVec2<AlignFlags>,
1130 containing_block_size: LogicalVec2<Au>,
1133 containing_block_origin: PhysicalVec<Au>,
1137 #[conditional_malloc_size_of]
1140 containing_block_style: ServoArc<ComputedValues>,
1141 original_parent_writing_mode: WritingMode,
1143}
1144
1145impl std::fmt::Debug for LayoutRootLayoutInputs {
1146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1147 f.debug_struct("LayoutRootLayoutInputs")
1148 .field("containing_block_size", &self.containing_block_size)
1149 .finish()
1150 }
1151}
1152
1153impl LayoutRootLayoutInputs {
1154 pub(crate) fn layout(
1157 &self,
1158 layout_context: &LayoutContext,
1159 context: &IndependentFormattingContext,
1160 shared_fragment: &ArcRefCell<HoistedSharedFragment>,
1161 ) -> Result<(), ()> {
1162 let containing_block = DefiniteContainingBlock {
1163 size: self.containing_block_size,
1164 style: &self.containing_block_style,
1165 };
1166 let (box_fragment, positioning_context) = context.layout_as_absolute(
1167 layout_context,
1168 &self.fully_adjusted_static_position_rect,
1169 &containing_block,
1170 self.containing_block_origin,
1171 self.resolved_alignment,
1172 self.original_parent_writing_mode,
1173 );
1174
1175 if !positioning_context.is_empty() {
1176 return Err(());
1177 }
1178
1179 shared_fragment.borrow_mut().fragment = Some(Fragment::Box(box_fragment));
1180 Ok(())
1181 }
1182}