Give PositioningContext more responsibilities

Iits details are now private to the module.

It has a couple methods that take closures to make sure that "before" and "after" steps are done together:

* In an absolutely positioned box, take care of nested abspos (establish a new containing block, etc.)
* For a box that *might* be `position: relative`, optionally take care of the same.
This commit is contained in:
Simon Sapin 2019-12-12 21:44:24 +01:00
parent 1c8d14ac0d
commit c44ee516a1
4 changed files with 271 additions and 222 deletions

View file

@ -245,7 +245,6 @@ impl InlineFormattingContext {
},
};
ifc.positioning_context
.abspos
.push(box_.layout(initial_start_corner, tree_rank));
},
InlineLevelBox::OutOfFlowFloatBox(_box_) => {

View file

@ -18,7 +18,6 @@ use crate::{relative_adjustement, ContainingBlock};
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use rayon_croissant::ParallelIteratorExt;
use servo_arc::Arc;
use style::computed_values::position::T as Position;
use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto};
use style::Zero;
@ -235,8 +234,8 @@ fn layout_block_level_children<'a>(
/* float_context = */ None,
)
},
Default::default,
|left, right| left.append(right),
PositioningContext::new,
PositioningContext::append,
)
.collect();
for fragment in &mut fragments {
@ -317,9 +316,7 @@ impl BlockLevelBox {
)),
},
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
positioning_context
.abspos
.push(box_.layout(Vec2::zero(), tree_rank));
positioning_context.push(box_.layout(Vec2::zero(), tree_rank));
Fragment::Anonymous(AnonymousFragment::no_op(
containing_block.style.writing_mode,
))
@ -433,80 +430,70 @@ fn layout_in_flow_non_replaced_block_level<'a>(
min_box_size.block == Length::zero() &&
pb.block_end == Length::zero() &&
block_level_kind == BlockLevelKind::SameFormattingContextBlock;
let mut nested_positioning_context = PositioningContext { abspos: Vec::new() };
let mut flow_layout = layout_contents(
if style.get_box().position == Position::Relative {
&mut nested_positioning_context
} else {
positioning_context
},
&containing_block_for_children,
this_start_margin_can_collapse_with_children,
);
let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
if this_start_margin_can_collapse_with_children.0 {
block_margins_collapsed_with_children
.start
.adjoin_assign(&flow_layout.collapsible_margins_in_children.start);
if flow_layout
.collapsible_margins_in_children
.collapsed_through
{
positioning_context.for_maybe_position_relative(layout_context, style, |positioning_context| {
let mut flow_layout = layout_contents(
positioning_context,
&containing_block_for_children,
this_start_margin_can_collapse_with_children,
);
let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
if this_start_margin_can_collapse_with_children.0 {
block_margins_collapsed_with_children
.start
.adjoin_assign(&std::mem::replace(
&mut flow_layout.collapsible_margins_in_children.end,
CollapsedMargin::zero(),
));
}
}
if this_end_margin_can_collapse_with_children {
block_margins_collapsed_with_children
.end
.adjoin_assign(&flow_layout.collapsible_margins_in_children.end);
} else {
flow_layout.content_block_size += flow_layout.collapsible_margins_in_children.end.solve();
}
block_margins_collapsed_with_children.collapsed_through =
this_start_margin_can_collapse_with_children.0 &&
this_end_margin_can_collapse_with_children &&
flow_layout
.adjoin_assign(&flow_layout.collapsible_margins_in_children.start);
if flow_layout
.collapsible_margins_in_children
.collapsed_through;
let relative_adjustement = relative_adjustement(style, inline_size, block_size);
let block_size = block_size.auto_is(|| {
flow_layout
.content_block_size
.clamp_between_extremums(min_box_size.block, max_box_size.block)
});
let content_rect = Rect {
start_corner: Vec2 {
block: pb.block_start + relative_adjustement.block,
inline: pb.inline_start + relative_adjustement.inline + margin.inline_start,
},
size: Vec2 {
block: block_size,
inline: inline_size,
},
};
if style.get_box().position == Position::Relative {
nested_positioning_context.layout_abspos(
layout_context,
&mut flow_layout.fragments,
&content_rect.size,
&padding,
style,
)
}
BoxFragment {
style: style.clone(),
children: flow_layout.fragments,
content_rect,
padding,
border,
margin,
block_margins_collapsed_with_children,
}
.collapsed_through
{
block_margins_collapsed_with_children
.start
.adjoin_assign(&std::mem::replace(
&mut flow_layout.collapsible_margins_in_children.end,
CollapsedMargin::zero(),
));
}
}
if this_end_margin_can_collapse_with_children {
block_margins_collapsed_with_children
.end
.adjoin_assign(&flow_layout.collapsible_margins_in_children.end);
} else {
flow_layout.content_block_size +=
flow_layout.collapsible_margins_in_children.end.solve();
}
block_margins_collapsed_with_children.collapsed_through =
this_start_margin_can_collapse_with_children.0 &&
this_end_margin_can_collapse_with_children &&
flow_layout
.collapsible_margins_in_children
.collapsed_through;
let relative_adjustement = relative_adjustement(style, inline_size, block_size);
let block_size = block_size.auto_is(|| {
flow_layout
.content_block_size
.clamp_between_extremums(min_box_size.block, max_box_size.block)
});
let content_rect = Rect {
start_corner: Vec2 {
block: pb.block_start + relative_adjustement.block,
inline: pb.inline_start + relative_adjustement.inline + margin.inline_start,
},
size: Vec2 {
block: block_size,
inline: inline_size,
},
};
BoxFragment {
style: style.clone(),
children: flow_layout.fragments,
content_rect,
padding,
border,
margin,
block_margins_collapsed_with_children,
}
})
}
/// https://drafts.csswg.org/css2/visudet.html#block-replaced-width

