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::{IndependentFormattingContextLayoutResult, 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.translate_rect(
203 relative_adjustement(&base.style, containing_block)
204 .to_physical_vector(containing_block.style.writing_mode)
205 .into(),
206 );
207 }
208
209 new_fragment
210 }
211
212 fn forget_unhoisted_boxes(&mut self, fragment: &BoxFragment) {
213 let style = fragment.style();
214 debug_assert!(
215 style.establishes_containing_block_for_absolute_descendants(fragment.base.flags)
216 );
217 if style.establishes_containing_block_for_all_descendants(fragment.base.flags) {
218 self.absolutes.clear();
219 } else {
220 self.absolutes
221 .retain(|hoisted_box| hoisted_box.position() == Position::Fixed);
222 }
223 }
224
225 fn take_boxes_for_fragment(
226 &mut self,
227 new_fragment: &BoxFragment,
228 boxes_to_layout_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
229 boxes_to_continue_hoisting_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
230 ) {
231 let style = new_fragment.style();
232 debug_assert!(
233 style.establishes_containing_block_for_absolute_descendants(new_fragment.base.flags)
234 );
235
236 if style.establishes_containing_block_for_all_descendants(new_fragment.base.flags) {
237 boxes_to_layout_out.append(&mut self.absolutes);
238 return;
239 }
240
241 let (mut boxes_to_layout, mut boxes_to_continue_hoisting) = self
243 .absolutes
244 .drain(..)
245 .partition(|hoisted_box| hoisted_box.position() != Position::Fixed);
246 boxes_to_layout_out.append(&mut boxes_to_layout);
247 boxes_to_continue_hoisting_out.append(&mut boxes_to_continue_hoisting);
248 }
249
250 pub(crate) fn layout_collected_children(
253 &mut self,
254 layout_context: &LayoutContext,
255 new_fragment: &mut BoxFragment,
256 ) {
257 if self.absolutes.is_empty() {
258 return;
259 }
260
261 let style = new_fragment.style().clone();
270 if !style.establishes_containing_block_for_absolute_descendants(new_fragment.base.flags) {
271 return;
272 }
273
274 let padding_rect = PhysicalRect::new(
275 PhysicalPoint::origin(),
277 new_fragment.base.rect().size,
278 )
279 .outer_rect(new_fragment.padding);
280 let containing_block = DefiniteContainingBlock {
281 size: padding_rect.size.to_logical(style.writing_mode),
282 style: &style,
283 };
284
285 let mut fixed_position_boxes_to_hoist = Vec::new();
286 let mut boxes_to_layout = Vec::new();
287 self.take_boxes_for_fragment(
288 new_fragment,
289 &mut boxes_to_layout,
290 &mut fixed_position_boxes_to_hoist,
291 );
292
293 while !boxes_to_layout.is_empty() {
299 HoistedAbsolutelyPositionedBox::layout_many(
300 layout_context,
301 std::mem::take(&mut boxes_to_layout),
302 &mut new_fragment.children,
303 &mut self.absolutes,
304 &containing_block,
305 new_fragment.padding,
306 );
307
308 self.take_boxes_for_fragment(
309 new_fragment,
310 &mut boxes_to_layout,
311 &mut fixed_position_boxes_to_hoist,
312 );
313 }
314
315 self.absolutes = fixed_position_boxes_to_hoist;
319 }
320
321 pub(crate) fn push(&mut self, hoisted_box: HoistedAbsolutelyPositionedBox) {
322 debug_assert!(hoisted_box.position().is_absolutely_positioned());
323 self.absolutes.push(hoisted_box);
324 }
325
326 pub(crate) fn append(&mut self, mut other: Self) {
327 if other.absolutes.is_empty() {
328 return;
329 }
330 if self.absolutes.is_empty() {
331 self.absolutes = other.absolutes;
332 } else {
333 self.absolutes.append(&mut other.absolutes)
334 }
335 }
336
337 pub(crate) fn layout_initial_containing_block_children(
338 &mut self,
339 layout_context: &LayoutContext,
340 initial_containing_block: &DefiniteContainingBlock,
341 fragments: &mut Vec<Fragment>,
342 ) {
343 while !self.absolutes.is_empty() {
348 HoistedAbsolutelyPositionedBox::layout_many(
349 layout_context,
350 mem::take(&mut self.absolutes),
351 fragments,
352 &mut self.absolutes,
353 initial_containing_block,
354 Default::default(),
355 )
356 }
357 }
358
359 pub(crate) fn len(&self) -> PositioningContextLength {
361 PositioningContextLength(self.absolutes.len())
362 }
363
364 pub(crate) fn truncate(&mut self, length: &PositioningContextLength) {
368 self.absolutes.truncate(length.0)
369 }
370}
371
372#[derive(Clone, Copy, Debug, PartialEq)]
374pub(crate) struct PositioningContextLength(usize);
375
376impl Zero for PositioningContextLength {
377 fn zero() -> Self {
378 Self(0)
379 }
380
381 fn is_zero(&self) -> bool {
382 self.0.is_zero()
383 }
384}
385
386impl HoistedAbsolutelyPositionedBox {
387 fn position(&self) -> Position {
388 let position = self
389 .absolutely_positioned_box
390 .borrow()
391 .context
392 .style()
393 .clone_position();
394 assert!(position.is_absolutely_positioned());
395 position
396 }
397
398 pub(crate) fn layout_many(
399 layout_context: &LayoutContext,
400 mut boxes: Vec<Self>,
401 fragments: &mut Vec<Fragment>,
402 for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>,
403 containing_block: &DefiniteContainingBlock,
404 containing_block_padding: PhysicalSides<Au>,
405 ) {
406 if layout_context.use_rayon {
407 let mut new_fragments = Vec::new();
408 let mut new_hoisted_boxes = Vec::new();
409
410 boxes
411 .par_iter_mut()
412 .map(|hoisted_box| {
413 let mut new_hoisted_boxes: Vec<HoistedAbsolutelyPositionedBox> = Vec::new();
414 let new_fragment = hoisted_box.layout(
415 layout_context,
416 &mut new_hoisted_boxes,
417 containing_block,
418 containing_block_padding,
419 );
420
421 hoisted_box.fragment.borrow_mut().fragment = Some(new_fragment.clone());
422 (new_fragment, new_hoisted_boxes)
423 })
424 .unzip_into_vecs(&mut new_fragments, &mut new_hoisted_boxes);
425
426 fragments.extend(new_fragments);
427 for_nearest_containing_block_for_all_descendants
428 .extend(new_hoisted_boxes.into_iter().flatten());
429 } else {
430 fragments.extend(boxes.iter_mut().map(|box_| {
431 let new_fragment = box_.layout(
432 layout_context,
433 for_nearest_containing_block_for_all_descendants,
434 containing_block,
435 containing_block_padding,
436 );
437
438 box_.fragment.borrow_mut().fragment = Some(new_fragment.clone());
439 new_fragment
440 }))
441 }
442 }
443
444 pub(crate) fn layout(
445 &mut self,
446 layout_context: &LayoutContext,
447 hoisted_absolutes_from_children: &mut Vec<HoistedAbsolutelyPositionedBox>,
448 containing_block: &DefiniteContainingBlock,
449 containing_block_padding: PhysicalSides<Au>,
450 ) -> Fragment {
451 let cbis = containing_block.size.inline;
452 let cbbs = containing_block.size.block;
453 let containing_block_writing_mode = containing_block.style.writing_mode;
454 let absolutely_positioned_box = self.absolutely_positioned_box.borrow();
455 let context = &absolutely_positioned_box.context;
456 let style = context.style().clone();
457 let layout_style = context.layout_style();
458 let ContentBoxSizesAndPBM {
459 content_box_sizes,
460 pbm,
461 ..
462 } = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
463 let containing_block = &containing_block.into();
464 let is_table = layout_style.is_table();
465 let is_table_or_replaced = is_table || context.is_replaced();
466 let preferred_aspect_ratio = context.preferred_aspect_ratio(&pbm.padding_border_sums);
467
468 let mut static_position_rect = self
472 .static_position_rect()
473 .outer_rect(-containing_block_padding);
474 static_position_rect.size = static_position_rect.size.max(PhysicalSize::zero());
475 let static_position_rect = static_position_rect.to_logical(containing_block);
476
477 let box_offset = style.box_offsets(containing_block.style.writing_mode);
478
479 let inline_box_offsets = box_offset.inline_sides().percentages_relative_to(cbis);
482 let inline_alignment = match inline_box_offsets.either_specified() {
483 true => style.clone_justify_self().0,
484 false => self.resolved_alignment.inline,
485 };
486
487 let inline_axis_solver = AbsoluteAxisSolver {
488 axis: Direction::Inline,
489 containing_size: cbis,
490 padding_border_sum: pbm.padding_border_sums.inline,
491 computed_margin_start: pbm.margin.inline_start,
492 computed_margin_end: pbm.margin.inline_end,
493 computed_sizes: content_box_sizes.inline,
494 avoid_negative_margin_start: true,
495 box_offsets: inline_box_offsets,
496 static_position_rect_axis: static_position_rect.get_axis(Direction::Inline),
497 alignment: inline_alignment,
498 flip_anchor: self.original_parent_writing_mode.is_bidi_ltr() !=
499 containing_block_writing_mode.is_bidi_ltr(),
500 is_table_or_replaced,
501 };
502
503 let block_box_offsets = box_offset.block_sides().percentages_relative_to(cbbs);
506 let block_alignment = match block_box_offsets.either_specified() {
507 true => style.clone_align_self().0,
508 false => self.resolved_alignment.block,
509 };
510 let block_axis_solver = AbsoluteAxisSolver {
511 axis: Direction::Block,
512 containing_size: cbbs,
513 padding_border_sum: pbm.padding_border_sums.block,
514 computed_margin_start: pbm.margin.block_start,
515 computed_margin_end: pbm.margin.block_end,
516 computed_sizes: content_box_sizes.block,
517 avoid_negative_margin_start: false,
518 box_offsets: block_box_offsets,
519 static_position_rect_axis: static_position_rect.get_axis(Direction::Block),
520 alignment: block_alignment,
521 flip_anchor: false,
522 is_table_or_replaced,
523 };
524
525 let block_automatic_size = block_axis_solver.automatic_size();
528 let block_stretch_size = Some(block_axis_solver.stretch_size());
529 let inline_stretch_size = inline_axis_solver.stretch_size();
530 let tentative_block_content_size =
531 context.tentative_block_content_size(preferred_aspect_ratio, inline_stretch_size);
532 let tentative_block_size = if let Some(block_content_size) = tentative_block_content_size {
533 SizeConstraint::Definite(block_axis_solver.computed_sizes.resolve(
534 Direction::Block,
535 block_automatic_size,
536 Au::zero,
537 block_stretch_size,
538 || block_content_size,
539 is_table,
540 ))
541 } else {
542 block_axis_solver.computed_sizes.resolve_extrinsic(
543 block_automatic_size,
544 Au::zero(),
545 block_stretch_size,
546 )
547 };
548
549 let get_inline_content_size = || {
552 let constraint_space =
553 ConstraintSpace::new(tentative_block_size, &style, preferred_aspect_ratio);
554 context
555 .inline_content_sizes(layout_context, &constraint_space)
556 .sizes
557 };
558 let inline_size = inline_axis_solver.computed_sizes.resolve(
559 Direction::Inline,
560 inline_axis_solver.automatic_size(),
561 Au::zero,
562 Some(inline_stretch_size),
563 get_inline_content_size,
564 is_table,
565 );
566
567 let containing_block_for_children = ContainingBlock {
568 size: ContainingBlockSize {
569 inline: inline_size,
570 block: tentative_block_size,
571 },
572 style: &style,
573 };
574 assert_eq!(
576 containing_block_writing_mode.is_horizontal(),
577 style.writing_mode.is_horizontal(),
578 "Mixed horizontal and vertical writing modes are not supported yet"
579 );
580
581 let mut positioning_context = PositioningContext::default();
582 let lazy_block_size = LazySize::new(
583 &block_axis_solver.computed_sizes,
584 Direction::Block,
585 block_automatic_size,
586 Au::zero,
587 block_stretch_size,
588 is_table,
589 );
590 let (layout, is_cached) = context.layout_and_is_cached(
591 layout_context,
592 &mut positioning_context,
593 &containing_block_for_children,
594 containing_block,
595 preferred_aspect_ratio,
596 &lazy_block_size,
597 );
598 let IndependentFormattingContextLayoutResult {
599 content_inline_size_for_table,
600 content_block_size,
601 fragments,
602 specific_layout_info,
603 ..
604 } = layout;
605
606 let content_size = LogicalVec2 {
607 inline: content_inline_size_for_table.unwrap_or(inline_size),
609
610 block: lazy_block_size.resolve(|| content_block_size),
612 };
613
614 let inline_margins = inline_axis_solver.solve_margins(content_size.inline);
615 let block_margins = block_axis_solver.solve_margins(content_size.block);
616 let margin = LogicalSides {
617 inline_start: inline_margins.start,
618 inline_end: inline_margins.end,
619 block_start: block_margins.start,
620 block_end: block_margins.end,
621 };
622
623 let pb = pbm.padding + pbm.border;
624 let margin_rect_size = content_size + pbm.padding_border_sums + margin.sum();
625 let inline_origin = inline_axis_solver.origin_for_margin_box(
626 margin_rect_size.inline,
627 style.writing_mode,
628 self.original_parent_writing_mode,
629 containing_block_writing_mode,
630 );
631 let block_origin = block_axis_solver.origin_for_margin_box(
632 margin_rect_size.block,
633 style.writing_mode,
634 self.original_parent_writing_mode,
635 containing_block_writing_mode,
636 );
637 let content_rect = LogicalRect {
638 start_corner: LogicalVec2 {
639 inline: inline_origin + margin.inline_start + pb.inline_start,
640 block: block_origin + margin.block_start + pb.block_start,
641 },
642 size: content_size,
643 }
644 .as_physical(Some(containing_block));
645
646 let mut adjust_hoisted_boxes = |mut positioning_context: PositioningContext| {
647 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
653 &content_rect.origin.to_vector(),
654 PositioningContextLength::zero(),
655 );
656
657 hoisted_absolutes_from_children.extend(positioning_context.absolutes);
658 };
659
660 if is_cached &&
661 let Some(Fragment::Box(old_fragment)) = context.base.fragments().first() &&
662 content_rect == old_fragment.content_rect()
663 {
664 positioning_context.forget_unhoisted_boxes(old_fragment);
667 adjust_hoisted_boxes(positioning_context);
668 return Fragment::Box(old_fragment.clone());
669 }
670
671 let mut new_fragment = BoxFragment::new(
672 context.base_fragment_info(),
673 style,
674 fragments,
675 content_rect,
676 pbm.padding.to_physical(containing_block_writing_mode),
677 pbm.border.to_physical(containing_block_writing_mode),
678 margin.to_physical(containing_block_writing_mode),
679 specific_layout_info,
680 );
681
682 positioning_context.layout_collected_children(layout_context, &mut new_fragment);
686
687 adjust_hoisted_boxes(positioning_context);
688
689 let fragment = Fragment::Box(new_fragment.into());
690 context.base.set_fragment(fragment.clone());
691 fragment
692 }
693
694 fn static_position_rect(&self) -> PhysicalRect<Au> {
695 self.adjusted_static_position_rect
696 .unwrap_or_else(|| self.fragment.borrow().original_static_position_rect)
697 }
698
699 fn adjust_static_position_with_offset(&mut self, offset: &PhysicalVec<Au>) {
700 self.adjusted_static_position_rect = Some(self.static_position_rect().translate(*offset));
701 }
702}
703
704#[derive(Clone, Copy, Debug)]
705struct RectAxis {
706 origin: Au,
707 length: Au,
708}
709
710impl LogicalRect<Au> {
711 fn get_axis(&self, axis: Direction) -> RectAxis {
712 match axis {
713 Direction::Block => RectAxis {
714 origin: self.start_corner.block,
715 length: self.size.block,
716 },
717 Direction::Inline => RectAxis {
718 origin: self.start_corner.inline,
719 length: self.size.inline,
720 },
721 }
722 }
723}
724
725struct AbsoluteAxisSolver {
726 axis: Direction,
727 containing_size: Au,
728 padding_border_sum: Au,
729 computed_margin_start: AuOrAuto,
730 computed_margin_end: AuOrAuto,
731 computed_sizes: Sizes,
732 avoid_negative_margin_start: bool,
733 box_offsets: LogicalSides1D<AuOrAuto>,
734 static_position_rect_axis: RectAxis,
735 alignment: AlignFlags,
736 flip_anchor: bool,
737 is_table_or_replaced: bool,
738}
739
740impl AbsoluteAxisSolver {
741 fn inset_sum(&self) -> Au {
746 match (
747 self.box_offsets.start.non_auto(),
748 self.box_offsets.end.non_auto(),
749 ) {
750 (None, None) => {
751 if self.flip_anchor {
752 self.containing_size -
753 self.static_position_rect_axis.origin -
754 self.static_position_rect_axis.length
755 } else {
756 self.static_position_rect_axis.origin
757 }
758 },
759 (Some(start), None) => start,
760 (None, Some(end)) => end,
761 (Some(start), Some(end)) => start + end,
762 }
763 }
764
765 #[inline]
768 fn available_space(&self) -> Au {
769 Au::zero().max(self.containing_size - self.inset_sum())
770 }
771
772 #[inline]
773 fn automatic_size(&self) -> Size<Au> {
774 match self.alignment.value() {
775 _ if self.box_offsets.either_auto() => Size::FitContent,
776 AlignFlags::NORMAL | AlignFlags::AUTO if !self.is_table_or_replaced => Size::Stretch,
777 AlignFlags::STRETCH => Size::Stretch,
778 _ => Size::FitContent,
779 }
780 }
781
782 #[inline]
783 fn stretch_size(&self) -> Au {
784 Au::zero().max(
785 self.available_space() -
786 self.padding_border_sum -
787 self.computed_margin_start.auto_is(Au::zero) -
788 self.computed_margin_end.auto_is(Au::zero),
789 )
790 }
791
792 fn solve_margins(&self, size: Au) -> LogicalSides1D<Au> {
793 if self.box_offsets.either_auto() {
794 LogicalSides1D::new(
795 self.computed_margin_start.auto_is(Au::zero),
796 self.computed_margin_end.auto_is(Au::zero),
797 )
798 } else {
799 let free_space = self.available_space() - self.padding_border_sum - size;
800 match (self.computed_margin_start, self.computed_margin_end) {
801 (AuOrAuto::Auto, AuOrAuto::Auto) => {
802 if self.avoid_negative_margin_start && free_space < Au::zero() {
803 LogicalSides1D::new(Au::zero(), free_space)
804 } else {
805 let margin_start = free_space / 2;
806 LogicalSides1D::new(margin_start, free_space - margin_start)
807 }
808 },
809 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
810 LogicalSides1D::new(free_space - end, end)
811 },
812 (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => {
813 LogicalSides1D::new(start, free_space - start)
814 },
815 (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
816 LogicalSides1D::new(start, end)
817 },
818 }
819 }
820 }
821
822 fn origin_for_margin_box(
823 &self,
824 size: Au,
825 self_writing_mode: WritingMode,
826 original_parent_writing_mode: WritingMode,
827 containing_block_writing_mode: WritingMode,
828 ) -> Au {
829 let (alignment_container, alignment_container_writing_mode, flip_anchor, offsets) = match (
830 self.box_offsets.start.non_auto(),
831 self.box_offsets.end.non_auto(),
832 ) {
833 (None, None) => (
834 self.static_position_rect_axis,
835 original_parent_writing_mode,
836 self.flip_anchor,
837 None,
838 ),
839 (Some(start), Some(end)) => {
840 let alignment_container = RectAxis {
841 origin: start,
842 length: self.available_space(),
843 };
844 (
845 alignment_container,
846 containing_block_writing_mode,
847 false,
848 Some(LogicalSides1D { start, end }),
849 )
850 },
851 (Some(start), None) => return start,
855 (None, Some(end)) => {
856 return self.containing_size - size - end;
857 },
858 };
859
860 assert_eq!(
861 self_writing_mode.is_horizontal(),
862 original_parent_writing_mode.is_horizontal(),
863 "Mixed horizontal and vertical writing modes are not supported yet"
864 );
865 assert_eq!(
866 self_writing_mode.is_horizontal(),
867 containing_block_writing_mode.is_horizontal(),
868 "Mixed horizontal and vertical writing modes are not supported yet"
869 );
870 let self_value_matches_container = || {
871 self.axis == Direction::Block ||
872 self_writing_mode.is_bidi_ltr() == alignment_container_writing_mode.is_bidi_ltr()
873 };
874
875 let alignment = match self.alignment.value() {
880 AlignFlags::CENTER | AlignFlags::SPACE_AROUND | AlignFlags::SPACE_EVENLY => {
884 AlignFlags::CENTER
885 },
886 AlignFlags::SELF_START if self_value_matches_container() => AlignFlags::START,
888 AlignFlags::SELF_START => AlignFlags::END,
889 AlignFlags::SELF_END if self_value_matches_container() => AlignFlags::END,
891 AlignFlags::SELF_END => AlignFlags::START,
892 AlignFlags::LEFT if alignment_container_writing_mode.is_bidi_ltr() => AlignFlags::START,
894 AlignFlags::LEFT => AlignFlags::END,
895 AlignFlags::RIGHT if alignment_container_writing_mode.is_bidi_ltr() => AlignFlags::END,
897 AlignFlags::RIGHT => AlignFlags::START,
898 AlignFlags::END | AlignFlags::FLEX_END | AlignFlags::LAST_BASELINE => AlignFlags::END,
902 _ => AlignFlags::START,
906 };
907
908 let alignment = match alignment {
909 AlignFlags::START if flip_anchor => AlignFlags::END,
910 AlignFlags::END if flip_anchor => AlignFlags::START,
911 alignment => alignment,
912 };
913
914 let free_space = alignment_container.length - size;
915 let flags = self.alignment.flags();
916 let alignment = if flags == AlignFlags::SAFE && free_space < Au::zero() {
917 AlignFlags::START
918 } else {
919 alignment
920 };
921
922 let origin = match alignment {
923 AlignFlags::START => alignment_container.origin,
924 AlignFlags::CENTER => alignment_container.origin + free_space / 2,
925 AlignFlags::END => alignment_container.origin + free_space,
926 _ => unreachable!(),
927 };
928 if matches!(flags, AlignFlags::SAFE | AlignFlags::UNSAFE) ||
929 matches!(
930 self.alignment,
931 AlignFlags::NORMAL | AlignFlags::AUTO | AlignFlags::STRETCH
932 )
933 {
934 return origin;
935 }
936 let Some(offsets) = offsets else {
937 return origin;
938 };
939
940 let min = Au::zero().min(offsets.start);
943 let max = self.containing_size - Au::zero().min(offsets.end) - size;
944 origin.clamp_between_extremums(min, Some(max))
945 }
946}
947
948pub(crate) fn relative_adjustement(
950 style: &ComputedValues,
951 containing_block: &ContainingBlock,
952) -> LogicalVec2<Au> {
953 let cbis = containing_block.size.inline;
957 let cbbs = containing_block.size.block;
958 let box_offsets = style
959 .box_offsets(containing_block.style.writing_mode)
960 .map_inline_and_block_axes(
961 |value| value.map(|value| value.to_used_value(cbis)),
962 |value| match cbbs {
963 SizeConstraint::Definite(cbbs) => value.map(|value| value.to_used_value(cbbs)),
964 _ => match value.non_auto().and_then(|value| value.to_length()) {
965 Some(value) => AuOrAuto::LengthPercentage(value.into()),
966 None => AuOrAuto::Auto,
967 },
968 },
969 );
970 fn adjust(start: AuOrAuto, end: AuOrAuto) -> Au {
971 match (start, end) {
972 (AuOrAuto::Auto, AuOrAuto::Auto) => Au::zero(),
973 (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => -end,
974 (AuOrAuto::LengthPercentage(start), _) => start,
975 }
976 }
977 LogicalVec2 {
978 inline: adjust(box_offsets.inline_start, box_offsets.inline_end),
979 block: adjust(box_offsets.block_start, box_offsets.block_end),
980 }
981}