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