View file

@ -12,13 +12,12 @@ use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragments::Fragment;
use crate::geom;
use crate::geom::flow_relative::Vec2;
use crate::positioned::AbsolutelyPositionedBox;
use crate::positioned::PositioningContext;
use crate::positioned::{AbsolutelyPositionedBox, CollectedAbsolutelyPositionedBox};
use crate::replaced::ReplacedContent;
use crate::sizing::ContentSizesRequest;
use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside};
use crate::DefiniteContainingBlock;
use rayon::iter::{IntoParallelRefIterator, ParallelExtend, ParallelIterator};
use script_layout_interface::wrapper_traits::LayoutNode;
use servo_arc::Arc;
use style::properties::ComputedValues;
@ -111,7 +110,7 @@ impl BoxTreeRoot {
};
let dummy_tree_rank = 0;
let mut positioning_context = PositioningContext { abspos: Vec::new() };
let mut positioning_context = PositioningContext::new();
let mut independent_layout = self.0.layout(
layout_context,
&mut positioning_context,
@ -119,18 +118,11 @@ impl BoxTreeRoot {
dummy_tree_rank,
);
let map = |a: &CollectedAbsolutelyPositionedBox| {
a.layout(layout_context, &initial_containing_block)
};
if layout_context.use_rayon {
independent_layout
.fragments
.par_extend(positioning_context.abspos.par_iter().map(map))
} else {
independent_layout
.fragments
.extend(positioning_context.abspos.iter().map(map))
}
positioning_context.layout_in_initial_containing_block(
layout_context,
&initial_containing_block,
&mut independent_layout.fragments,
);
FragmentTreeRoot(independent_layout.fragments)
}

View file

