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