use app_units::Au;
use rayon::iter::IntoParallelRefMutIterator;
use rayon::prelude::{IndexedParallelIterator, ParallelIterator};
use serde::Serialize;
use style::computed_values::position::T as Position;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
use style::values::specified::align::{AlignFlags, AxisDirection};
use style::values::specified::text::TextDecorationLine;
use style::Zero;
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::dom::NodeExt;
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragment_tree::{
BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, HoistedSharedFragment,
};
use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalPoint,
PhysicalRect, PhysicalVec, ToLogical, ToLogicalWithContainingBlock,
};
use crate::style_ext::{ComputedValuesExt, DisplayInside};
use crate::{ContainingBlock, DefiniteContainingBlock, IndefiniteContainingBlock};
#[derive(Debug, Serialize)]
pub(crate) struct AbsolutelyPositionedBox {
pub context: IndependentFormattingContext,
}
pub(crate) struct PositioningContext {
for_nearest_positioned_ancestor: Option<Vec<HoistedAbsolutelyPositionedBox>>,
for_nearest_containing_block_for_all_descendants: Vec<HoistedAbsolutelyPositionedBox>,
}
pub(crate) struct HoistedAbsolutelyPositionedBox {
absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
pub fragment: ArcRefCell<HoistedSharedFragment>,
}
impl AbsolutelyPositionedBox {
pub fn construct<'dom>(
context: &LayoutContext,
node_info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
display_inside: DisplayInside,
contents: Contents,
) -> Self {
Self {
context: IndependentFormattingContext::construct(
context,
node_info,
display_inside,
contents,
TextDecorationLine::NONE,
),
}
}
pub(crate) fn to_hoisted(
absolutely_positioned_box: ArcRefCell<Self>,
static_position_rectangle: PhysicalRect<Au>,
resolved_alignment: LogicalVec2<AlignFlags>,
original_parent_writing_mode: WritingMode,
) -> HoistedAbsolutelyPositionedBox {
HoistedAbsolutelyPositionedBox {
fragment: ArcRefCell::new(HoistedSharedFragment::new(
static_position_rectangle,
resolved_alignment,
original_parent_writing_mode,
)),
absolutely_positioned_box,
}
}
}
impl PositioningContext {
pub(crate) fn new_for_containing_block_for_all_descendants() -> Self {
Self {
for_nearest_positioned_ancestor: None,
for_nearest_containing_block_for_all_descendants: Vec::new(),
}
}
pub(crate) fn new_for_subtree(collects_for_nearest_positioned_ancestor: bool) -> Self {
Self {
for_nearest_positioned_ancestor: if collects_for_nearest_positioned_ancestor {
Some(Vec::new())
} else {
None
},
for_nearest_containing_block_for_all_descendants: Vec::new(),
}
}
pub(crate) fn collects_for_nearest_positioned_ancestor(&self) -> bool {
self.for_nearest_positioned_ancestor.is_some()
}
pub(crate) fn new_for_style(style: &ComputedValues) -> Option<Self> {
if style.establishes_containing_block_for_all_descendants(FragmentFlags::empty()) {
Some(Self::new_for_containing_block_for_all_descendants())
} else if style
.establishes_containing_block_for_absolute_descendants(FragmentFlags::empty())
{
Some(Self {
for_nearest_positioned_ancestor: Some(Vec::new()),
for_nearest_containing_block_for_all_descendants: Vec::new(),
})
} else {
None
}
}
pub(crate) fn adjust_static_position_of_hoisted_fragments(
&mut self,
parent_fragment: &Fragment,
index: PositioningContextLength,
) {
let start_offset = match &parent_fragment {
Fragment::Box(fragment) | Fragment::Float(fragment) => &fragment.content_rect.origin,
Fragment::AbsoluteOrFixedPositioned(_) => return,
Fragment::Positioning(fragment) => &fragment.rect.origin,
_ => unreachable!(),
};
self.adjust_static_position_of_hoisted_fragments_with_offset(
&start_offset.to_vector(),
index,
);
}
pub(crate) fn adjust_static_position_of_hoisted_fragments_with_offset(
&mut self,
offset: &PhysicalVec<Au>,
index: PositioningContextLength,
) {
if let Some(hoisted_boxes) = self.for_nearest_positioned_ancestor.as_mut() {
hoisted_boxes
.iter_mut()
.skip(index.for_nearest_positioned_ancestor)
.for_each(|hoisted_fragment| {
hoisted_fragment
.fragment
.borrow_mut()
.adjust_offsets(offset)
})
}
self.for_nearest_containing_block_for_all_descendants
.iter_mut()
.skip(index.for_nearest_containing_block_for_all_descendants)
.for_each(|hoisted_fragment| {
hoisted_fragment
.fragment
.borrow_mut()
.adjust_offsets(offset)
})
}
pub(crate) fn layout_maybe_position_relative_fragment(
&mut self,
layout_context: &LayoutContext,
containing_block: &ContainingBlock,
style: &ComputedValues,
fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment,
) -> BoxFragment {
let mut new_context = match Self::new_for_style(style) {
Some(new_context) => new_context,
None => return fragment_layout_fn(self),
};
let mut new_fragment = fragment_layout_fn(&mut new_context);
new_context.layout_collected_children(layout_context, &mut new_fragment);
self.append(new_context);
if style.clone_position() == Position::Relative {
new_fragment.content_rect.origin += relative_adjustement(style, containing_block)
.to_physical_vector(containing_block.style.writing_mode)
}
new_fragment
}
pub fn layout_collected_children(
&mut self,
layout_context: &LayoutContext,
new_fragment: &mut BoxFragment,
) {
let padding_rect = PhysicalRect::new(
PhysicalPoint::origin(),
new_fragment.content_rect.size,
)
.outer_rect(new_fragment.padding);
let containing_block = DefiniteContainingBlock {
size: padding_rect
.size
.to_logical(new_fragment.style.writing_mode),
style: &new_fragment.style,
};
let take_hoisted_boxes_pending_layout = |context: &mut Self| match context
.for_nearest_positioned_ancestor
.as_mut()
{
Some(fragments) => std::mem::take(fragments),
None => std::mem::take(&mut context.for_nearest_containing_block_for_all_descendants),
};
let mut hoisted_boxes = take_hoisted_boxes_pending_layout(self);
let mut laid_out_child_fragments = Vec::new();
while !hoisted_boxes.is_empty() {
HoistedAbsolutelyPositionedBox::layout_many(
layout_context,
&mut hoisted_boxes,
&mut laid_out_child_fragments,
&mut self.for_nearest_containing_block_for_all_descendants,
&containing_block,
);
hoisted_boxes = take_hoisted_boxes_pending_layout(self);
}
new_fragment.children.extend(laid_out_child_fragments);
}
pub(crate) fn push(&mut self, box_: HoistedAbsolutelyPositionedBox) {
if let Some(nearest) = &mut self.for_nearest_positioned_ancestor {
let position = box_
.absolutely_positioned_box
.borrow()
.context
.style()
.clone_position();
match position {
Position::Fixed => {}, Position::Absolute => return nearest.push(box_),
Position::Static | Position::Relative | Position::Sticky => unreachable!(),
}
}
self.for_nearest_containing_block_for_all_descendants
.push(box_)
}
fn is_empty(&self) -> bool {
self.for_nearest_containing_block_for_all_descendants
.is_empty() &&
self.for_nearest_positioned_ancestor
.as_ref()
.map_or(true, |vector| vector.is_empty())
}
pub(crate) fn append(&mut self, other: Self) {
if other.is_empty() {
return;
}
vec_append_owned(
&mut self.for_nearest_containing_block_for_all_descendants,
other.for_nearest_containing_block_for_all_descendants,
);
match (
self.for_nearest_positioned_ancestor.as_mut(),
other.for_nearest_positioned_ancestor,
) {
(Some(us), Some(them)) => vec_append_owned(us, them),
(None, Some(them)) => {
vec_append_owned(
&mut self.for_nearest_containing_block_for_all_descendants,
them,
);
},
(None, None) => {},
_ => unreachable!(),
}
}
pub(crate) fn layout_initial_containing_block_children(
&mut self,
layout_context: &LayoutContext,
initial_containing_block: &DefiniteContainingBlock,
fragments: &mut Vec<ArcRefCell<Fragment>>,
) {
debug_assert!(self.for_nearest_positioned_ancestor.is_none());
while !self
.for_nearest_containing_block_for_all_descendants
.is_empty()
{
HoistedAbsolutelyPositionedBox::layout_many(
layout_context,
&mut std::mem::take(&mut self.for_nearest_containing_block_for_all_descendants),
fragments,
&mut self.for_nearest_containing_block_for_all_descendants,
initial_containing_block,
)
}
}
pub(crate) fn clear(&mut self) {
self.for_nearest_containing_block_for_all_descendants
.clear();
if let Some(v) = self.for_nearest_positioned_ancestor.as_mut() {
v.clear()
}
}
pub(crate) fn len(&self) -> PositioningContextLength {
PositioningContextLength {
for_nearest_positioned_ancestor: self
.for_nearest_positioned_ancestor
.as_ref()
.map_or(0, |vec| vec.len()),
for_nearest_containing_block_for_all_descendants: self
.for_nearest_containing_block_for_all_descendants
.len(),
}
}
pub(crate) fn truncate(&mut self, length: &PositioningContextLength) {
if let Some(vec) = self.for_nearest_positioned_ancestor.as_mut() {
vec.truncate(length.for_nearest_positioned_ancestor);
}
self.for_nearest_containing_block_for_all_descendants
.truncate(length.for_nearest_containing_block_for_all_descendants);
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct PositioningContextLength {
for_nearest_positioned_ancestor: usize,
for_nearest_containing_block_for_all_descendants: usize,
}
impl Zero for PositioningContextLength {
fn zero() -> Self {
PositioningContextLength {
for_nearest_positioned_ancestor: 0,
for_nearest_containing_block_for_all_descendants: 0,
}
}
fn is_zero(&self) -> bool {
self.for_nearest_positioned_ancestor == 0 &&
self.for_nearest_containing_block_for_all_descendants == 0
}
}
impl HoistedAbsolutelyPositionedBox {
pub(crate) fn layout_many(
layout_context: &LayoutContext,
boxes: &mut [Self],
fragments: &mut Vec<ArcRefCell<Fragment>>,
for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>,
containing_block: &DefiniteContainingBlock,
) {
if layout_context.use_rayon {
let mut new_fragments = Vec::new();
let mut new_hoisted_boxes = Vec::new();
boxes
.par_iter_mut()
.map(|hoisted_box| {
let mut new_hoisted_boxes: Vec<HoistedAbsolutelyPositionedBox> = Vec::new();
let new_fragment = ArcRefCell::new(Fragment::Box(hoisted_box.layout(
layout_context,
&mut new_hoisted_boxes,
containing_block,
)));
hoisted_box.fragment.borrow_mut().fragment = Some(new_fragment.clone());
(new_fragment, new_hoisted_boxes)
})
.unzip_into_vecs(&mut new_fragments, &mut new_hoisted_boxes);
fragments.extend(new_fragments);
for_nearest_containing_block_for_all_descendants
.extend(new_hoisted_boxes.into_iter().flatten());
} else {
fragments.extend(boxes.iter_mut().map(|box_| {
let new_fragment = ArcRefCell::new(Fragment::Box(box_.layout(
layout_context,
for_nearest_containing_block_for_all_descendants,
containing_block,
)));
box_.fragment.borrow_mut().fragment = Some(new_fragment.clone());
new_fragment
}))
}
}
pub(crate) fn layout(
&mut self,
layout_context: &LayoutContext,
for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>,
containing_block: &DefiniteContainingBlock,
) -> BoxFragment {
let cbis = containing_block.size.inline;
let cbbs = containing_block.size.block;
let mut absolutely_positioned_box = self.absolutely_positioned_box.borrow_mut();
let containing_block_writing_mode = containing_block.style.writing_mode;
let style = absolutely_positioned_box.context.style().clone();
let pbm = style.padding_border_margin(&containing_block.into());
let indefinite_containing_block = containing_block.into();
let computed_size = match &absolutely_positioned_box.context {
IndependentFormattingContext::Replaced(replaced) => {
let used_size = replaced.contents.used_size_as_if_inline_element(
&containing_block.into(),
&replaced.style,
&pbm,
);
LogicalVec2 {
inline: AuOrAuto::LengthPercentage(used_size.inline),
block: AuOrAuto::LengthPercentage(used_size.block),
}
},
IndependentFormattingContext::NonReplaced(non_replaced) => non_replaced
.style
.content_box_size(&indefinite_containing_block, &pbm),
};
let shared_fragment = self.fragment.borrow();
let static_position_rect = shared_fragment
.static_position_rect
.to_logical(&indefinite_containing_block);
let box_offset = style.box_offsets(&indefinite_containing_block);
let inline_box_offsets = AbsoluteBoxOffsets {
start: box_offset.inline_start,
end: box_offset.inline_end,
};
let inline_alignment = match inline_box_offsets.either_specified() {
true => AlignFlags::START | AlignFlags::SAFE,
false => shared_fragment.resolved_alignment.inline,
};
let inline_axis_solver = AbsoluteAxisSolver {
axis: AxisDirection::Inline,
containing_size: cbis,
padding_border_sum: pbm.padding_border_sums.inline,
computed_margin_start: pbm.margin.inline_start,
computed_margin_end: pbm.margin.inline_end,
avoid_negative_margin_start: true,
box_offsets: inline_box_offsets,
static_position_rect_axis: static_position_rect.get_axis(AxisDirection::Inline),
alignment: inline_alignment,
flip_anchor: shared_fragment.original_parent_writing_mode.is_bidi_ltr() !=
indefinite_containing_block.style.writing_mode.is_bidi_ltr(),
};
let block_box_offsets = AbsoluteBoxOffsets {
start: box_offset.block_start,
end: box_offset.block_end,
};
let block_alignment = match block_box_offsets.either_specified() {
true => style.clone_align_self().0 .0,
false => shared_fragment.resolved_alignment.block,
};
let block_axis_solver = AbsoluteAxisSolver {
axis: AxisDirection::Block,
containing_size: cbbs,
padding_border_sum: pbm.padding_border_sums.block,
computed_margin_start: pbm.margin.block_start,
computed_margin_end: pbm.margin.block_end,
avoid_negative_margin_start: false,
box_offsets: block_box_offsets,
static_position_rect_axis: static_position_rect.get_axis(AxisDirection::Block),
alignment: block_alignment,
flip_anchor: false,
};
let overconstrained = LogicalVec2 {
inline: inline_axis_solver.is_overconstrained_for_size(computed_size.inline),
block: block_axis_solver.is_overconstrained_for_size(computed_size.block),
};
let mut inline_axis = inline_axis_solver.solve_for_size(computed_size.inline);
let mut block_axis = block_axis_solver.solve_for_size(computed_size.block);
let mut positioning_context = PositioningContext::new_for_style(&style).unwrap();
let mut new_fragment = {
let content_size: LogicalVec2<Au>;
let fragments;
match &mut absolutely_positioned_box.context {
IndependentFormattingContext::Replaced(replaced) => {
content_size = computed_size.auto_is(|| unreachable!());
fragments = replaced.contents.make_fragments(
&style,
&containing_block.into(),
content_size.to_physical_size(containing_block_writing_mode),
);
},
IndependentFormattingContext::NonReplaced(non_replaced) => {
let min_size = non_replaced
.style
.content_min_box_size(&indefinite_containing_block, &pbm)
.map(|t| t.map(Au::from).auto_is(Au::zero));
let max_size = style
.content_max_box_size(&containing_block.into(), &pbm)
.map(|t| t.map(Au::from));
let mut inline_size = inline_axis.size.auto_is(|| {
let anchor = match inline_axis.anchor {
Anchor::Start(start) => start,
Anchor::End(end) => end,
};
let margin_sum = inline_axis.margin_start + inline_axis.margin_end;
let available_size =
cbis - anchor - pbm.padding_border_sums.inline - margin_sum;
let style = non_replaced.style.clone();
let containing_block_for_children =
IndefiniteContainingBlock::from(containing_block)
.new_for_intrinsic_inline_size_of_child(
&style,
&LogicalVec2::zero(),
);
non_replaced
.inline_content_sizes(layout_context, &containing_block_for_children)
.shrink_to_fit(available_size)
});
if let Some(max) = max_size.inline {
if inline_size > max {
inline_axis =
inline_axis_solver.solve_for_size(AuOrAuto::LengthPercentage(max));
inline_size = inline_axis.size.auto_is(|| unreachable!());
}
}
if inline_size < min_size.inline {
inline_axis = inline_axis_solver
.solve_for_size(AuOrAuto::LengthPercentage(min_size.inline));
inline_size = inline_axis.size.auto_is(|| unreachable!());
}
struct Result {
content_size: LogicalVec2<Au>,
fragments: Vec<Fragment>,
}
let mut try_layout = |size| {
let containing_block_for_children = ContainingBlock {
inline_size,
block_size: size,
style: &style,
};
assert_eq!(
containing_block.style.writing_mode.is_horizontal(),
containing_block_for_children
.style
.writing_mode
.is_horizontal(),
"Mixed horizontal and vertical writing modes are not supported yet"
);
positioning_context.clear();
let independent_layout = non_replaced.layout(
layout_context,
&mut positioning_context,
&containing_block_for_children,
&containing_block.into(),
);
let (block_size, inline_size) =
match independent_layout.content_inline_size_for_table {
Some(inline_size) => {
(independent_layout.content_block_size, inline_size)
},
None => (
size.auto_is(|| independent_layout.content_block_size),
inline_size,
),
};
Result {
content_size: LogicalVec2 {
inline: inline_size,
block: block_size,
},
fragments: independent_layout.fragments,
}
};
let mut result = try_layout(block_axis.size);
if let Some(max) = max_size.block {
if result.content_size.block > max {
block_axis =
block_axis_solver.solve_for_size(AuOrAuto::LengthPercentage(max));
result = try_layout(AuOrAuto::LengthPercentage(max));
}
}
if result.content_size.block < min_size.block {
block_axis = block_axis_solver
.solve_for_size(AuOrAuto::LengthPercentage(min_size.block));
result = try_layout(AuOrAuto::LengthPercentage(min_size.block));
}
content_size = result.content_size;
fragments = result.fragments;
},
};
let margin = LogicalSides {
inline_start: inline_axis.margin_start,
inline_end: inline_axis.margin_end,
block_start: block_axis.margin_start,
block_end: block_axis.margin_end,
};
let pb = pbm.padding + pbm.border;
let inline_start = match inline_axis.anchor {
Anchor::Start(start) => start + pb.inline_start + margin.inline_start,
Anchor::End(end) => {
cbis - end - pb.inline_end - margin.inline_end - content_size.inline
},
};
let block_start = match block_axis.anchor {
Anchor::Start(start) => start + pb.block_start + margin.block_start,
Anchor::End(end) => {
cbbs - end - pb.block_end - margin.block_end - content_size.block
},
};
let mut content_rect = LogicalRect {
start_corner: LogicalVec2 {
inline: inline_start,
block: block_start,
},
size: content_size,
};
let margin_box_rect = content_rect
.inflate(&pbm.padding)
.inflate(&pbm.border)
.inflate(&margin);
block_axis_solver.solve_alignment(margin_box_rect, &mut content_rect);
inline_axis_solver.solve_alignment(margin_box_rect, &mut content_rect);
let physical_overconstrained =
overconstrained.to_physical_size(containing_block.style.writing_mode);
BoxFragment::new_with_overconstrained(
absolutely_positioned_box.context.base_fragment_info(),
style,
fragments,
content_rect.to_physical(Some(&containing_block.into())),
pbm.padding.to_physical(containing_block_writing_mode),
pbm.border.to_physical(containing_block_writing_mode),
margin.to_physical(containing_block_writing_mode),
None, CollapsedBlockMargins::zero(),
physical_overconstrained,
)
};
positioning_context.layout_collected_children(layout_context, &mut new_fragment);
positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
&new_fragment.content_rect.origin.to_vector(),
PositioningContextLength::zero(),
);
for_nearest_containing_block_for_all_descendants
.extend(positioning_context.for_nearest_containing_block_for_all_descendants);
new_fragment
}
}
#[derive(Clone, Copy)]
struct RectAxis {
origin: Au,
length: Au,
}
impl LogicalRect<Au> {
fn get_axis(&self, axis: AxisDirection) -> RectAxis {
match axis {
AxisDirection::Block => RectAxis {
origin: self.start_corner.block,
length: self.size.block,
},
AxisDirection::Inline => RectAxis {
origin: self.start_corner.inline,
length: self.size.inline,
},
}
}
}
#[derive(Debug)]
struct AbsoluteBoxOffsets<'a> {
start: LengthPercentageOrAuto<'a>,
end: LengthPercentageOrAuto<'a>,
}
impl AbsoluteBoxOffsets<'_> {
pub(crate) fn both_specified(&self) -> bool {
!self.start.is_auto() && !self.end.is_auto()
}
pub(crate) fn either_specified(&self) -> bool {
!self.start.is_auto() || !self.end.is_auto()
}
}
enum Anchor {
Start(Au),
End(Au),
}
struct AxisResult {
anchor: Anchor,
size: AuOrAuto,
margin_start: Au,
margin_end: Au,
}
struct AbsoluteAxisSolver<'a> {
axis: AxisDirection,
containing_size: Au,
padding_border_sum: Au,
computed_margin_start: AuOrAuto,
computed_margin_end: AuOrAuto,
avoid_negative_margin_start: bool,
box_offsets: AbsoluteBoxOffsets<'a>,
static_position_rect_axis: RectAxis,
alignment: AlignFlags,
flip_anchor: bool,
}
impl<'a> AbsoluteAxisSolver<'a> {
fn solve_for_size(&self, computed_size: AuOrAuto) -> AxisResult {
match (
self.box_offsets.start.non_auto(),
self.box_offsets.end.non_auto(),
) {
(None, None) => AxisResult {
anchor: if self.flip_anchor {
Anchor::End(self.containing_size - self.static_position_rect_axis.origin)
} else {
Anchor::Start(self.static_position_rect_axis.origin)
},
size: computed_size,
margin_start: self.computed_margin_start.auto_is(Au::zero),
margin_end: self.computed_margin_end.auto_is(Au::zero),
},
(Some(start), None) => AxisResult {
anchor: Anchor::Start(start.to_used_value(self.containing_size)),
size: computed_size,
margin_start: self.computed_margin_start.auto_is(Au::zero),
margin_end: self.computed_margin_end.auto_is(Au::zero),
},
(None, Some(end)) => AxisResult {
anchor: Anchor::End(end.to_used_value(self.containing_size)),
size: computed_size,
margin_start: self.computed_margin_start.auto_is(Au::zero),
margin_end: self.computed_margin_end.auto_is(Au::zero),
},
(Some(start), Some(end)) => {
let start = start.to_used_value(self.containing_size);
let end = end.to_used_value(self.containing_size);
let margin_start;
let margin_end;
let used_size;
if let AuOrAuto::LengthPercentage(s) = computed_size {
used_size = s;
let margins = self.containing_size - start - end - self.padding_border_sum - s;
match (self.computed_margin_start, self.computed_margin_end) {
(AuOrAuto::Auto, AuOrAuto::Auto) => {
if self.avoid_negative_margin_start && margins < Au::zero() {
margin_start = Au::zero();
margin_end = margins;
} else {
margin_start = margins / 2;
margin_end = margins - margin_start;
}
},
(AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
margin_start = margins - end;
margin_end = end;
},
(AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => {
margin_start = start;
margin_end = margins - start;
},
(AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
margin_start = start;
margin_end = end;
},
}
} else {
margin_start = self.computed_margin_start.auto_is(Au::zero);
margin_end = self.computed_margin_end.auto_is(Au::zero);
used_size = self.containing_size -
start -
end -
self.padding_border_sum -
margin_start -
margin_end;
};
AxisResult {
anchor: Anchor::Start(start),
size: AuOrAuto::LengthPercentage(used_size),
margin_start,
margin_end,
}
},
}
}
fn is_overconstrained_for_size(&self, computed_size: AuOrAuto) -> bool {
!computed_size.is_auto() &&
self.box_offsets.both_specified() &&
!self.computed_margin_start.is_auto() &&
!self.computed_margin_end.is_auto()
}
fn origin_for_alignment_or_justification(&self, margin_box_axis: RectAxis) -> Option<Au> {
let alignment_container = match (
self.box_offsets.start.non_auto(),
self.box_offsets.end.non_auto(),
) {
(None, None) => self.static_position_rect_axis,
(Some(start), Some(end)) => {
let start = start.to_used_value(self.containing_size);
let end = end.to_used_value(self.containing_size);
RectAxis {
origin: start,
length: self.containing_size - (end + start),
}
},
_ => return None,
};
let mut value_after_safety = self.alignment.value();
if self.alignment.flags() == AlignFlags::SAFE &&
margin_box_axis.length > alignment_container.length
{
value_after_safety = AlignFlags::START;
}
match value_after_safety {
AlignFlags::CENTER | AlignFlags::SPACE_AROUND | AlignFlags::SPACE_EVENLY => Some(
alignment_container.origin +
((alignment_container.length - margin_box_axis.length) / 2),
),
AlignFlags::FLEX_END | AlignFlags::END => Some(
alignment_container.origin + alignment_container.length - margin_box_axis.length,
),
_ => None,
}
}
fn solve_alignment(
&self,
margin_box_rect: LogicalRect<Au>,
content_box_rect: &mut LogicalRect<Au>,
) {
let Some(new_origin) =
self.origin_for_alignment_or_justification(margin_box_rect.get_axis(self.axis))
else {
return;
};
match self.axis {
AxisDirection::Block => {
content_box_rect.start_corner.block +=
new_origin - margin_box_rect.start_corner.block
},
AxisDirection::Inline => {
content_box_rect.start_corner.inline +=
new_origin - margin_box_rect.start_corner.inline
},
}
}
}
fn vec_append_owned<T>(a: &mut Vec<T>, mut b: Vec<T>) {
if a.is_empty() {
*a = b
} else {
a.append(&mut b)
}
}
pub(crate) fn relative_adjustement(
style: &ComputedValues,
containing_block: &ContainingBlock,
) -> LogicalVec2<Au> {
let cbis = containing_block.inline_size;
let cbbs = containing_block.block_size;
let box_offsets = style
.box_offsets(containing_block)
.map_inline_and_block_axes(
|value| value.map(|value| value.to_used_value(cbis)),
|value| match cbbs.non_auto() {
Some(cbbs) => value.map(|value| value.to_used_value(cbbs)),
None => match value.non_auto().and_then(|value| value.to_length()) {
Some(value) => AuOrAuto::LengthPercentage(value.into()),
None => AuOrAuto::Auto,
},
},
);
fn adjust(start: AuOrAuto, end: AuOrAuto) -> Au {
match (start, end) {
(AuOrAuto::Auto, AuOrAuto::Auto) => Au::zero(),
(AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => -end,
(AuOrAuto::LengthPercentage(start), _) => start,
}
}
LogicalVec2 {
inline: adjust(box_offsets.inline_start, box_offsets.inline_end),
block: adjust(box_offsets.block_start, box_offsets.block_end),
}
}