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 ifc.positioning_context
.abspos
.push(box_.layout(initial_start_corner, tree_rank)); .push(box_.layout(initial_start_corner, tree_rank));
}, },
InlineLevelBox::OutOfFlowFloatBox(_box_) => { InlineLevelBox::OutOfFlowFloatBox(_box_) => {

View file

@ -18,7 +18,6 @@ use crate::{relative_adjustement, ContainingBlock};
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use rayon_croissant::ParallelIteratorExt; use rayon_croissant::ParallelIteratorExt;
use servo_arc::Arc; use servo_arc::Arc;
use style::computed_values::position::T as Position;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto}; use style::values::computed::{Length, LengthOrAuto};
use style::Zero; use style::Zero;
@ -235,8 +234,8 @@ fn layout_block_level_children<'a>(
/* float_context = */ None, /* float_context = */ None,
) )
}, },
Default::default, PositioningContext::new,
|left, right| left.append(right), PositioningContext::append,
) )
.collect(); .collect();
for fragment in &mut fragments { for fragment in &mut fragments {
@ -317,9 +316,7 @@ impl BlockLevelBox {
)), )),
}, },
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => { BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
positioning_context positioning_context.push(box_.layout(Vec2::zero(), tree_rank));
.abspos
.push(box_.layout(Vec2::zero(), tree_rank));
Fragment::Anonymous(AnonymousFragment::no_op( Fragment::Anonymous(AnonymousFragment::no_op(
containing_block.style.writing_mode, containing_block.style.writing_mode,
)) ))
@ -433,13 +430,10 @@ fn layout_in_flow_non_replaced_block_level<'a>(
min_box_size.block == Length::zero() && min_box_size.block == Length::zero() &&
pb.block_end == Length::zero() && pb.block_end == Length::zero() &&
block_level_kind == BlockLevelKind::SameFormattingContextBlock; block_level_kind == BlockLevelKind::SameFormattingContextBlock;
let mut nested_positioning_context = PositioningContext { abspos: Vec::new() };
positioning_context.for_maybe_position_relative(layout_context, style, |positioning_context| {
let mut flow_layout = layout_contents( let mut flow_layout = layout_contents(
if style.get_box().position == Position::Relative { positioning_context,
&mut nested_positioning_context
} else {
positioning_context
},
&containing_block_for_children, &containing_block_for_children,
this_start_margin_can_collapse_with_children, this_start_margin_can_collapse_with_children,
); );
@ -465,7 +459,8 @@ fn layout_in_flow_non_replaced_block_level<'a>(
.end .end
.adjoin_assign(&flow_layout.collapsible_margins_in_children.end); .adjoin_assign(&flow_layout.collapsible_margins_in_children.end);
} else { } else {
flow_layout.content_block_size += flow_layout.collapsible_margins_in_children.end.solve(); flow_layout.content_block_size +=
flow_layout.collapsible_margins_in_children.end.solve();
} }
block_margins_collapsed_with_children.collapsed_through = block_margins_collapsed_with_children.collapsed_through =
this_start_margin_can_collapse_with_children.0 && this_start_margin_can_collapse_with_children.0 &&
@ -489,15 +484,6 @@ fn layout_in_flow_non_replaced_block_level<'a>(
inline: inline_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 { BoxFragment {
style: style.clone(), style: style.clone(),
children: flow_layout.fragments, children: flow_layout.fragments,
@ -507,6 +493,7 @@ fn layout_in_flow_non_replaced_block_level<'a>(
margin, margin,
block_margins_collapsed_with_children, block_margins_collapsed_with_children,
} }
})
} }
/// https://drafts.csswg.org/css2/visudet.html#block-replaced-width /// 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::fragments::Fragment;
use crate::geom; use crate::geom;
use crate::geom::flow_relative::Vec2; use crate::geom::flow_relative::Vec2;
use crate::positioned::AbsolutelyPositionedBox;
use crate::positioned::PositioningContext; use crate::positioned::PositioningContext;
use crate::positioned::{AbsolutelyPositionedBox, CollectedAbsolutelyPositionedBox};
use crate::replaced::ReplacedContent; use crate::replaced::ReplacedContent;
use crate::sizing::ContentSizesRequest; use crate::sizing::ContentSizesRequest;
use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside}; use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside};
use crate::DefiniteContainingBlock; use crate::DefiniteContainingBlock;
use rayon::iter::{IntoParallelRefIterator, ParallelExtend, ParallelIterator};
use script_layout_interface::wrapper_traits::LayoutNode; use script_layout_interface::wrapper_traits::LayoutNode;
use servo_arc::Arc; use servo_arc::Arc;
use style::properties::ComputedValues; use style::properties::ComputedValues;
@ -111,7 +110,7 @@ impl BoxTreeRoot {
}; };
let dummy_tree_rank = 0; 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( let mut independent_layout = self.0.layout(
layout_context, layout_context,
&mut positioning_context, &mut positioning_context,
@ -119,18 +118,11 @@ impl BoxTreeRoot {
dummy_tree_rank, dummy_tree_rank,
); );
let map = |a: &CollectedAbsolutelyPositionedBox| { positioning_context.layout_in_initial_containing_block(
a.layout(layout_context, &initial_containing_block) layout_context,
}; &initial_containing_block,
if layout_context.use_rayon { &mut independent_layout.fragments,
independent_layout );
.fragments
.par_extend(positioning_context.abspos.par_iter().map(map))
} else {
independent_layout
.fragments
.extend(positioning_context.abspos.iter().map(map))
}
FragmentTreeRoot(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::sizing::ContentSizesRequest;
use crate::style_ext::{ComputedValuesExt, DisplayInside}; use crate::style_ext::{ComputedValuesExt, DisplayInside};
use crate::{ContainingBlock, DefiniteContainingBlock}; use crate::{ContainingBlock, DefiniteContainingBlock};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use rayon::iter::{IntoParallelRefIterator, ParallelExtend, ParallelIterator};
use servo_arc::Arc; use servo_arc::Arc;
use style::computed_values::position::T as Position;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto}; use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
use style::Zero; use style::Zero;
@ -21,10 +22,8 @@ pub(crate) struct AbsolutelyPositionedBox {
pub contents: IndependentFormattingContext, pub contents: IndependentFormattingContext,
} }
#[derive(Default)]
pub(crate) struct PositioningContext<'box_tree> { pub(crate) struct PositioningContext<'box_tree> {
/// With `position: absolute` boxes: Vec<CollectedAbsolutelyPositionedBox<'box_tree>>,
pub abspos: Vec<CollectedAbsolutelyPositionedBox<'box_tree>>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -83,8 +82,8 @@ impl AbsolutelyPositionedBox {
} }
} }
pub(crate) fn layout<'a>( pub(crate) fn layout(
&'a self, &self,
initial_start_corner: Vec2<Length>, initial_start_corner: Vec2<Length>,
tree_rank: usize, tree_rank: usize,
) -> CollectedAbsolutelyPositionedBox { ) -> CollectedAbsolutelyPositionedBox {
@ -123,73 +122,143 @@ impl AbsolutelyPositionedBox {
} }
} }
impl PositioningContext<'_> { impl<'box_tree> PositioningContext<'box_tree> {
/// Unlike `Vec::append`, this takes ownership of the other value. pub(crate) fn new() -> Self {
pub(crate) fn append(&mut self, mut other: Self) { Self {
if self.abspos.is_empty() { boxes: Vec::new(),
self.abspos = other.abspos
} else {
self.abspos.append(&mut other.abspos)
} }
} }
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( pub(crate) fn adjust_static_positions(
&mut self, &mut self,
tree_rank_in_parent: usize, tree_rank_in_parent: usize,
f: impl FnOnce(&mut Self) -> Vec<Fragment>, f: impl FnOnce(&mut Self) -> Vec<Fragment>,
) -> Vec<Fragment> { ) -> Vec<Fragment> {
let abspos_so_far = self.abspos.len(); let so_far = self.boxes.len();
let fragments = f(self); let fragments = f(self);
adjust_static_positions( adjust_static_positions(
&mut self.abspos[abspos_so_far..], &mut self.boxes[so_far..],
&fragments, &fragments,
tree_rank_in_parent, tree_rank_in_parent,
); );
fragments fragments
} }
pub(crate) fn layout_abspos( pub(crate) fn layout_in_initial_containing_block(
self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
initial_containing_block: &DefiniteContainingBlock,
fragments: &mut Vec<Fragment>, fragments: &mut Vec<Fragment>,
content_rect_size: &Vec2<Length>,
padding: &Sides<Length>,
style: &ComputedValues,
) { ) {
if self.abspos.is_empty() { CollectedAbsolutelyPositionedBox::layout_many(
return; 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 { let padding_rect = Rect {
size: content_rect_size.clone(), size: positioned_box_fragment.content_rect.size.clone(),
// Ignore the content rects position in its own containing block: // Ignore the content rects position in its own containing block:
start_corner: Vec2::zero(), start_corner: Vec2::zero(),
} }
.inflate(&padding); .inflate(&positioned_box_fragment.padding);
let containing_block = DefiniteContainingBlock { let containing_block = DefiniteContainingBlock {
size: padding_rect.size.clone(), size: padding_rect.size.clone(),
style, style: &positioned_box_fragment.style,
}; };
let map = let mut children = Vec::new();
|a: &CollectedAbsolutelyPositionedBox| a.layout(layout_context, &containing_block); CollectedAbsolutelyPositionedBox::layout_many(
let children = if layout_context.use_rayon { layout_context,
self.abspos.par_iter().map(map).collect() &self.boxes,
} else { &mut children,
self.abspos.iter().map(map).collect() &containing_block,
}; );
fragments.push(Fragment::Anonymous(AnonymousFragment { positioned_box_fragment
.children
.push(Fragment::Anonymous(AnonymousFragment {
children, children,
rect: padding_rect, rect: padding_rect,
mode: style.writing_mode, mode: positioned_box_fragment.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( pub(crate) fn layout(
&self, &self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block: &DefiniteContainingBlock, containing_block: &DefiniteContainingBlock,
) -> Fragment { ) -> BoxFragment {
let style = &self.absolutely_positioned_box.contents.style; let style = &self.absolutely_positioned_box.contents.style;
let cbis = containing_block.size.inline; let cbis = containing_block.size.inline;
let cbbs = containing_block.size.block; let cbbs = containing_block.size.block;
@ -249,15 +318,16 @@ impl CollectedAbsolutelyPositionedBox<'_> {
block_end: block_axis.margin_end, block_end: block_axis.margin_end,
}; };
let mut positioning_context = PositioningContext { abspos: Vec::new() }; PositioningContext::for_positioned(layout_context, |positioning_context| {
let (size, mut fragments) = match self.absolutely_positioned_box.contents.as_replaced() { let size;
let fragments;
match self.absolutely_positioned_box.contents.as_replaced() {
Ok(replaced) => { Ok(replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width // https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-height // https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
let style = &self.absolutely_positioned_box.contents.style; let style = &self.absolutely_positioned_box.contents.style;
let size = replaced_used_size.unwrap(); size = replaced_used_size.unwrap();
let fragments = replaced.make_fragments(style, size.clone()); fragments = replaced.make_fragments(style, size.clone());
(size, fragments)
}, },
Err(non_replaced) => { Err(non_replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
@ -289,18 +359,18 @@ impl CollectedAbsolutelyPositionedBox<'_> {
let dummy_tree_rank = 0; let dummy_tree_rank = 0;
let independent_layout = non_replaced.layout( let independent_layout = non_replaced.layout(
layout_context, layout_context,
&mut positioning_context, positioning_context,
&containing_block_for_children, &containing_block_for_children,
dummy_tree_rank, dummy_tree_rank,
); );
let size = Vec2 { size = Vec2 {
inline: inline_size, inline: inline_size,
block: block_axis block: block_axis
.size .size
.auto_is(|| independent_layout.content_block_size), .auto_is(|| independent_layout.content_block_size),
}; };
(size, independent_layout.fragments) fragments = independent_layout.fragments
}, },
}; };
@ -321,15 +391,7 @@ impl CollectedAbsolutelyPositionedBox<'_> {
size, size,
}; };
positioning_context.layout_abspos( BoxFragment {
layout_context,
&mut fragments,
&content_rect.size,
&padding,
style,
);
Fragment::Box(BoxFragment {
style: style.clone(), style: style.clone(),
children: fragments, children: fragments,
content_rect, content_rect,
@ -337,6 +399,7 @@ impl CollectedAbsolutelyPositionedBox<'_> {
border, border,
margin, margin,
block_margins_collapsed_with_children: CollapsedBlockMargins::zero(), 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)
}
}