1use std::mem;
6use std::sync::Arc;
7
8use app_units::Au;
9use malloc_size_of_derive::MallocSizeOf;
10use rayon::iter::IntoParallelRefMutIterator;
11use rayon::prelude::{IndexedParallelIterator, ParallelIterator};
12use servo_arc::Arc as ServoArc;
13use style::Zero;
14use style::computed_values::position::T as Position;
15use style::logical_geometry::{Direction, WritingMode};
16use style::properties::ComputedValues;
17use style::values::specified::align::AlignFlags;
18
19use crate::cell::ArcRefCell;
20use crate::context::LayoutContext;
21use crate::dom_traversal::{Contents, NodeAndStyleInfo};
22use crate::formatting_contexts::IndependentFormattingContext;
23use crate::fragment_tree::{
24 BoxFragment, Fragment, FragmentFlags, HoistedSharedFragment, LayoutRootFragment,
25};
26use crate::geom::{
27 AuOrAuto, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect,
28 PhysicalSides, PhysicalSize, PhysicalVec, ToLogical, ToLogicalWithContainingBlock,
29};
30use crate::layout_box_base::{IndependentFormattingContextLayoutResult, LayoutBoxBase};
31use crate::sizing::{LazySize, Size, SizeConstraint, Sizes};
32use crate::style_ext::{Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, DisplayInside};
33use crate::{
34 ConstraintSpace, ContainingBlock, ContainingBlockSize, DefiniteContainingBlock,
35 PropagatedBoxTreeData,
36};
37
38#[derive(Debug, MallocSizeOf)]
39pub(crate) struct AbsolutelyPositionedBox {
40 pub context: IndependentFormattingContext,
41}
42
43#[derive(Clone, MallocSizeOf)]
44pub(crate) struct HoistedAbsolutelyPositionedBox {
45 absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
46 pub fragment: ArcRefCell<HoistedSharedFragment>,
50 pub adjusted_static_position_rect: Option<PhysicalRect<Au>>,
60 pub resolved_alignment: LogicalVec2<AlignFlags>,
65 pub original_parent_writing_mode: WritingMode,
70}
71
72impl AbsolutelyPositionedBox {
73 pub fn new(context: IndependentFormattingContext) -> Self {
74 Self { context }
75 }
76
77 pub fn construct(
78 context: &LayoutContext,
79 node_info: &NodeAndStyleInfo,
80 display_inside: DisplayInside,
81 contents: Contents,
82 ) -> Self {
83 Self {
84 context: IndependentFormattingContext::construct(
85 context,
86 node_info,
87 display_inside,
88 contents,
89 PropagatedBoxTreeData::default(),
93 ),
94 }
95 }
96
97 pub(crate) fn to_hoisted(
98 absolutely_positioned_box: ArcRefCell<Self>,
99 static_position_rect: PhysicalRect<Au>,
100 resolved_alignment: LogicalVec2<AlignFlags>,
101 original_parent_writing_mode: WritingMode,
102 ) -> HoistedAbsolutelyPositionedBox {
103 HoistedAbsolutelyPositionedBox {
104 fragment: ArcRefCell::new(HoistedSharedFragment::new(static_position_rect)),
105 adjusted_static_position_rect: None,
106 resolved_alignment,
107 original_parent_writing_mode,
108 absolutely_positioned_box,
109 }
110 }
111}
112
113#[derive(Clone, Default, MallocSizeOf)]
114pub(crate) struct PositioningContext {
115 absolutes: Vec<HoistedAbsolutelyPositionedBox>,
116}
117
118impl PositioningContext {
119 #[inline]
120 pub(crate) fn is_empty(&self) -> bool {
121 self.absolutes.is_empty()
122 }
123
124 #[inline]
125 pub(crate) fn new_for_layout_box_base(layout_box_base: &LayoutBoxBase) -> Option<Self> {
126 Self::new_for_style_and_fragment_flags(
127 &layout_box_base.style,
128 &layout_box_base.base_fragment_info.flags,
129 )
130 }
131
132 fn new_for_style_and_fragment_flags(
133 style: &ComputedValues,
134 flags: &FragmentFlags,
135 ) -> Option<Self> {
136 if style.establishes_containing_block_for_absolute_descendants(*flags) {
137 Some(Self::default())
138 } else {
139 None
140 }
141 }
142
143 pub(crate) fn adjust_static_position_of_hoisted_fragments(
156 &mut self,
157 parent_fragment: &Fragment,
158 index: PositioningContextLength,
159 ) {
160 let Some(base) = parent_fragment.base() else {
161 return;
162 };
163 self.adjust_static_position_of_hoisted_fragments_with_offset(
164 &base.rect().origin.to_vector(),
165 index,
166 );
167 }
168
169 pub(crate) fn adjust_static_position_of_hoisted_fragments_with_offset(
171 &mut self,
172 offset: &PhysicalVec<Au>,
173 index: PositioningContextLength,
174 ) {
175 self.absolutes
176 .iter_mut()
177 .skip(index.0)
178 .for_each(|hoisted_box| {
179 hoisted_box.adjust_static_position_with_offset(offset);
180 })
181 }
182
183 pub(crate) fn layout_maybe_position_relative_fragment(
187 &mut self,
188 layout_context: &LayoutContext,
189 containing_block: &ContainingBlock,
190 base: &LayoutBoxBase,
191 fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment,
192 ) -> BoxFragment {
193 let establishes_containing_block_for_absolutes = base
196 .style
197 .establishes_containing_block_for_absolute_descendants(base.base_fragment_info.flags);
198 if !establishes_containing_block_for_absolutes {
199 return fragment_layout_fn(self);
200 }
201
202 let mut new_context = PositioningContext::default();
203 let mut new_fragment = fragment_layout_fn(&mut new_context);
204
205 new_context.layout_collected_children(layout_context, &mut new_fragment);
208 self.append(new_context);
209
210 if base.style.clone_position() == Position::Relative {
211 new_fragment.base.translate_rect(
212 relative_adjustement(&base.style, containing_block)
213 .to_physical_vector(containing_block.style.writing_mode)
214 .into(),
215 );
216 }
217
218 new_fragment
219 }
220
221 fn forget_unhoisted_boxes(&mut self, fragment: &BoxFragment) {
222 let style = fragment.style();
223 debug_assert!(
224 style.establishes_containing_block_for_absolute_descendants(fragment.base.flags)
225 );
226 if style.establishes_containing_block_for_all_descendants(fragment.base.flags) {
227 self.absolutes.clear();
228 } else {
229 self.absolutes
230 .retain(|hoisted_box| hoisted_box.position() == Position::Fixed);
231 }
232 }
233
234 fn take_boxes_for_fragment(
235 &mut self,
236 new_fragment: &BoxFragment,
237 boxes_to_layout_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
238 boxes_to_continue_hoisting_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
239 ) {
240 let style = new_fragment.style();
241 debug_assert!(
242 style.establishes_containing_block_for_absolute_descendants(new_fragment.base.flags)
243 );
244
245 if style.establishes_containing_block_for_all_descendants(new_fragment.base.flags) {
246 boxes_to_layout_out.append(&mut self.absolutes);
247 return;
248 }
249
250 let (mut boxes_to_layout, mut boxes_to_continue_hoisting) = self
252 .absolutes
253 .drain(..)
254 .partition(|hoisted_box| hoisted_box.position() != Position::Fixed);
255 boxes_to_layout_out.append(&mut boxes_to_layout);
256 boxes_to_continue_hoisting_out.append(&mut boxes_to_continue_hoisting);
257 }
258
259 pub(crate) fn layout_collected_children(
262 &mut self,
263 layout_context: &LayoutContext,
264 new_fragment: &mut BoxFragment,
265 ) {
266 if self.absolutes.is_empty() {
267 return;
268 }
269
270 let style = new_fragment.style().clone();
279 if !style.establishes_containing_block_for_absolute_descendants(new_fragment.base.flags) {
280 return;
281 }
282
283 let padding_rect = PhysicalRect::new(
284 PhysicalPoint::origin(),
286 new_fragment.base.rect().size,
287 )
288 .outer_rect(new_fragment.padding);
289 let containing_block = DefiniteContainingBlock {
290 size: padding_rect.size.to_logical(style.writing_mode),
291 style: &style,
292 };
293
294 let mut fixed_position_boxes_to_hoist = Vec::new();
295 let mut boxes_to_layout = Vec::new();
296 self.take_boxes_for_fragment(
297 new_fragment,
298 &mut boxes_to_layout,
299 &mut fixed_position_boxes_to_hoist,
300 );
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 new_fragment.children,
312 &mut self.absolutes,
313 &containing_block,
314 new_fragment.padding,
315 );
316
317 self.take_boxes_for_fragment(
318 new_fragment,
319 &mut boxes_to_layout,
320 &mut fixed_position_boxes_to_hoist,
321 );
322 }
323
324 self.absolutes = fixed_position_boxes_to_hoist;
328 }
329
330 pub(crate) fn push(&mut self, hoisted_box: HoistedAbsolutelyPositionedBox) {
331 debug_assert!(hoisted_box.position().is_absolutely_positioned());
332 self.absolutes.push(hoisted_box);
333 }
334
335 pub(crate) fn append(&mut self, mut other: Self) {
336 if other.absolutes.is_empty() {
337 return;
338 }
339 if self.absolutes.is_empty() {
340 self.absolutes = other.absolutes;
341 } else {
342 self.absolutes.append(&mut other.absolutes)
343 }
344 }
345
346 pub(crate) fn layout_initial_containing_block_children(
347 &mut self,
348 layout_context: &LayoutContext,
349 initial_containing_block: &DefiniteContainingBlock,
350 fragments: &mut Vec<Fragment>,
351 ) {
352 while !self.absolutes.is_empty() {
357 HoistedAbsolutelyPositionedBox::layout_many(
358 layout_context,
359 mem::take(&mut self.absolutes),
360 fragments,
361 &mut self.absolutes,
362 initial_containing_block,
363 Default::default(),
364 )
365 }
366 }
367
368 pub(crate) fn len(&self) -> PositioningContextLength {
370 PositioningContextLength(self.absolutes.len())
371 }
372
373 pub(crate) fn truncate(&mut self, length: &PositioningContextLength) {
377 self.absolutes.truncate(length.0)
378 }
379}
380
381#[derive(Clone, Copy, Debug, PartialEq)]
383pub(crate) struct PositioningContextLength(usize);
384
385impl Zero for PositioningContextLength {
386 fn zero() -> Self {
387 Self(0)
388 }
389
390 fn is_zero(&self) -> bool {
391 self.0.is_zero()
392 }
393}
394
395impl HoistedAbsolutelyPositionedBox {
396 fn position(&self) -> Position {
397 let position = self
398 .absolutely_positioned_box
399 .borrow()
400 .context
401 .style()
402 .clone_position();
403 assert!(position.is_absolutely_positioned());
404 position
405 }
406
407 pub(crate) fn layout_many(
408 layout_context: &LayoutContext,
409 mut boxes: Vec<Self>,
410 fragments: &mut Vec<Fragment>,
411 for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>,
412 containing_block: &DefiniteContainingBlock,
413 containing_block_padding: PhysicalSides<Au>,
414 ) {
415 let job_sizes = boxes.iter().map(|hoisted_box| {
416 hoisted_box
417 .absolutely_positioned_box
418 .borrow()
419 .context
420 .subtree_size()
421 });
422 if layout_context.should_parallelize_layout(job_sizes) {
423 let mut new_fragments = Vec::new();
424 let mut new_hoisted_boxes = Vec::new();
425
426 boxes
427 .par_iter_mut()
428 .map(|hoisted_box| {
429 let mut new_hoisted_boxes: Vec<HoistedAbsolutelyPositionedBox> = Vec::new();
430 let new_fragment = hoisted_box.layout(
431 layout_context,
432 &mut new_hoisted_boxes,
433 containing_block,
434 containing_block_padding,
435 );
436 (new_fragment, new_hoisted_boxes)
437 })
438 .unzip_into_vecs(&mut new_fragments, &mut new_hoisted_boxes);
439
440 fragments.extend(new_fragments);
441 for_nearest_containing_block_for_all_descendants
442 .extend(new_hoisted_boxes.into_iter().flatten());
443 } else {
444 fragments.extend(boxes.iter_mut().map(|hoisted_box| {
445 hoisted_box.layout(
446 layout_context,
447 for_nearest_containing_block_for_all_descendants,
448 containing_block,
449 containing_block_padding,
450 )
451 }))
452 }
453 }
454
455 pub(crate) fn layout(
456 &mut self,
457 layout_context: &LayoutContext,
458 hoisted_absolutes_from_children: &mut Vec<HoistedAbsolutelyPositionedBox>,
459 containing_block: &DefiniteContainingBlock,
460 containing_block_padding: PhysicalSides<Au>,
461 ) -> Fragment {
462 let mut static_position_rect = self
466 .static_position_rect()
467 .outer_rect(-containing_block_padding);
468 static_position_rect.size = static_position_rect.size.max(PhysicalSize::zero());
469 let fully_adjusted_static_position_rect =
470 static_position_rect.to_logical(&containing_block.into());
471
472 let absolutely_positioned_box = self.absolutely_positioned_box.borrow();
473 let independent_formatting_context = &absolutely_positioned_box.context;
474 let (box_fragment, mut positioning_context) = independent_formatting_context
475 .layout_as_absolute(
476 layout_context,
477 &fully_adjusted_static_position_rect,
478 containing_block,
479 self.resolved_alignment,
480 self.original_parent_writing_mode,
481 );
482
483 let is_layout_root = positioning_context.is_empty();
488
489 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
495 &box_fragment.content_rect().origin.to_vector(),
496 PositioningContextLength::zero(),
497 );
498 hoisted_absolutes_from_children.extend(positioning_context.absolutes);
499
500 let fragment = Fragment::Box(box_fragment);
501 self.fragment.borrow_mut().fragment = Some(fragment.clone());
502
503 let fragment = match is_layout_root {
504 false => fragment,
505 true => Fragment::LayoutRoot(LayoutRootFragment {
506 fragment: self.fragment.clone(),
507 }),
508 };
509
510 independent_formatting_context
511 .base
512 .set_fragment(fragment.clone());
513
514 *independent_formatting_context
515 .layout_root_layout_inputs
516 .borrow_mut() = is_layout_root.then(|| {
517 Box::new(LayoutRootLayoutInputs {
518 fully_adjusted_static_position_rect,
519 resolved_alignment: self.resolved_alignment,
520 containing_block_size: containing_block.size,
521 containing_block_style: containing_block.style.clone(),
522 original_parent_writing_mode: self.original_parent_writing_mode,
523 })
524 });
525
526 fragment
527 }
528
529 fn static_position_rect(&self) -> PhysicalRect<Au> {
530 self.adjusted_static_position_rect
531 .unwrap_or_else(|| self.fragment.borrow().original_static_position_rect)
532 }
533
534 fn adjust_static_position_with_offset(&mut self, offset: &PhysicalVec<Au>) {
535 self.adjusted_static_position_rect = Some(self.static_position_rect().translate(*offset));
536 }
537}
538
539impl IndependentFormattingContext {
540 pub(crate) fn layout_as_absolute(
541 &self,
542 layout_context: &LayoutContext,
543 static_position_rect: &LogicalRect<Au>,
544 containing_block: &DefiniteContainingBlock,
545 resolved_alignment: LogicalVec2<AlignFlags>,
546 original_parent_writing_mode: WritingMode,
547 ) -> (Arc<BoxFragment>, PositioningContext) {
548 let cbis = containing_block.size.inline;
549 let cbbs = containing_block.size.block;
550 let containing_block_writing_mode = containing_block.style.writing_mode;
551 let style = self.style().clone();
552 let layout_style = self.layout_style();
553 let ContentBoxSizesAndPBM {
554 content_box_sizes,
555 pbm,
556 ..
557 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
558 let is_table = layout_style.is_table();
559 let is_table_or_replaced = is_table || self.is_replaced();
560 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
561
562 let box_offset = style.box_offsets(containing_block.style.writing_mode);
563
564 let inline_box_offsets = box_offset.inline_sides().percentages_relative_to(cbis);
567 let inline_alignment = match inline_box_offsets.either_specified() {
568 true => style.clone_justify_self().0,
569 false => resolved_alignment.inline,
570 };
571
572 let inline_axis_solver = AbsoluteAxisSolver {
573 axis: Direction::Inline,
574 containing_size: cbis,
575 padding_border_sum: pbm.padding_border_sums.inline,
576 computed_margin_start: pbm.margin.inline_start,
577 computed_margin_end: pbm.margin.inline_end,
578 computed_sizes: content_box_sizes.inline,
579 avoid_negative_margin_start: true,
580 box_offsets: inline_box_offsets,
581 static_position_rect_axis: static_position_rect.get_axis(Direction::Inline),
582 alignment: inline_alignment,
583 flip_anchor: original_parent_writing_mode.is_bidi_ltr() !=
584 containing_block_writing_mode.is_bidi_ltr(),
585 is_table_or_replaced,
586 };
587
588 let block_box_offsets = box_offset.block_sides().percentages_relative_to(cbbs);
591 let block_alignment = match block_box_offsets.either_specified() {
592 true => style.clone_align_self().0,
593 false => resolved_alignment.block,
594 };
595 let block_axis_solver = AbsoluteAxisSolver {
596 axis: Direction::Block,
597 containing_size: cbbs,
598 padding_border_sum: pbm.padding_border_sums.block,
599 computed_margin_start: pbm.margin.block_start,
600 computed_margin_end: pbm.margin.block_end,
601 computed_sizes: content_box_sizes.block,
602 avoid_negative_margin_start: false,
603 box_offsets: block_box_offsets,
604 static_position_rect_axis: static_position_rect.get_axis(Direction::Block),
605 alignment: block_alignment,
606 flip_anchor: false,
607 is_table_or_replaced,
608 };
609
610 let block_automatic_size = block_axis_solver.automatic_size();
613 let block_stretch_size = Some(block_axis_solver.stretch_size());
614 let inline_stretch_size = inline_axis_solver.stretch_size();
615 let tentative_block_content_size =
616 self.tentative_block_content_size(preferred_aspect_ratio, inline_stretch_size);
617 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
618 SizeConstraint::Definite(block_axis_solver.computed_sizes.resolve(
619 Direction::Block,
620 block_automatic_size,
621 Au::zero,
622 block_stretch_size,
623 || block_content_size,
624 is_table,
625 ))
626 } else {
627 block_axis_solver.computed_sizes.resolve_extrinsic(
628 block_automatic_size,
629 Au::zero(),
630 block_stretch_size,
631 )
632 };
633
634 let get_inline_content_size = || {
637 let constraint_space =
638 ConstraintSpace::new(tentative_block_size, &style, preferred_aspect_ratio);
639 self.inline_content_sizes(layout_context, &constraint_space)
640 .sizes
641 };
642 let inline_size = inline_axis_solver.computed_sizes.resolve(
643 Direction::Inline,
644 inline_axis_solver.automatic_size(),
645 Au::zero,
646 Some(inline_stretch_size),
647 get_inline_content_size,
648 is_table,
649 );
650
651 let containing_block_for_children = ContainingBlock {
652 size: ContainingBlockSize {
653 inline: inline_size,
654 block: tentative_block_size,
655 },
656 style: &style,
657 };
658 assert_eq!(
660 containing_block_writing_mode.is_horizontal(),
661 style.writing_mode.is_horizontal(),
662 "Mixed horizontal and vertical writing modes are not supported yet"
663 );
664
665 let mut positioning_context = PositioningContext::default();
666 let lazy_block_size = LazySize::new(
667 &block_axis_solver.computed_sizes,
668 Direction::Block,
669 block_automatic_size,
670 Au::zero,
671 block_stretch_size,
672 is_table,
673 );
674
675 let containing_block = &containing_block.into();
676 let (layout, is_cached) = self.layout_and_is_cached(
677 layout_context,
678 &mut positioning_context,
679 &containing_block_for_children,
680 containing_block,
681 preferred_aspect_ratio,
682 &lazy_block_size,
683 );
684 let IndependentFormattingContextLayoutResult {
685 content_inline_size_for_table,
686 content_block_size,
687 fragments,
688 specific_layout_info,
689 ..
690 } = layout;
691
692 let content_size = LogicalVec2 {
693 inline: content_inline_size_for_table.unwrap_or(inline_size),
695
696 block: lazy_block_size.resolve(|| content_block_size),
698 };
699
700 let inline_margins = inline_axis_solver.solve_margins(content_size.inline);
701 let block_margins = block_axis_solver.solve_margins(content_size.block);
702 let margin = LogicalSides {
703 inline_start: inline_margins.start,
704 inline_end: inline_margins.end,
705 block_start: block_margins.start,
706 block_end: block_margins.end,
707 };
708
709 let pb = pbm.padding + pbm.border;
710 let margin_rect_size = content_size + pbm.padding_border_sums + margin.sum();
711 let inline_origin = inline_axis_solver.origin_for_margin_box(
712 margin_rect_size.inline,
713 style.writing_mode,
714 original_parent_writing_mode,
715 containing_block_writing_mode,
716 );
717 let block_origin = block_axis_solver.origin_for_margin_box(
718 margin_rect_size.block,
719 style.writing_mode,
720 original_parent_writing_mode,
721 containing_block_writing_mode,
722 );
723 let content_rect = LogicalRect {
724 start_corner: LogicalVec2 {
725 inline: inline_origin + margin.inline_start + pb.inline_start,
726 block: block_origin + margin.block_start + pb.block_start,
727 },
728 size: content_size,
729 }
730 .as_physical(Some(containing_block));
731
732 if is_cached &&
733 let Some(old_fragment) = self.base.fragments().first() &&
734 let Some(old_box_fragment) = old_fragment
735 .retrieve_box_fragment()
736 .map(|fragment| fragment.clone()) &&
737 content_rect == old_box_fragment.content_rect()
738 {
739 positioning_context.forget_unhoisted_boxes(&old_box_fragment);
742 return (old_box_fragment, positioning_context);
743 }
744
745 let mut new_box_fragment = BoxFragment::new(
746 self.base_fragment_info(),
747 style,
748 fragments,
749 content_rect,
750 pbm.padding.to_physical(containing_block_writing_mode),
751 pbm.border.to_physical(containing_block_writing_mode),
752 margin.to_physical(containing_block_writing_mode),
753 specific_layout_info,
754 );
755
756 positioning_context.layout_collected_children(layout_context, &mut new_box_fragment);
760 (new_box_fragment.into(), positioning_context)
761 }
762}
763
764#[derive(Clone, Copy, Debug)]
765struct RectAxis {
766 origin: Au,
767 length: Au,
768}
769
770impl LogicalRect<Au> {
771 fn get_axis(&self, axis: Direction) -> RectAxis {
772 match axis {
773 Direction::Block => RectAxis {
774 origin: self.start_corner.block,
775 length: self.size.block,
776 },
777 Direction::Inline => RectAxis {
778 origin: self.start_corner.inline,
779 length: self.size.inline,
780 },
781 }
782 }
783}
784
785struct AbsoluteAxisSolver {
786 axis: Direction,
787 containing_size: Au,
788 padding_border_sum: Au,
789 computed_margin_start: AuOrAuto,
790 computed_margin_end: AuOrAuto,
791 computed_sizes: Sizes,
792 avoid_negative_margin_start: bool,
793 box_offsets: LogicalSides1D<AuOrAuto>,
794 static_position_rect_axis: RectAxis,
795 alignment: AlignFlags,
796 flip_anchor: bool,
797 is_table_or_replaced: bool,
798}
799
800impl AbsoluteAxisSolver {
801 fn inset_sum(&self) -> Au {
806 match (
807 self.box_offsets.start.non_auto(),
808 self.box_offsets.end.non_auto(),
809 ) {
810 (None, None) => {
811 if self.flip_anchor {
812 self.containing_size -
813 self.static_position_rect_axis.origin -
814 self.static_position_rect_axis.length
815 } else {
816 self.static_position_rect_axis.origin
817 }
818 },
819 (Some(start), None) => start,
820 (None, Some(end)) => end,
821 (Some(start), Some(end)) => start + end,
822 }
823 }
824
825 #[inline]
828 fn available_space(&self) -> Au {
829 Au::zero().max(self.containing_size - self.inset_sum())
830 }
831
832 #[inline]
833 fn automatic_size(&self) -> Size<Au> {
834 match self.alignment.value() {
835 _ if self.box_offsets.either_auto() => Size::FitContent,
836 AlignFlags::NORMAL | AlignFlags::AUTO if !self.is_table_or_replaced => Size::Stretch,
837 AlignFlags::STRETCH => Size::Stretch,
838 _ => Size::FitContent,
839 }
840 }
841
842 #[inline]
843 fn stretch_size(&self) -> Au {
844 Au::zero().max(
845 self.available_space() -
846 self.padding_border_sum -
847 self.computed_margin_start.auto_is(Au::zero) -
848 self.computed_margin_end.auto_is(Au::zero),
849 )
850 }
851
852 fn solve_margins(&self, size: Au) -> LogicalSides1D<Au> {
853 if self.box_offsets.either_auto() {
854 LogicalSides1D::new(
855 self.computed_margin_start.auto_is(Au::zero),
856 self.computed_margin_end.auto_is(Au::zero),
857 )
858 } else {
859 let free_space = self.available_space() - self.padding_border_sum - size;
860 match (self.computed_margin_start, self.computed_margin_end) {
861 (AuOrAuto::Auto, AuOrAuto::Auto) => {
862 if self.avoid_negative_margin_start && free_space < Au::zero() {
863 LogicalSides1D::new(Au::zero(), free_space)
864 } else {
865 let margin_start = free_space / 2;
866 LogicalSides1D::new(margin_start, free_space - margin_start)
867 }
868 },
869 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
870 LogicalSides1D::new(free_space - end, end)
871 },
872 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => {
873 LogicalSides1D::new(start, free_space - start)
874 },
875 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
876 LogicalSides1D::new(start, end)
877 },
878 }
879 }
880 }
881
882 fn origin_for_margin_box(
883 &self,
884 size: Au,
885 self_writing_mode: WritingMode,
886 original_parent_writing_mode: WritingMode,
887 containing_block_writing_mode: WritingMode,
888 ) -> Au {
889 let (alignment_container, alignment_container_writing_mode, flip_anchor, offsets) = match (
890 self.box_offsets.start.non_auto(),
891 self.box_offsets.end.non_auto(),
892 ) {
893 (None, None) => (
894 self.static_position_rect_axis,
895 original_parent_writing_mode,
896 self.flip_anchor,
897 None,
898 ),
899 (Some(start), Some(end)) => {
900 let alignment_container = RectAxis {
901 origin: start,
902 length: self.available_space(),
903 };
904 (
905 alignment_container,
906 containing_block_writing_mode,
907 false,
908 Some(LogicalSides1D { start, end }),
909 )
910 },
911 (Some(start), None) => return start,
915 (None, Some(end)) => {
916 return self.containing_size - size - end;
917 },
918 };
919
920 assert_eq!(
921 self_writing_mode.is_horizontal(),
922 original_parent_writing_mode.is_horizontal(),
923 "Mixed horizontal and vertical writing modes are not supported yet"
924 );
925 assert_eq!(
926 self_writing_mode.is_horizontal(),
927 containing_block_writing_mode.is_horizontal(),
928 "Mixed horizontal and vertical writing modes are not supported yet"
929 );
930 let self_value_matches_container = || {
931 self.axis == Direction::Block ||
932 self_writing_mode.is_bidi_ltr() == alignment_container_writing_mode.is_bidi_ltr()
933 };
934
935 let alignment = match self.alignment.value() {
940 AlignFlags::CENTER | AlignFlags::SPACE_AROUND | AlignFlags::SPACE_EVENLY => {
944 AlignFlags::CENTER
945 },
946 AlignFlags::SELF_START if self_value_matches_container() => AlignFlags::START,
948 AlignFlags::SELF_START => AlignFlags::END,
949 AlignFlags::SELF_END if self_value_matches_container() => AlignFlags::END,
951 AlignFlags::SELF_END => AlignFlags::START,
952 AlignFlags::LEFT if alignment_container_writing_mode.is_bidi_ltr() => AlignFlags::START,
954 AlignFlags::LEFT => AlignFlags::END,
955 AlignFlags::RIGHT if alignment_container_writing_mode.is_bidi_ltr() => AlignFlags::END,
957 AlignFlags::RIGHT => AlignFlags::START,
958 AlignFlags::END | AlignFlags::FLEX_END | AlignFlags::LAST_BASELINE => AlignFlags::END,
962 _ => AlignFlags::START,
966 };
967
968 let alignment = match alignment {
969 AlignFlags::START if flip_anchor => AlignFlags::END,
970 AlignFlags::END if flip_anchor => AlignFlags::START,
971 alignment => alignment,
972 };
973
974 let free_space = alignment_container.length - size;
975 let flags = self.alignment.flags();
976 let alignment = if flags == AlignFlags::SAFE && free_space < Au::zero() {
977 AlignFlags::START
978 } else {
979 alignment
980 };
981
982 let origin = match alignment {
983 AlignFlags::START => alignment_container.origin,
984 AlignFlags::CENTER => alignment_container.origin + free_space / 2,
985 AlignFlags::END => alignment_container.origin + free_space,
986 _ => unreachable!(),
987 };
988 if matches!(flags, AlignFlags::SAFE | AlignFlags::UNSAFE) ||
989 matches!(
990 self.alignment,
991 AlignFlags::NORMAL | AlignFlags::AUTO | AlignFlags::STRETCH
992 )
993 {
994 return origin;
995 }
996 let Some(offsets) = offsets else {
997 return origin;
998 };
999
1000 let min = Au::zero().min(offsets.start);
1003 let max = self.containing_size - Au::zero().min(offsets.end) - size;
1004 origin.clamp_between_extremums(min, Some(max))
1005 }
1006}
1007
1008pub(crate) fn relative_adjustement(
1010 style: &ComputedValues,
1011 containing_block: &ContainingBlock,
1012) -> LogicalVec2<Au> {
1013 let cbis = containing_block.size.inline;
1017 let cbbs = containing_block.size.block;
1018 let box_offsets = style
1019 .box_offsets(containing_block.style.writing_mode)
1020 .map_inline_and_block_axes(
1021 |value| value.map(|value| value.to_used_value(cbis)),
1022 |value| match cbbs {
1023 SizeConstraint::Definite(cbbs) => value.map(|value| value.to_used_value(cbbs)),
1024 _ => match value.non_auto().and_then(|value| value.to_length()) {
1025 Some(value) => AuOrAuto::LengthPercentage(value.into()),
1026 None => AuOrAuto::Auto,
1027 },
1028 },
1029 );
1030 fn adjust(start: AuOrAuto, end: AuOrAuto) -> Au {
1031 match (start, end) {
1032 (AuOrAuto::Auto, AuOrAuto::Auto) => Au::zero(),
1033 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => -end,
1034 (AuOrAuto::LengthPercentage(start), _) => start,
1035 }
1036 }
1037 LogicalVec2 {
1038 inline: adjust(box_offsets.inline_start, box_offsets.inline_end),
1039 block: adjust(box_offsets.block_start, box_offsets.block_end),
1040 }
1041}
1042
1043#[derive(MallocSizeOf)]
1048pub(crate) struct LayoutRootLayoutInputs {
1049 fully_adjusted_static_position_rect: LogicalRect<Au>,
1053 resolved_alignment: LogicalVec2<AlignFlags>,
1056 containing_block_size: LogicalVec2<Au>,
1059 #[conditional_malloc_size_of]
1062 containing_block_style: ServoArc<ComputedValues>,
1063 original_parent_writing_mode: WritingMode,
1065}
1066
1067impl std::fmt::Debug for LayoutRootLayoutInputs {
1068 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1069 f.debug_struct("LayoutRootLayoutInputs")
1070 .field("containing_block_size", &self.containing_block_size)
1071 .finish()
1072 }
1073}
1074
1075impl LayoutRootLayoutInputs {
1076 pub(crate) fn layout(
1079 &self,
1080 layout_context: &LayoutContext,
1081 context: &IndependentFormattingContext,
1082 shared_fragment: &ArcRefCell<HoistedSharedFragment>,
1083 ) -> Result<(), ()> {
1084 let containing_block = DefiniteContainingBlock {
1085 size: self.containing_block_size,
1086 style: &self.containing_block_style,
1087 };
1088 let (box_fragment, positioning_context) = context.layout_as_absolute(
1089 layout_context,
1090 &self.fully_adjusted_static_position_rect,
1091 &containing_block,
1092 self.resolved_alignment,
1093 self.original_parent_writing_mode,
1094 );
1095
1096 if !positioning_context.is_empty() {
1097 return Err(());
1098 }
1099
1100 shared_fragment.borrow_mut().fragment = Some(Fragment::Box(box_fragment));
1101 Ok(())
1102 }
1103}