@ -10,8 +10,9 @@ use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::sizing::ContentSizesRequest;
use crate::style_ext::{ComputedValuesExt, DisplayInside};
use crate::{ContainingBlock, DefiniteContainingBlock};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use rayon::iter::{IntoParallelRefIterator, ParallelExtend, ParallelIterator};
use servo_arc::Arc;
use style::computed_values::position::T as Position;
use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
use style::Zero;
@ -21,10 +22,8 @@ pub(crate) struct AbsolutelyPositionedBox {
pub contents: IndependentFormattingContext,
}
#[derive(Default)]
pub(crate) struct PositioningContext<'box_tree> {
/// With `position: absolute`
pub abspos: Vec<CollectedAbsolutelyPositionedBox<'box_tree>>,
boxes: Vec<CollectedAbsolutelyPositionedBox<'box_tree>>,
}
#[derive(Debug)]
@ -83,8 +82,8 @@ impl AbsolutelyPositionedBox {
}
}
pub(crate) fn layout<'a>(
&'a self,
pub(crate) fn layout(
&self,
initial_start_corner: Vec2<Length>,
tree_rank: usize,
) -> CollectedAbsolutelyPositionedBox {
@ -123,73 +122,143 @@ impl AbsolutelyPositionedBox {
}
}
impl PositioningContext<'_> {
/// Unlike `Vec::append`, this takes ownership of the other value.
pub(crate) fn append(&mut self, mut other: Self) {
if self.abspos.is_empty() {
self.abspos = other.abspos
} else {
self.abspos.append(&mut other.abspos)
impl<'box_tree> PositioningContext<'box_tree> {
pub(crate) fn new() -> Self {
Self {
boxes: Vec::new(),
}
}
pub(crate) fn for_maybe_position_relative(
&mut self,
layout_context: &LayoutContext,
style: &ComputedValues,
f: impl FnOnce(&mut Self) -> BoxFragment,
) -> BoxFragment {
if style.clone_position() == Position::Relative {
Self::for_positioned(layout_context, f)
} else {
f(self)
}
}
fn for_positioned(
layout_context: &LayoutContext,
f: impl FnOnce(&mut Self) -> BoxFragment,
) -> BoxFragment {
let mut new = Self::new();
let mut positioned_box_fragment = f(&mut new);
new.layout_in_positioned_ancestor(layout_context, &mut positioned_box_fragment);
positioned_box_fragment
}
pub(crate) fn push(&mut self, box_: CollectedAbsolutelyPositionedBox<'box_tree>) {
self.boxes.push(box_)
}
pub(crate) fn append(&mut self, other: Self) {
vec_append_owned(
&mut self.boxes,
other.boxes,
);
}
pub(crate) fn adjust_static_positions(
&mut self,
tree_rank_in_parent: usize,
f: impl FnOnce(&mut Self) -> Vec<Fragment>,
) -> Vec<Fragment> {
let abspos_so_far = self.abspos.len();
let so_far = self.boxes.len();
let fragments = f(self);
adjust_static_positions(
&mut self.abspos[abspos_so_far..],
&mut self.boxes[so_far..],
&fragments,
tree_rank_in_parent,
);
fragments
}
pub(crate) fn layout_abspos(
self,
pub(crate) fn layout_in_initial_containing_block(
&mut self,
layout_context: &LayoutContext,
initial_containing_block: &DefiniteContainingBlock,
fragments: &mut Vec<Fragment>,
content_rect_size: &Vec2<Length>,
padding: &Sides<Length>,
style: &ComputedValues,
) {
if self.abspos.is_empty() {
return;
CollectedAbsolutelyPositionedBox::layout_many(
layout_context,
&self.boxes,
fragments,
initial_containing_block,
)
}
fn layout_in_positioned_ancestor(
&mut self,
layout_context: &LayoutContext,
positioned_box_fragment: &mut BoxFragment,
) {
if !self.boxes.is_empty() {
let padding_rect = Rect {
size: positioned_box_fragment.content_rect.size.clone(),
// Ignore the content rects position in its own containing block:
start_corner: Vec2::zero(),
}
.inflate(&positioned_box_fragment.padding);
let containing_block = DefiniteContainingBlock {
size: padding_rect.size.clone(),
style: &positioned_box_fragment.style,
};
let mut children = Vec::new();
CollectedAbsolutelyPositionedBox::layout_many(
layout_context,
&self.boxes,
&mut children,
&containing_block,
);
positioned_box_fragment
.children
.push(Fragment::Anonymous(AnonymousFragment {
children,
rect: padding_rect,
mode: positioned_box_fragment.style.writing_mode,
}))
}
let padding_rect = Rect {
size: content_rect_size.clone(),
// Ignore the content rects position in its own containing block:
start_corner: Vec2::zero(),
}
.inflate(&padding);
let containing_block = DefiniteContainingBlock {
size: padding_rect.size.clone(),
style,
};
let map =
|a: &CollectedAbsolutelyPositionedBox| a.layout(layout_context, &containing_block);
let children = if layout_context.use_rayon {
self.abspos.par_iter().map(map).collect()
} else {
self.abspos.iter().map(map).collect()
};
fragments.push(Fragment::Anonymous(AnonymousFragment {
children,
rect: padding_rect,
mode: style.writing_mode,
}))
}
}
impl CollectedAbsolutelyPositionedBox<'_> {
impl<'box_tree> CollectedAbsolutelyPositionedBox<'box_tree> {
pub(crate) fn layout_many(
layout_context: &LayoutContext,
boxes: &[Self],
fragments: &mut Vec<Fragment>,
containing_block: &DefiniteContainingBlock,
) {
if layout_context.use_rayon {
fragments.par_extend(boxes.par_iter().map(
|box_| {
Fragment::Box(box_.layout(
layout_context,
containing_block,
))
}
))
} else {
fragments.extend(boxes.iter().map(|box_| {
Fragment::Box(box_.layout(
layout_context,
containing_block,
))
}))
}
}
pub(crate) fn layout(
&self,
layout_context: &LayoutContext,
containing_block: &DefiniteContainingBlock,
) -> Fragment {
) -> BoxFragment {
let style = &self.absolutely_positioned_box.contents.style;
let cbis = containing_block.size.inline;
let cbbs = containing_block.size.block;
@ -249,94 +318,88 @@ impl CollectedAbsolutelyPositionedBox<'_> {
block_end: block_axis.margin_end,
};
let mut positioning_context = PositioningContext { abspos: Vec::new() };
let (size, mut fragments) = match self.absolutely_positioned_box.contents.as_replaced() {
Ok(replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
let style = &self.absolutely_positioned_box.contents.style;
let size = replaced_used_size.unwrap();
let fragments = replaced.make_fragments(style, size.clone());
(size, fragments)
},
Err(non_replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height
let inline_size = inline_axis.size.auto_is(|| {
let available_size = match inline_axis.anchor {
Anchor::Start(start) => {
cbis - start - pb.inline_sum() - margin.inline_sum()
},
Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(),
PositioningContext::for_positioned(layout_context, |positioning_context| {
let size;
let fragments;
match self.absolutely_positioned_box.contents.as_replaced() {
Ok(replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
let style = &self.absolutely_positioned_box.contents.style;
size = replaced_used_size.unwrap();
fragments = replaced.make_fragments(style, size.clone());
},
Err(non_replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height
let inline_size = inline_axis.size.auto_is(|| {
let available_size = match inline_axis.anchor {
Anchor::Start(start) => {
cbis - start - pb.inline_sum() - margin.inline_sum()
},
Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(),
};
self.absolutely_positioned_box
.contents
.content_sizes
.shrink_to_fit(available_size)
});
let containing_block_for_children = ContainingBlock {
inline_size,
block_size: block_axis.size,
style,
};
self.absolutely_positioned_box
.contents
.content_sizes
.shrink_to_fit(available_size)
});
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
assert_eq!(
containing_block.style.writing_mode,
containing_block_for_children.style.writing_mode,
"Mixed writing modes are not supported yet"
);
let dummy_tree_rank = 0;
let independent_layout = non_replaced.layout(
layout_context,
positioning_context,
&containing_block_for_children,
dummy_tree_rank,
);
let containing_block_for_children = ContainingBlock {
inline_size,
block_size: block_axis.size,
style,
};
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
assert_eq!(
containing_block.style.writing_mode,
containing_block_for_children.style.writing_mode,
"Mixed writing modes are not supported yet"
);
let dummy_tree_rank = 0;
let independent_layout = non_replaced.layout(
layout_context,
&mut positioning_context,
&containing_block_for_children,
dummy_tree_rank,
);
size = Vec2 {
inline: inline_size,
block: block_axis
.size
.auto_is(|| independent_layout.content_block_size),
};
fragments = independent_layout.fragments
},
};
let size = Vec2 {
inline: inline_size,
block: block_axis
.size
.auto_is(|| independent_layout.content_block_size),
};
(size, independent_layout.fragments)
},
};
let inline_start = match inline_axis.anchor {
Anchor::Start(start) => start + pb.inline_start + margin.inline_start,
Anchor::End(end) => cbbs - end - pb.inline_end - margin.inline_end - 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 - size.block,
};
let inline_start = match inline_axis.anchor {
Anchor::Start(start) => start + pb.inline_start + margin.inline_start,
Anchor::End(end) => cbbs - end - pb.inline_end - margin.inline_end - 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 - size.block,
};
let content_rect = Rect {
start_corner: Vec2 {
inline: inline_start,
block: block_start,
},
size,
};
let content_rect = Rect {
start_corner: Vec2 {
inline: inline_start,
block: block_start,
},
size,
};
positioning_context.layout_abspos(
layout_context,
&mut fragments,
&content_rect.size,
&padding,
style,
);
Fragment::Box(BoxFragment {
style: style.clone(),
children: fragments,
content_rect,
padding,
border,
margin,
block_margins_collapsed_with_children: CollapsedBlockMargins::zero(),
BoxFragment {
style: style.clone(),
children: fragments,
content_rect,
padding,
border,
margin,
block_margins_collapsed_with_children: CollapsedBlockMargins::zero(),
}
})
}
}
@ -468,3 +531,11 @@ fn adjust_static_positions(
}
}
}
fn vec_append_owned<T>(a: &mut Vec<T>, mut b: Vec<T>) {
if a.is_empty() {
*a = b
} else {
a.append(&mut b)
}
}