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,13 +430,10 @@ 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() };
positioning_context.for_maybe_position_relative(layout_context, style, |positioning_context| {
let mut flow_layout = layout_contents(
if style.get_box().position == Position::Relative {
&mut nested_positioning_context
} else {
positioning_context
},
positioning_context,
&containing_block_for_children,
this_start_margin_can_collapse_with_children,
);
@ -465,7 +459,8 @@ fn layout_in_flow_non_replaced_block_level<'a>(
.end
.adjoin_assign(&flow_layout.collapsible_margins_in_children.end);
} 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 =
this_start_margin_can_collapse_with_children.0 &&
@ -489,15 +484,6 @@ fn layout_in_flow_non_replaced_block_level<'a>(
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,
@ -507,6 +493,7 @@ fn layout_in_flow_non_replaced_block_level<'a>(
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: content_rect_size.clone(),
size: positioned_box_fragment.content_rect.size.clone(),
// Ignore the content rects position in its own containing block:
start_corner: Vec2::zero(),
}
.inflate(&padding);
.inflate(&positioned_box_fragment.padding);
let containing_block = DefiniteContainingBlock {
size: padding_rect.size.clone(),
style,
style: &positioned_box_fragment.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 {
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: style.writing_mode,
mode: positioned_box_fragment.style.writing_mode,
}))
}
}
}
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,
))
}))
}
}
impl CollectedAbsolutelyPositionedBox<'_> {
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,15 +318,16 @@ 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() {
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;
let size = replaced_used_size.unwrap();
let fragments = replaced.make_fragments(style, size.clone());
(size, fragments)
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
@ -289,18 +359,18 @@ impl CollectedAbsolutelyPositionedBox<'_> {
let dummy_tree_rank = 0;
let independent_layout = non_replaced.layout(
layout_context,
&mut positioning_context,
positioning_context,
&containing_block_for_children,
dummy_tree_rank,
);
let size = Vec2 {
size = Vec2 {
inline: inline_size,
block: block_axis
.size
.auto_is(|| independent_layout.content_block_size),
};
(size, independent_layout.fragments)
fragments = independent_layout.fragments
},
};
@ -321,15 +391,7 @@ impl CollectedAbsolutelyPositionedBox<'_> {
size,
};
positioning_context.layout_abspos(
layout_context,
&mut fragments,
&content_rect.size,
&padding,
style,
);
Fragment::Box(BoxFragment {
BoxFragment {
style: style.clone(),
children: fragments,
content_rect,
@ -337,6 +399,7 @@ impl CollectedAbsolutelyPositionedBox<'_> {
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)
}
}