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.static_position_rect().translate(PhysicalVec::new(
467 containing_block_padding.left,
468 containing_block_padding.top,
469 ));
470 static_position_rect.size = static_position_rect.size.max(PhysicalSize::zero());
471 let fully_adjusted_static_position_rect =
472 static_position_rect.to_logical(&containing_block.into());
473
474 let absolutely_positioned_box = self.absolutely_positioned_box.borrow();
475 let independent_formatting_context = &absolutely_positioned_box.context;
476 let (box_fragment, mut positioning_context) = independent_formatting_context
477 .layout_as_absolute(
478 layout_context,
479 &fully_adjusted_static_position_rect,
480 containing_block,
481 self.resolved_alignment,
482 self.original_parent_writing_mode,
483 );
484
485 let is_layout_root = positioning_context.is_empty();
490
491 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
497 &box_fragment.content_rect().origin.to_vector(),
498 PositioningContextLength::zero(),
499 );
500 hoisted_absolutes_from_children.extend(positioning_context.absolutes);
501
502 let fragment = Fragment::Box(box_fragment);
503 self.fragment.borrow_mut().fragment = Some(fragment.clone());
504
505 let fragment = match is_layout_root {
506 false => fragment,
507 true => Fragment::LayoutRoot(LayoutRootFragment {
508 fragment: self.fragment.clone(),
509 }),
510 };
511
512 independent_formatting_context
513 .base
514 .set_fragment(fragment.clone());
515
516 *independent_formatting_context
517 .layout_root_layout_inputs
518 .borrow_mut() = is_layout_root.then(|| {
519 Box::new(LayoutRootLayoutInputs {
520 fully_adjusted_static_position_rect,
521 resolved_alignment: self.resolved_alignment,
522 containing_block_size: containing_block.size,
523 containing_block_style: containing_block.style.clone(),
524 original_parent_writing_mode: self.original_parent_writing_mode,
525 })
526 });
527
528 fragment
529 }
530
531 fn static_position_rect(&self) -> PhysicalRect<Au> {
532 self.adjusted_static_position_rect
533 .unwrap_or_else(|| self.fragment.borrow().original_static_position_rect)
534 }
535
536 fn adjust_static_position_with_offset(&mut self, offset: &PhysicalVec<Au>) {
537 self.adjusted_static_position_rect = Some(self.static_position_rect().translate(*offset));
538 }
539}
540
541impl IndependentFormattingContext {
542 pub(crate) fn layout_as_absolute(
543 &self,
544 layout_context: &LayoutContext,
545 static_position_rect: &LogicalRect<Au>,
546 containing_block: &DefiniteContainingBlock,
547 resolved_alignment: LogicalVec2<AlignFlags>,
548 original_parent_writing_mode: WritingMode,
549 ) -> (Arc<BoxFragment>, PositioningContext) {
550 let cbis = containing_block.size.inline;
551 let cbbs = containing_block.size.block;
552 let containing_block_writing_mode = containing_block.style.writing_mode;
553 let style = self.style().clone();
554 let layout_style = self.layout_style();
555 let ContentBoxSizesAndPBM {
556 content_box_sizes,
557 pbm,
558 ..
559 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
560 let is_table = layout_style.is_table();
561 let is_table_or_replaced = is_table || self.is_replaced();
562 let preferred_aspect_ratio = self.preferred_aspect_ratio(&pbm.padding_border_sums);
563
564 let box_offset = style.box_offsets(containing_block.style.writing_mode);
565
566 let inline_box_offsets = box_offset.inline_sides().percentages_relative_to(cbis);
569 let inline_alignment = match inline_box_offsets.either_specified() {
570 true => style.clone_justify_self().0,
571 false => resolved_alignment.inline,
572 };
573
574 let inline_axis_solver = AbsoluteAxisSolver {
575 axis: Direction::Inline,
576 containing_size: cbis,
577 padding_border_sum: pbm.padding_border_sums.inline,
578 computed_margin_start: pbm.margin.inline_start,
579 computed_margin_end: pbm.margin.inline_end,
580 computed_sizes: content_box_sizes.inline,
581 avoid_negative_margin_start: true,
582 box_offsets: inline_box_offsets,
583 static_position_rect_axis: static_position_rect.get_axis(Direction::Inline),
584 alignment: inline_alignment,
585 flip_anchor: original_parent_writing_mode.is_bidi_ltr() !=
586 containing_block_writing_mode.is_bidi_ltr(),
587 is_table_or_replaced,
588 };
589
590 let block_box_offsets = box_offset.block_sides().percentages_relative_to(cbbs);
593 let block_alignment = match block_box_offsets.either_specified() {
594 true => style.clone_align_self().0,
595 false => resolved_alignment.block,
596 };
597 let block_axis_solver = AbsoluteAxisSolver {
598 axis: Direction::Block,
599 containing_size: cbbs,
600 padding_border_sum: pbm.padding_border_sums.block,
601 computed_margin_start: pbm.margin.block_start,
602 computed_margin_end: pbm.margin.block_end,
603 computed_sizes: content_box_sizes.block,
604 avoid_negative_margin_start: false,
605 box_offsets: block_box_offsets,
606 static_position_rect_axis: static_position_rect.get_axis(Direction::Block),
607 alignment: block_alignment,
608 flip_anchor: false,
609 is_table_or_replaced,
610 };
611
612 let block_automatic_size = block_axis_solver.automatic_size();
615 let block_stretch_size = Some(block_axis_solver.stretch_size());
616 let inline_stretch_size = inline_axis_solver.stretch_size();
617 let tentative_block_content_size =
618 self.tentative_block_content_size(preferred_aspect_ratio, inline_stretch_size);
619 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
620 SizeConstraint::Definite(block_axis_solver.computed_sizes.resolve(
621 Direction::Block,
622 block_automatic_size,
623 Au::zero,
624 block_stretch_size,
625 || block_content_size,
626 is_table,
627 ))
628 } else {
629 block_axis_solver.computed_sizes.resolve_extrinsic(
630 block_automatic_size,
631 Au::zero(),
632 block_stretch_size,
633 )
634 };
635
636 let get_inline_content_size = || {
639 let constraint_space =
640 ConstraintSpace::new(tentative_block_size, &style, preferred_aspect_ratio);
641 self.inline_content_sizes(layout_context, &constraint_space)
642 .sizes
643 };
644 let inline_size = inline_axis_solver.computed_sizes.resolve(
645 Direction::Inline,
646 inline_axis_solver.automatic_size(),
647 Au::zero,
648 Some(inline_stretch_size),
649 get_inline_content_size,
650 is_table,
651 );
652
653 let containing_block_for_children = ContainingBlock {
654 size: ContainingBlockSize {
655 inline: inline_size,
656 block: tentative_block_size,
657 },
658 style: &style,
659 };
660 assert_eq!(
662 containing_block_writing_mode.is_horizontal(),
663 style.writing_mode.is_horizontal(),
664 "Mixed horizontal and vertical writing modes are not supported yet"
665 );
666
667 let mut positioning_context = PositioningContext::default();
668 let lazy_block_size = LazySize::new(
669 &block_axis_solver.computed_sizes,
670 Direction::Block,
671 block_automatic_size,
672 Au::zero,
673 block_stretch_size,
674 is_table,
675 );
676
677 let containing_block = &containing_block.into();
678 let (layout, is_cached) = self.layout_and_is_cached(
679 layout_context,
680 &mut positioning_context,
681 &containing_block_for_children,
682 containing_block,
683 preferred_aspect_ratio,
684 &lazy_block_size,
685 );
686 let IndependentFormattingContextLayoutResult {
687 content_inline_size_for_table,
688 content_block_size,
689 fragments,
690 specific_layout_info,
691 ..
692 } = layout;
693
694 let content_size = LogicalVec2 {
695 inline: content_inline_size_for_table.unwrap_or(inline_size),
697
698 block: lazy_block_size.resolve(|| content_block_size),
700 };
701
702 let inline_margins = inline_axis_solver.solve_margins(content_size.inline);
703 let block_margins = block_axis_solver.solve_margins(content_size.block);
704 let margin = LogicalSides {
705 inline_start: inline_margins.start,
706 inline_end: inline_margins.end,
707 block_start: block_margins.start,
708 block_end: block_margins.end,
709 };
710
711 let pb = pbm.padding + pbm.border;
712 let margin_rect_size = content_size + pbm.padding_border_sums + margin.sum();
713 let inline_origin = inline_axis_solver.origin_for_margin_box(
714 margin_rect_size.inline,
715 style.writing_mode,
716 original_parent_writing_mode,
717 containing_block_writing_mode,
718 );
719 let block_origin = block_axis_solver.origin_for_margin_box(
720 margin_rect_size.block,
721 style.writing_mode,
722 original_parent_writing_mode,
723 containing_block_writing_mode,
724 );
725 let content_rect = LogicalRect {
726 start_corner: LogicalVec2 {
727 inline: inline_origin + margin.inline_start + pb.inline_start,
728 block: block_origin + margin.block_start + pb.block_start,
729 },
730 size: content_size,
731 }
732 .as_physical(Some(containing_block));
733
734 if is_cached &&
735 let Some(old_fragment) = self.base.fragments().first() &&
736 let Some(old_box_fragment) = old_fragment
737 .retrieve_box_fragment()
738 .map(|fragment| fragment.clone()) &&
739 content_rect == old_box_fragment.content_rect()
740 {
741 positioning_context.forget_unhoisted_boxes(&old_box_fragment);
744 return (old_box_fragment, positioning_context);
745 }
746
747 let mut new_box_fragment = BoxFragment::new(
748 self.base_fragment_info(),
749 style,
750 fragments,
751 content_rect,
752 pbm.padding.to_physical(containing_block_writing_mode),
753 pbm.border.to_physical(containing_block_writing_mode),
754 margin.to_physical(containing_block_writing_mode),
755 specific_layout_info,
756 );
757
758 positioning_context.layout_collected_children(layout_context, &mut new_box_fragment);
762 (new_box_fragment.into(), positioning_context)
763 }
764}
765
766#[derive(Clone, Copy, Debug)]
767struct RectAxis {
768 origin: Au,
769 length: Au,
770}
771
772impl LogicalRect<Au> {
773 fn get_axis(&self, axis: Direction) -> RectAxis {
774 match axis {
775 Direction::Block => RectAxis {
776 origin: self.start_corner.block,
777 length: self.size.block,
778 },
779 Direction::Inline => RectAxis {
780 origin: self.start_corner.inline,
781 length: self.size.inline,
782 },
783 }
784 }
785}
786
787struct AbsoluteAxisSolver {
788 axis: Direction,
789 containing_size: Au,
790 padding_border_sum: Au,
791 computed_margin_start: AuOrAuto,
792 computed_margin_end: AuOrAuto,
793 computed_sizes: Sizes,
794 avoid_negative_margin_start: bool,
795 box_offsets: LogicalSides1D<AuOrAuto>,
796 static_position_rect_axis: RectAxis,
797 alignment: AlignFlags,
798 flip_anchor: bool,
799 is_table_or_replaced: bool,
800}
801
802impl AbsoluteAxisSolver {
803 fn inset_sum(&self) -> Au {
808 match (
809 self.box_offsets.start.non_auto(),
810 self.box_offsets.end.non_auto(),
811 ) {
812 (None, None) => {
813 if self.flip_anchor {
814 self.containing_size -
815 self.static_position_rect_axis.origin -
816 self.static_position_rect_axis.length
817 } else {
818 self.static_position_rect_axis.origin
819 }
820 },
821 (Some(start), None) => start,
822 (None, Some(end)) => end,
823 (Some(start), Some(end)) => start + end,
824 }
825 }
826
827 #[inline]
830 fn available_space(&self) -> Au {
831 Au::zero().max(self.containing_size - self.inset_sum())
832 }
833
834 #[inline]
835 fn automatic_size(&self) -> Size<Au> {
836 match self.alignment.value() {
837 _ if self.box_offsets.either_auto() => Size::FitContent,
838 AlignFlags::NORMAL | AlignFlags::AUTO if !self.is_table_or_replaced => Size::Stretch,
839 AlignFlags::STRETCH => Size::Stretch,
840 _ => Size::FitContent,
841 }
842 }
843
844 #[inline]
845 fn stretch_size(&self) -> Au {
846 Au::zero().max(
847 self.available_space() -
848 self.padding_border_sum -
849 self.computed_margin_start.auto_is(Au::zero) -
850 self.computed_margin_end.auto_is(Au::zero),
851 )
852 }
853
854 fn solve_margins(&self, size: Au) -> LogicalSides1D<Au> {
855 if self.box_offsets.either_auto() {
856 LogicalSides1D::new(
857 self.computed_margin_start.auto_is(Au::zero),
858 self.computed_margin_end.auto_is(Au::zero),
859 )
860 } else {
861 let free_space = self.available_space() - self.padding_border_sum - size;
862 match (self.computed_margin_start, self.computed_margin_end) {
863 (AuOrAuto::Auto, AuOrAuto::Auto) => {
864 if self.avoid_negative_margin_start && free_space < Au::zero() {
865 LogicalSides1D::new(Au::zero(), free_space)
866 } else {
867 let margin_start = free_space / 2;
868 LogicalSides1D::new(margin_start, free_space - margin_start)
869 }
870 },
871 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
872 LogicalSides1D::new(free_space - end, end)
873 },
874 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => {
875 LogicalSides1D::new(start, free_space - start)
876 },
877 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
878 LogicalSides1D::new(start, end)
879 },
880 }
881 }
882 }
883
884 fn origin_for_margin_box(
885 &self,
886 size: Au,
887 self_writing_mode: WritingMode,
888 original_parent_writing_mode: WritingMode,
889 containing_block_writing_mode: WritingMode,
890 ) -> Au {
891 let (alignment_container, alignment_container_writing_mode, flip_anchor, offsets) = match (
892 self.box_offsets.start.non_auto(),
893 self.box_offsets.end.non_auto(),
894 ) {
895 (None, None) => (
896 self.static_position_rect_axis,
897 original_parent_writing_mode,
898 self.flip_anchor,
899 None,
900 ),
901 (Some(start), Some(end)) => {
902 let alignment_container = RectAxis {
903 origin: start,
904 length: self.available_space(),
905 };
906 (
907 alignment_container,
908 containing_block_writing_mode,
909 false,
910 Some(LogicalSides1D { start, end }),
911 )
912 },
913 (Some(start), None) => return start,
917 (None, Some(end)) => {
918 return self.containing_size - size - end;
919 },
920 };
921
922 assert_eq!(
923 self_writing_mode.is_horizontal(),
924 original_parent_writing_mode.is_horizontal(),
925 "Mixed horizontal and vertical writing modes are not supported yet"
926 );
927 assert_eq!(
928 self_writing_mode.is_horizontal(),
929 containing_block_writing_mode.is_horizontal(),
930 "Mixed horizontal and vertical writing modes are not supported yet"
931 );
932 let self_value_matches_container = || {
933 self.axis == Direction::Block ||
934 self_writing_mode.is_bidi_ltr() == alignment_container_writing_mode.is_bidi_ltr()
935 };
936
937 let alignment = match self.alignment.value() {
942 AlignFlags::CENTER | AlignFlags::SPACE_AROUND | AlignFlags::SPACE_EVENLY => {
946 AlignFlags::CENTER
947 },
948 AlignFlags::SELF_START if self_value_matches_container() => AlignFlags::START,
950 AlignFlags::SELF_START => AlignFlags::END,
951 AlignFlags::SELF_END if self_value_matches_container() => AlignFlags::END,
953 AlignFlags::SELF_END => AlignFlags::START,
954 AlignFlags::LEFT if alignment_container_writing_mode.is_bidi_ltr() => AlignFlags::START,
956 AlignFlags::LEFT => AlignFlags::END,
957 AlignFlags::RIGHT if alignment_container_writing_mode.is_bidi_ltr() => AlignFlags::END,
959 AlignFlags::RIGHT => AlignFlags::START,
960 AlignFlags::END | AlignFlags::FLEX_END | AlignFlags::LAST_BASELINE => AlignFlags::END,
964 _ => AlignFlags::START,
968 };
969
970 let alignment = match alignment {
971 AlignFlags::START if flip_anchor => AlignFlags::END,
972 AlignFlags::END if flip_anchor => AlignFlags::START,
973 alignment => alignment,
974 };
975
976 let free_space = alignment_container.length - size;
977 let flags = self.alignment.flags();
978 let alignment = if flags == AlignFlags::SAFE && free_space < Au::zero() {
979 AlignFlags::START
980 } else {
981 alignment
982 };
983
984 let origin = match alignment {
985 AlignFlags::START => alignment_container.origin,
986 AlignFlags::CENTER => alignment_container.origin + free_space / 2,
987 AlignFlags::END => alignment_container.origin + free_space,
988 _ => unreachable!(),
989 };
990 if matches!(flags, AlignFlags::SAFE | AlignFlags::UNSAFE) ||
991 matches!(
992 self.alignment,
993 AlignFlags::NORMAL | AlignFlags::AUTO | AlignFlags::STRETCH
994 )
995 {
996 return origin;
997 }
998 let Some(offsets) = offsets else {
999 return origin;
1000 };
1001
1002 let min = Au::zero().min(offsets.start);
1005 let max = self.containing_size - Au::zero().min(offsets.end) - size;
1006 origin.clamp_between_extremums(min, Some(max))
1007 }
1008}
1009
1010pub(crate) fn relative_adjustement(
1012 style: &ComputedValues,
1013 containing_block: &ContainingBlock,
1014) -> LogicalVec2<Au> {
1015 let cbis = containing_block.size.inline;
1019 let cbbs = containing_block.size.block;
1020 let box_offsets = style
1021 .box_offsets(containing_block.style.writing_mode)
1022 .map_inline_and_block_axes(
1023 |value| value.map(|value| value.to_used_value(cbis)),
1024 |value| match cbbs {
1025 SizeConstraint::Definite(cbbs) => value.map(|value| value.to_used_value(cbbs)),
1026 _ => match value.non_auto().and_then(|value| value.to_length()) {
1027 Some(value) => AuOrAuto::LengthPercentage(value.into()),
1028 None => AuOrAuto::Auto,
1029 },
1030 },
1031 );
1032 fn adjust(start: AuOrAuto, end: AuOrAuto) -> Au {
1033 match (start, end) {
1034 (AuOrAuto::Auto, AuOrAuto::Auto) => Au::zero(),
1035 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => -end,
1036 (AuOrAuto::LengthPercentage(start), _) => start,
1037 }
1038 }
1039 LogicalVec2 {
1040 inline: adjust(box_offsets.inline_start, box_offsets.inline_end),
1041 block: adjust(box_offsets.block_start, box_offsets.block_end),
1042 }
1043}
1044
1045#[derive(MallocSizeOf)]
1050pub(crate) struct LayoutRootLayoutInputs {
1051 fully_adjusted_static_position_rect: LogicalRect<Au>,
1055 resolved_alignment: LogicalVec2<AlignFlags>,
1058 containing_block_size: LogicalVec2<Au>,
1061 #[conditional_malloc_size_of]
1064 containing_block_style: ServoArc<ComputedValues>,
1065 original_parent_writing_mode: WritingMode,
1067}
1068
1069impl std::fmt::Debug for LayoutRootLayoutInputs {
1070 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1071 f.debug_struct("LayoutRootLayoutInputs")
1072 .field("containing_block_size", &self.containing_block_size)
1073 .finish()
1074 }
1075}
1076
1077impl LayoutRootLayoutInputs {
1078 pub(crate) fn layout(
1081 &self,
1082 layout_context: &LayoutContext,
1083 context: &IndependentFormattingContext,
1084 shared_fragment: &ArcRefCell<HoistedSharedFragment>,
1085 ) -> Result<(), ()> {
1086 let containing_block = DefiniteContainingBlock {
1087 size: self.containing_block_size,
1088 style: &self.containing_block_style,
1089 };
1090 let (box_fragment, positioning_context) = context.layout_as_absolute(
1091 layout_context,
1092 &self.fully_adjusted_static_position_rect,
1093 &containing_block,
1094 self.resolved_alignment,
1095 self.original_parent_writing_mode,
1096 );
1097
1098 if !positioning_context.is_empty() {
1099 return Err(());
1100 }
1101
1102 shared_fragment.borrow_mut().fragment = Some(Fragment::Box(box_fragment));
1103 Ok(())
1104 }
1105}