1use std::mem;
6
7use app_units::Au;
8use malloc_size_of_derive::MallocSizeOf;
9use rayon::iter::IntoParallelRefMutIterator;
10use rayon::prelude::{IndexedParallelIterator, ParallelIterator};
11use style::Zero;
12use style::computed_values::position::T as Position;
13use style::logical_geometry::{Direction, WritingMode};
14use style::properties::ComputedValues;
15use style::values::specified::align::AlignFlags;
16
17use crate::cell::ArcRefCell;
18use crate::context::LayoutContext;
19use crate::dom_traversal::{Contents, NodeAndStyleInfo};
20use crate::formatting_contexts::IndependentFormattingContext;
21use crate::fragment_tree::{BoxFragment, Fragment, FragmentFlags, HoistedSharedFragment};
22use crate::geom::{
23 AuOrAuto, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect,
24 PhysicalSides, PhysicalSize, PhysicalVec, ToLogical, ToLogicalWithContainingBlock,
25};
26use crate::layout_box_base::{CacheableLayoutResult, LayoutBoxBase};
27use crate::sizing::{LazySize, Size, SizeConstraint, Sizes};
28use crate::style_ext::{Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, DisplayInside};
29use crate::{
30 ConstraintSpace, ContainingBlock, ContainingBlockSize, DefiniteContainingBlock,
31 PropagatedBoxTreeData,
32};
33
34#[derive(Debug, MallocSizeOf)]
35pub(crate) struct AbsolutelyPositionedBox {
36 pub context: IndependentFormattingContext,
37}
38
39#[derive(Clone, MallocSizeOf)]
40pub(crate) struct HoistedAbsolutelyPositionedBox {
41 absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
42 pub fragment: ArcRefCell<HoistedSharedFragment>,
46 pub adjusted_static_position_rect: Option<PhysicalRect<Au>>,
56 pub resolved_alignment: LogicalVec2<AlignFlags>,
61 pub original_parent_writing_mode: WritingMode,
66}
67
68impl AbsolutelyPositionedBox {
69 pub fn new(context: IndependentFormattingContext) -> Self {
70 Self { context }
71 }
72
73 pub fn construct(
74 context: &LayoutContext,
75 node_info: &NodeAndStyleInfo,
76 display_inside: DisplayInside,
77 contents: Contents,
78 ) -> Self {
79 Self {
80 context: IndependentFormattingContext::construct(
81 context,
82 node_info,
83 display_inside,
84 contents,
85 PropagatedBoxTreeData::default(),
89 ),
90 }
91 }
92
93 pub(crate) fn to_hoisted(
94 absolutely_positioned_box: ArcRefCell<Self>,
95 static_position_rect: PhysicalRect<Au>,
96 resolved_alignment: LogicalVec2<AlignFlags>,
97 original_parent_writing_mode: WritingMode,
98 ) -> HoistedAbsolutelyPositionedBox {
99 HoistedAbsolutelyPositionedBox {
100 fragment: ArcRefCell::new(HoistedSharedFragment::new(static_position_rect)),
101 adjusted_static_position_rect: None,
102 resolved_alignment,
103 original_parent_writing_mode,
104 absolutely_positioned_box,
105 }
106 }
107}
108
109#[derive(Clone, Default, MallocSizeOf)]
110pub(crate) struct PositioningContext {
111 absolutes: Vec<HoistedAbsolutelyPositionedBox>,
112}
113
114impl PositioningContext {
115 #[inline]
116 pub(crate) fn new_for_layout_box_base(layout_box_base: &LayoutBoxBase) -> Option<Self> {
117 Self::new_for_style_and_fragment_flags(
118 &layout_box_base.style,
119 &layout_box_base.base_fragment_info.flags,
120 )
121 }
122
123 fn new_for_style_and_fragment_flags(
124 style: &ComputedValues,
125 flags: &FragmentFlags,
126 ) -> Option<Self> {
127 if style.establishes_containing_block_for_absolute_descendants(*flags) {
128 Some(Self::default())
129 } else {
130 None
131 }
132 }
133
134 pub(crate) fn adjust_static_position_of_hoisted_fragments(
147 &mut self,
148 parent_fragment: &Fragment,
149 index: PositioningContextLength,
150 ) {
151 let Some(base) = parent_fragment.base() else {
152 return;
153 };
154 self.adjust_static_position_of_hoisted_fragments_with_offset(
155 &base.rect.origin.to_vector(),
156 index,
157 );
158 }
159
160 pub(crate) fn adjust_static_position_of_hoisted_fragments_with_offset(
162 &mut self,
163 offset: &PhysicalVec<Au>,
164 index: PositioningContextLength,
165 ) {
166 self.absolutes
167 .iter_mut()
168 .skip(index.0)
169 .for_each(|hoisted_box| {
170 hoisted_box.adjust_static_position_with_offset(offset);
171 })
172 }
173
174 pub(crate) fn layout_maybe_position_relative_fragment(
178 &mut self,
179 layout_context: &LayoutContext,
180 containing_block: &ContainingBlock,
181 base: &LayoutBoxBase,
182 fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment,
183 ) -> BoxFragment {
184 let establishes_containing_block_for_absolutes = base
187 .style
188 .establishes_containing_block_for_absolute_descendants(base.base_fragment_info.flags);
189 if !establishes_containing_block_for_absolutes {
190 return fragment_layout_fn(self);
191 }
192
193 let mut new_context = PositioningContext::default();
194 let mut new_fragment = fragment_layout_fn(&mut new_context);
195
196 new_context.layout_collected_children(layout_context, &mut new_fragment);
199 self.append(new_context);
200
201 if base.style.clone_position() == Position::Relative {
202 new_fragment.base.rect.origin += relative_adjustement(&base.style, containing_block)
203 .to_physical_vector(containing_block.style.writing_mode)
204 }
205
206 new_fragment
207 }
208
209 fn take_boxes_for_fragment(
210 &mut self,
211 new_fragment: &BoxFragment,
212 boxes_to_layout_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
213 boxes_to_continue_hoisting_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
214 ) {
215 let style = new_fragment.style();
216 debug_assert!(
217 style.establishes_containing_block_for_absolute_descendants(new_fragment.base.flags)
218 );
219
220 if style.establishes_containing_block_for_all_descendants(new_fragment.base.flags) {
221 boxes_to_layout_out.append(&mut self.absolutes);
222 return;
223 }
224
225 let (mut boxes_to_layout, mut boxes_to_continue_hoisting) = self
227 .absolutes
228 .drain(..)
229 .partition(|hoisted_box| hoisted_box.position() != Position::Fixed);
230 boxes_to_layout_out.append(&mut boxes_to_layout);
231 boxes_to_continue_hoisting_out.append(&mut boxes_to_continue_hoisting);
232 }
233
234 pub(crate) fn layout_collected_children(
237 &mut self,
238 layout_context: &LayoutContext,
239 new_fragment: &mut BoxFragment,
240 ) {
241 if self.absolutes.is_empty() {
242 return;
243 }
244
245 let style = new_fragment.style().clone();
254 if !style.establishes_containing_block_for_absolute_descendants(new_fragment.base.flags) {
255 return;
256 }
257
258 let padding_rect = PhysicalRect::new(
259 PhysicalPoint::origin(),
261 new_fragment.base.rect.size,
262 )
263 .outer_rect(new_fragment.padding);
264 let containing_block = DefiniteContainingBlock {
265 size: padding_rect.size.to_logical(style.writing_mode),
266 style: &style,
267 };
268
269 let mut fixed_position_boxes_to_hoist = Vec::new();
270 let mut boxes_to_layout = Vec::new();
271 self.take_boxes_for_fragment(
272 new_fragment,
273 &mut boxes_to_layout,
274 &mut fixed_position_boxes_to_hoist,
275 );
276
277 while !boxes_to_layout.is_empty() {
283 HoistedAbsolutelyPositionedBox::layout_many(
284 layout_context,
285 std::mem::take(&mut boxes_to_layout),
286 &mut new_fragment.children,
287 &mut self.absolutes,
288 &containing_block,
289 new_fragment.padding,
290 );
291
292 self.take_boxes_for_fragment(
293 new_fragment,
294 &mut boxes_to_layout,
295 &mut fixed_position_boxes_to_hoist,
296 );
297 }
298
299 self.absolutes = fixed_position_boxes_to_hoist;
303 }
304
305 pub(crate) fn push(&mut self, hoisted_box: HoistedAbsolutelyPositionedBox) {
306 debug_assert!(hoisted_box.position().is_absolutely_positioned());
307 self.absolutes.push(hoisted_box);
308 }
309
310 pub(crate) fn append(&mut self, mut other: Self) {
311 if other.absolutes.is_empty() {
312 return;
313 }
314 if self.absolutes.is_empty() {
315 self.absolutes = other.absolutes;
316 } else {
317 self.absolutes.append(&mut other.absolutes)
318 }
319 }
320
321 pub(crate) fn layout_initial_containing_block_children(
322 &mut self,
323 layout_context: &LayoutContext,
324 initial_containing_block: &DefiniteContainingBlock,
325 fragments: &mut Vec<Fragment>,
326 ) {
327 while !self.absolutes.is_empty() {
332 HoistedAbsolutelyPositionedBox::layout_many(
333 layout_context,
334 mem::take(&mut self.absolutes),
335 fragments,
336 &mut self.absolutes,
337 initial_containing_block,
338 Default::default(),
339 )
340 }
341 }
342
343 pub(crate) fn len(&self) -> PositioningContextLength {
345 PositioningContextLength(self.absolutes.len())
346 }
347
348 pub(crate) fn truncate(&mut self, length: &PositioningContextLength) {
352 self.absolutes.truncate(length.0)
353 }
354}
355
356#[derive(Clone, Copy, Debug, PartialEq)]
358pub(crate) struct PositioningContextLength(usize);
359
360impl Zero for PositioningContextLength {
361 fn zero() -> Self {
362 Self(0)
363 }
364
365 fn is_zero(&self) -> bool {
366 self.0.is_zero()
367 }
368}
369
370impl HoistedAbsolutelyPositionedBox {
371 fn position(&self) -> Position {
372 let position = self
373 .absolutely_positioned_box
374 .borrow()
375 .context
376 .style()
377 .clone_position();
378 assert!(position.is_absolutely_positioned());
379 position
380 }
381
382 pub(crate) fn layout_many(
383 layout_context: &LayoutContext,
384 mut boxes: Vec<Self>,
385 fragments: &mut Vec<Fragment>,
386 for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>,
387 containing_block: &DefiniteContainingBlock,
388 containing_block_padding: PhysicalSides<Au>,
389 ) {
390 if layout_context.use_rayon {
391 let mut new_fragments = Vec::new();
392 let mut new_hoisted_boxes = Vec::new();
393
394 boxes
395 .par_iter_mut()
396 .map(|hoisted_box| {
397 let mut new_hoisted_boxes: Vec<HoistedAbsolutelyPositionedBox> = Vec::new();
398 let new_fragment = hoisted_box.layout(
399 layout_context,
400 &mut new_hoisted_boxes,
401 containing_block,
402 containing_block_padding,
403 );
404
405 hoisted_box.fragment.borrow_mut().fragment = Some(new_fragment.clone());
406 (new_fragment, new_hoisted_boxes)
407 })
408 .unzip_into_vecs(&mut new_fragments, &mut new_hoisted_boxes);
409
410 fragments.extend(new_fragments);
411 for_nearest_containing_block_for_all_descendants
412 .extend(new_hoisted_boxes.into_iter().flatten());
413 } else {
414 fragments.extend(boxes.iter_mut().map(|box_| {
415 let new_fragment = box_.layout(
416 layout_context,
417 for_nearest_containing_block_for_all_descendants,
418 containing_block,
419 containing_block_padding,
420 );
421
422 box_.fragment.borrow_mut().fragment = Some(new_fragment.clone());
423 new_fragment
424 }))
425 }
426 }
427
428 pub(crate) fn layout(
429 &mut self,
430 layout_context: &LayoutContext,
431 hoisted_absolutes_from_children: &mut Vec<HoistedAbsolutelyPositionedBox>,
432 containing_block: &DefiniteContainingBlock,
433 containing_block_padding: PhysicalSides<Au>,
434 ) -> Fragment {
435 let cbis = containing_block.size.inline;
436 let cbbs = containing_block.size.block;
437 let containing_block_writing_mode = containing_block.style.writing_mode;
438 let absolutely_positioned_box = self.absolutely_positioned_box.borrow();
439 let context = &absolutely_positioned_box.context;
440 let style = context.style().clone();
441 let layout_style = context.layout_style();
442 let ContentBoxSizesAndPBM {
443 content_box_sizes,
444 pbm,
445 ..
446 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
447 let containing_block = &containing_block.into();
448 let is_table = layout_style.is_table();
449 let is_table_or_replaced = is_table || context.is_replaced();
450 let preferred_aspect_ratio = context.preferred_aspect_ratio(&pbm.padding_border_sums);
451
452 let mut static_position_rect = self
456 .static_position_rect()
457 .outer_rect(-containing_block_padding);
458 static_position_rect.size = static_position_rect.size.max(PhysicalSize::zero());
459 let static_position_rect = static_position_rect.to_logical(containing_block);
460
461 let box_offset = style.box_offsets(containing_block.style.writing_mode);
462
463 let inline_box_offsets = box_offset.inline_sides().percentages_relative_to(cbis);
466 let inline_alignment = match inline_box_offsets.either_specified() {
467 true => style.clone_justify_self().0,
468 false => self.resolved_alignment.inline,
469 };
470
471 let inline_axis_solver = AbsoluteAxisSolver {
472 axis: Direction::Inline,
473 containing_size: cbis,
474 padding_border_sum: pbm.padding_border_sums.inline,
475 computed_margin_start: pbm.margin.inline_start,
476 computed_margin_end: pbm.margin.inline_end,
477 computed_sizes: content_box_sizes.inline,
478 avoid_negative_margin_start: true,
479 box_offsets: inline_box_offsets,
480 static_position_rect_axis: static_position_rect.get_axis(Direction::Inline),
481 alignment: inline_alignment,
482 flip_anchor: self.original_parent_writing_mode.is_bidi_ltr() !=
483 containing_block_writing_mode.is_bidi_ltr(),
484 is_table_or_replaced,
485 };
486
487 let block_box_offsets = box_offset.block_sides().percentages_relative_to(cbbs);
490 let block_alignment = match block_box_offsets.either_specified() {
491 true => style.clone_align_self().0,
492 false => self.resolved_alignment.block,
493 };
494 let block_axis_solver = AbsoluteAxisSolver {
495 axis: Direction::Block,
496 containing_size: cbbs,
497 padding_border_sum: pbm.padding_border_sums.block,
498 computed_margin_start: pbm.margin.block_start,
499 computed_margin_end: pbm.margin.block_end,
500 computed_sizes: content_box_sizes.block,
501 avoid_negative_margin_start: false,
502 box_offsets: block_box_offsets,
503 static_position_rect_axis: static_position_rect.get_axis(Direction::Block),
504 alignment: block_alignment,
505 flip_anchor: false,
506 is_table_or_replaced,
507 };
508
509 let block_automatic_size = block_axis_solver.automatic_size();
512 let block_stretch_size = Some(block_axis_solver.stretch_size());
513 let tentative_block_content_size =
514 context.tentative_block_content_size(preferred_aspect_ratio);
515 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
516 SizeConstraint::Definite(block_axis_solver.computed_sizes.resolve(
517 Direction::Block,
518 block_automatic_size,
519 Au::zero,
520 block_stretch_size,
521 || block_content_size,
522 is_table,
523 ))
524 } else {
525 block_axis_solver.computed_sizes.resolve_extrinsic(
526 block_automatic_size,
527 Au::zero(),
528 block_stretch_size,
529 )
530 };
531
532 let get_inline_content_size = || {
535 let constraint_space =
536 ConstraintSpace::new(tentative_block_size, &style, preferred_aspect_ratio);
537 context
538 .inline_content_sizes(layout_context, &constraint_space)
539 .sizes
540 };
541 let inline_size = inline_axis_solver.computed_sizes.resolve(
542 Direction::Inline,
543 inline_axis_solver.automatic_size(),
544 Au::zero,
545 Some(inline_axis_solver.stretch_size()),
546 get_inline_content_size,
547 is_table,
548 );
549
550 let containing_block_for_children = ContainingBlock {
551 size: ContainingBlockSize {
552 inline: inline_size,
553 block: tentative_block_size,
554 },
555 style: &style,
556 };
557 assert_eq!(
559 containing_block_writing_mode.is_horizontal(),
560 style.writing_mode.is_horizontal(),
561 "Mixed horizontal and vertical writing modes are not supported yet"
562 );
563
564 let mut positioning_context = PositioningContext::default();
565 let lazy_block_size = LazySize::new(
566 &block_axis_solver.computed_sizes,
567 Direction::Block,
568 block_automatic_size,
569 Au::zero,
570 block_stretch_size,
571 is_table,
572 );
573 let CacheableLayoutResult {
574 content_inline_size_for_table,
575 content_block_size,
576 fragments,
577 specific_layout_info,
578 ..
579 } = context.layout(
580 layout_context,
581 &mut positioning_context,
582 &containing_block_for_children,
583 containing_block,
584 preferred_aspect_ratio,
585 &lazy_block_size,
586 );
587
588 let content_size = LogicalVec2 {
589 inline: content_inline_size_for_table.unwrap_or(inline_size),
591
592 block: lazy_block_size.resolve(|| content_block_size),
594 };
595
596 let inline_margins = inline_axis_solver.solve_margins(content_size.inline);
597 let block_margins = block_axis_solver.solve_margins(content_size.block);
598 let margin = LogicalSides {
599 inline_start: inline_margins.start,
600 inline_end: inline_margins.end,
601 block_start: block_margins.start,
602 block_end: block_margins.end,
603 };
604
605 let pb = pbm.padding + pbm.border;
606 let margin_rect_size = content_size + pbm.padding_border_sums + margin.sum();
607 let inline_origin = inline_axis_solver.origin_for_margin_box(
608 margin_rect_size.inline,
609 style.writing_mode,
610 self.original_parent_writing_mode,
611 containing_block_writing_mode,
612 );
613 let block_origin = block_axis_solver.origin_for_margin_box(
614 margin_rect_size.block,
615 style.writing_mode,
616 self.original_parent_writing_mode,
617 containing_block_writing_mode,
618 );
619
620 let content_rect = LogicalRect {
621 start_corner: LogicalVec2 {
622 inline: inline_origin + margin.inline_start + pb.inline_start,
623 block: block_origin + margin.block_start + pb.block_start,
624 },
625 size: content_size,
626 };
627 let mut new_fragment = BoxFragment::new(
628 context.base_fragment_info(),
629 style,
630 fragments,
631 content_rect.as_physical(Some(containing_block)),
632 pbm.padding.to_physical(containing_block_writing_mode),
633 pbm.border.to_physical(containing_block_writing_mode),
634 margin.to_physical(containing_block_writing_mode),
635 specific_layout_info,
636 );
637
638 positioning_context.layout_collected_children(layout_context, &mut new_fragment);
642
643 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
649 &new_fragment.base.rect.origin.to_vector(),
650 PositioningContextLength::zero(),
651 );
652
653 hoisted_absolutes_from_children.extend(positioning_context.absolutes);
654
655 let fragment = Fragment::Box(ArcRefCell::new(new_fragment));
656 context.base.set_fragment(fragment.clone());
657 fragment
658 }
659
660 fn static_position_rect(&self) -> PhysicalRect<Au> {
661 self.adjusted_static_position_rect
662 .unwrap_or_else(|| self.fragment.borrow().original_static_position_rect)
663 }
664
665 fn adjust_static_position_with_offset(&mut self, offset: &PhysicalVec<Au>) {
666 self.adjusted_static_position_rect = Some(self.static_position_rect().translate(*offset));
667 }
668}
669
670#[derive(Clone, Copy, Debug)]
671struct RectAxis {
672 origin: Au,
673 length: Au,
674}
675
676impl LogicalRect<Au> {
677 fn get_axis(&self, axis: Direction) -> RectAxis {
678 match axis {
679 Direction::Block => RectAxis {
680 origin: self.start_corner.block,
681 length: self.size.block,
682 },
683 Direction::Inline => RectAxis {
684 origin: self.start_corner.inline,
685 length: self.size.inline,
686 },
687 }
688 }
689}
690
691struct AbsoluteAxisSolver {
692 axis: Direction,
693 containing_size: Au,
694 padding_border_sum: Au,
695 computed_margin_start: AuOrAuto,
696 computed_margin_end: AuOrAuto,
697 computed_sizes: Sizes,
698 avoid_negative_margin_start: bool,
699 box_offsets: LogicalSides1D<AuOrAuto>,
700 static_position_rect_axis: RectAxis,
701 alignment: AlignFlags,
702 flip_anchor: bool,
703 is_table_or_replaced: bool,
704}
705
706impl AbsoluteAxisSolver {
707 fn inset_sum(&self) -> Au {
712 match (
713 self.box_offsets.start.non_auto(),
714 self.box_offsets.end.non_auto(),
715 ) {
716 (None, None) => {
717 if self.flip_anchor {
718 self.containing_size -
719 self.static_position_rect_axis.origin -
720 self.static_position_rect_axis.length
721 } else {
722 self.static_position_rect_axis.origin
723 }
724 },
725 (Some(start), None) => start,
726 (None, Some(end)) => end,
727 (Some(start), Some(end)) => start + end,
728 }
729 }
730
731 #[inline]
734 fn available_space(&self) -> Au {
735 Au::zero().max(self.containing_size - self.inset_sum())
736 }
737
738 #[inline]
739 fn automatic_size(&self) -> Size<Au> {
740 match self.alignment.value() {
741 _ if self.box_offsets.either_auto() => Size::FitContent,
742 AlignFlags::NORMAL | AlignFlags::AUTO if !self.is_table_or_replaced => Size::Stretch,
743 AlignFlags::STRETCH => Size::Stretch,
744 _ => Size::FitContent,
745 }
746 }
747
748 #[inline]
749 fn stretch_size(&self) -> Au {
750 Au::zero().max(
751 self.available_space() -
752 self.padding_border_sum -
753 self.computed_margin_start.auto_is(Au::zero) -
754 self.computed_margin_end.auto_is(Au::zero),
755 )
756 }
757
758 fn solve_margins(&self, size: Au) -> LogicalSides1D<Au> {
759 if self.box_offsets.either_auto() {
760 LogicalSides1D::new(
761 self.computed_margin_start.auto_is(Au::zero),
762 self.computed_margin_end.auto_is(Au::zero),
763 )
764 } else {
765 let free_space = self.available_space() - self.padding_border_sum - size;
766 match (self.computed_margin_start, self.computed_margin_end) {
767 (AuOrAuto::Auto, AuOrAuto::Auto) => {
768 if self.avoid_negative_margin_start && free_space < Au::zero() {
769 LogicalSides1D::new(Au::zero(), free_space)
770 } else {
771 let margin_start = free_space / 2;
772 LogicalSides1D::new(margin_start, free_space - margin_start)
773 }
774 },
775 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
776 LogicalSides1D::new(free_space - end, end)
777 },
778 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => {
779 LogicalSides1D::new(start, free_space - start)
780 },
781 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
782 LogicalSides1D::new(start, end)
783 },
784 }
785 }
786 }
787
788 fn origin_for_margin_box(
789 &self,
790 size: Au,
791 self_writing_mode: WritingMode,
792 original_parent_writing_mode: WritingMode,
793 containing_block_writing_mode: WritingMode,
794 ) -> Au {
795 let (alignment_container, alignment_container_writing_mode, flip_anchor, offsets) = match (
796 self.box_offsets.start.non_auto(),
797 self.box_offsets.end.non_auto(),
798 ) {
799 (None, None) => (
800 self.static_position_rect_axis,
801 original_parent_writing_mode,
802 self.flip_anchor,
803 None,
804 ),
805 (Some(start), Some(end)) => {
806 let alignment_container = RectAxis {
807 origin: start,
808 length: self.available_space(),
809 };
810 (
811 alignment_container,
812 containing_block_writing_mode,
813 false,
814 Some(LogicalSides1D { start, end }),
815 )
816 },
817 (Some(start), None) => return start,
821 (None, Some(end)) => {
822 return self.containing_size - size - end;
823 },
824 };
825
826 assert_eq!(
827 self_writing_mode.is_horizontal(),
828 original_parent_writing_mode.is_horizontal(),
829 "Mixed horizontal and vertical writing modes are not supported yet"
830 );
831 assert_eq!(
832 self_writing_mode.is_horizontal(),
833 containing_block_writing_mode.is_horizontal(),
834 "Mixed horizontal and vertical writing modes are not supported yet"
835 );
836 let self_value_matches_container = || {
837 self.axis == Direction::Block ||
838 self_writing_mode.is_bidi_ltr() == alignment_container_writing_mode.is_bidi_ltr()
839 };
840
841 let alignment = match self.alignment.value() {
846 AlignFlags::CENTER | AlignFlags::SPACE_AROUND | AlignFlags::SPACE_EVENLY => {
850 AlignFlags::CENTER
851 },
852 AlignFlags::SELF_START if self_value_matches_container() => AlignFlags::START,
854 AlignFlags::SELF_START => AlignFlags::END,
855 AlignFlags::SELF_END if self_value_matches_container() => AlignFlags::END,
857 AlignFlags::SELF_END => AlignFlags::START,
858 AlignFlags::LEFT if alignment_container_writing_mode.is_bidi_ltr() => AlignFlags::START,
860 AlignFlags::LEFT => AlignFlags::END,
861 AlignFlags::RIGHT if alignment_container_writing_mode.is_bidi_ltr() => AlignFlags::END,
863 AlignFlags::RIGHT => AlignFlags::START,
864 AlignFlags::END | AlignFlags::FLEX_END => AlignFlags::END,
867 _ => AlignFlags::START,
870 };
871
872 let alignment = match alignment {
873 AlignFlags::START if flip_anchor => AlignFlags::END,
874 AlignFlags::END if flip_anchor => AlignFlags::START,
875 alignment => alignment,
876 };
877
878 let free_space = alignment_container.length - size;
879 let flags = self.alignment.flags();
880 let alignment = if flags == AlignFlags::SAFE && free_space < Au::zero() {
881 AlignFlags::START
882 } else {
883 alignment
884 };
885
886 let origin = match alignment {
887 AlignFlags::START => alignment_container.origin,
888 AlignFlags::CENTER => alignment_container.origin + free_space / 2,
889 AlignFlags::END => alignment_container.origin + free_space,
890 _ => unreachable!(),
891 };
892 if matches!(flags, AlignFlags::SAFE | AlignFlags::UNSAFE) ||
893 matches!(
894 self.alignment,
895 AlignFlags::NORMAL | AlignFlags::AUTO | AlignFlags::STRETCH
896 )
897 {
898 return origin;
899 }
900 let Some(offsets) = offsets else {
901 return origin;
902 };
903
904 let min = Au::zero().min(offsets.start);
907 let max = self.containing_size - Au::zero().min(offsets.end) - size;
908 origin.clamp_between_extremums(min, Some(max))
909 }
910}
911
912pub(crate) fn relative_adjustement(
914 style: &ComputedValues,
915 containing_block: &ContainingBlock,
916) -> LogicalVec2<Au> {
917 let cbis = containing_block.size.inline;
921 let cbbs = containing_block.size.block;
922 let box_offsets = style
923 .box_offsets(containing_block.style.writing_mode)
924 .map_inline_and_block_axes(
925 |value| value.map(|value| value.to_used_value(cbis)),
926 |value| match cbbs {
927 SizeConstraint::Definite(cbbs) => value.map(|value| value.to_used_value(cbbs)),
928 _ => match value.non_auto().and_then(|value| value.to_length()) {
929 Some(value) => AuOrAuto::LengthPercentage(value.into()),
930 None => AuOrAuto::Auto,
931 },
932 },
933 );
934 fn adjust(start: AuOrAuto, end: AuOrAuto) -> Au {
935 match (start, end) {
936 (AuOrAuto::Auto, AuOrAuto::Auto) => Au::zero(),
937 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => -end,
938 (AuOrAuto::LengthPercentage(start), _) => start,
939 }
940 }
941 LogicalVec2 {
942 inline: adjust(box_offsets.inline_start, box_offsets.inline_end),
943 block: adjust(box_offsets.block_start, box_offsets.block_end),
944 }
945}