Auto merge of #25273 - servo:fixedpos, r=nox

Layout `position: fixed` in the initial containing block
This commit is contained in:
bors-servo 2019-12-13 10:50:58 -05:00 committed by GitHub
commit 762e67f486
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 552 additions and 404 deletions

4
Cargo.lock generated
View file

@ -4219,9 +4219,9 @@ dependencies = [
[[package]] [[package]]
name = "rayon_croissant" name = "rayon_croissant"
version = "0.1.1" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b725e815f3aa08718063883a75003336889debafe2f8fa67fbe91563ddc4efa" checksum = "3e4aafda434bd10fec689858e2b1d713d0b784b1e60df3761ac8fa727d7e8e27"
dependencies = [ dependencies = [
"moite_moite", "moite_moite",
"rayon", "rayon",

View file

@ -25,7 +25,7 @@ msg = {path = "../msg"}
net_traits = {path = "../net_traits"} net_traits = {path = "../net_traits"}
range = {path = "../range"} range = {path = "../range"}
rayon = "1" rayon = "1"
rayon_croissant = "0.1.1" rayon_croissant = "0.2.0"
script_layout_interface = {path = "../script_layout_interface"} script_layout_interface = {path = "../script_layout_interface"}
script_traits = {path = "../script_traits"} script_traits = {path = "../script_traits"}
serde = "1.0" serde = "1.0"

View file

@ -172,14 +172,6 @@ impl BlockContainer {
contains_floats: ContainsFloats, contains_floats: ContainsFloats,
outer_content_sizes_of_children: ContentSizes, outer_content_sizes_of_children: ContentSizes,
} }
impl Default for Accumulator {
fn default() -> Self {
Self {
contains_floats: ContainsFloats::No,
outer_content_sizes_of_children: ContentSizes::zero(),
}
}
}
let mut acc = Accumulator { let mut acc = Accumulator {
contains_floats: builder.contains_floats, contains_floats: builder.contains_floats,
outer_content_sizes_of_children: ContentSizes::zero(), outer_content_sizes_of_children: ContentSizes::zero(),
@ -199,13 +191,21 @@ impl BlockContainer {
builder builder
.block_level_boxes .block_level_boxes
.into_par_iter() .into_par_iter()
.mapfold_reduce_into(&mut acc, mapfold, |left, right| { .mapfold_reduce_into(
left.contains_floats |= right.contains_floats; &mut acc,
if content_sizes.requests_inline() { mapfold,
left.outer_content_sizes_of_children || Accumulator {
.max_assign(&right.outer_content_sizes_of_children) contains_floats: ContainsFloats::No,
} outer_content_sizes_of_children: ContentSizes::zero(),
}) },
|left, right| {
left.contains_floats |= right.contains_floats;
if content_sizes.requests_inline() {
left.outer_content_sizes_of_children
.max_assign(&right.outer_content_sizes_of_children)
}
},
)
.collect() .collect()
} else { } else {
builder builder

View file

@ -9,10 +9,10 @@ use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragments::CollapsedBlockMargins; use crate::fragments::CollapsedBlockMargins;
use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, TextFragment}; use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, TextFragment};
use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment}; use crate::positioned::{relative_adjustement, AbsolutelyPositionedBox, PositioningContext};
use crate::sizing::ContentSizes; use crate::sizing::ContentSizes;
use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside}; use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside};
use crate::{relative_adjustement, ContainingBlock}; use crate::ContainingBlock;
use app_units::Au; use app_units::Au;
use gfx::text::text_run::GlyphRun; use gfx::text::text_run::GlyphRun;
use servo_arc::Arc; use servo_arc::Arc;
@ -68,9 +68,9 @@ struct PartialInlineBoxFragment<'box_tree> {
parent_nesting_level: InlineNestingLevelState<'box_tree>, parent_nesting_level: InlineNestingLevelState<'box_tree>,
} }
struct InlineFormattingContextState<'box_tree, 'a> { struct InlineFormattingContextState<'box_tree, 'a, 'b> {
absolutely_positioned_fragments: &'a mut Vec<AbsolutelyPositionedFragment<'box_tree>>, positioning_context: &'a mut PositioningContext<'box_tree>,
containing_block: &'a ContainingBlock<'a>, containing_block: &'b ContainingBlock<'b>,
lines: Lines, lines: Lines,
inline_position: Length, inline_position: Length,
partial_inline_boxes_stack: Vec<PartialInlineBoxFragment<'box_tree>>, partial_inline_boxes_stack: Vec<PartialInlineBoxFragment<'box_tree>>,
@ -195,12 +195,12 @@ impl InlineFormattingContext {
pub(super) fn layout<'a>( pub(super) fn layout<'a>(
&'a self, &'a self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
positioning_context: &mut PositioningContext<'a>,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
tree_rank: usize, tree_rank: usize,
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
) -> FlowLayout { ) -> FlowLayout {
let mut ifc = InlineFormattingContextState { let mut ifc = InlineFormattingContextState {
absolutely_positioned_fragments, positioning_context,
containing_block, containing_block,
partial_inline_boxes_stack: Vec::new(), partial_inline_boxes_stack: Vec::new(),
lines: Lines { lines: Lines {
@ -244,7 +244,7 @@ impl InlineFormattingContext {
panic!("display:none does not generate an abspos box") panic!("display:none does not generate an abspos box")
}, },
}; };
ifc.absolutely_positioned_fragments ifc.positioning_context
.push(box_.layout(initial_start_corner, tree_rank)); .push(box_.layout(initial_start_corner, tree_rank));
}, },
InlineLevelBox::OutOfFlowFloatBox(_box_) => { InlineLevelBox::OutOfFlowFloatBox(_box_) => {
@ -346,7 +346,7 @@ impl Lines {
impl InlineBox { impl InlineBox {
fn start_layout<'box_tree>( fn start_layout<'box_tree>(
&'box_tree self, &'box_tree self,
ifc: &mut InlineFormattingContextState<'box_tree, '_>, ifc: &mut InlineFormattingContextState<'box_tree, '_, '_>,
) -> PartialInlineBoxFragment<'box_tree> { ) -> PartialInlineBoxFragment<'box_tree> {
let style = self.style.clone(); let style = self.style.clone();
let cbis = ifc.containing_block.inline_size; let cbis = ifc.containing_block.inline_size;
@ -367,11 +367,9 @@ impl InlineBox {
block: padding.block_start + border.block_start + margin.block_start, block: padding.block_start + border.block_start + margin.block_start,
inline: ifc.inline_position - ifc.current_nesting_level.inline_start, inline: ifc.inline_position - ifc.current_nesting_level.inline_start,
}; };
start_corner += &relative_adjustement( if style.clone_position().is_relative() {
&style, start_corner += &relative_adjustement(&style, ifc.containing_block)
ifc.containing_block.inline_size, }
ifc.containing_block.block_size,
);
PartialInlineBoxFragment { PartialInlineBoxFragment {
style, style,
start_corner, start_corner,
@ -440,7 +438,7 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
fn layout_atomic<'box_tree>( fn layout_atomic<'box_tree>(
layout_context: &LayoutContext, layout_context: &LayoutContext,
ifc: &mut InlineFormattingContextState<'box_tree, '_>, ifc: &mut InlineFormattingContextState<'box_tree, '_, '_>,
atomic: &'box_tree IndependentFormattingContext, atomic: &'box_tree IndependentFormattingContext,
) { ) {
let cbis = ifc.containing_block.inline_size; let cbis = ifc.containing_block.inline_size;
@ -457,11 +455,9 @@ fn layout_atomic<'box_tree>(
block: pbm.block_start, block: pbm.block_start,
inline: ifc.inline_position - ifc.current_nesting_level.inline_start, inline: ifc.inline_position - ifc.current_nesting_level.inline_start,
}; };
start_corner += &relative_adjustement( if atomic.style.clone_position().is_relative() {
&atomic.style, start_corner += &relative_adjustement(&atomic.style, ifc.containing_block)
ifc.containing_block.inline_size, }
ifc.containing_block.block_size,
);
let fragment = match atomic.as_replaced() { let fragment = match atomic.as_replaced() {
Ok(replaced) => { Ok(replaced) => {
@ -521,9 +517,9 @@ fn layout_atomic<'box_tree>(
// FIXME: Do we need to call `adjust_static_positions` somewhere near here? // FIXME: Do we need to call `adjust_static_positions` somewhere near here?
let independent_layout = non_replaced.layout( let independent_layout = non_replaced.layout(
layout_context, layout_context,
ifc.positioning_context,
&containing_block_for_children, &containing_block_for_children,
dummy_tree_rank, dummy_tree_rank,
ifc.absolutely_positioned_fragments,
); );
// https://drafts.csswg.org/css2/visudet.html#block-root-margin // https://drafts.csswg.org/css2/visudet.html#block-root-margin

View file

@ -7,19 +7,17 @@
use crate::context::LayoutContext; use crate::context::LayoutContext;
use crate::flow::float::{FloatBox, FloatContext}; use crate::flow::float::{FloatBox, FloatContext};
use crate::flow::inline::InlineFormattingContext; use crate::flow::inline::InlineFormattingContext;
use crate::formatting_contexts::{IndependentFormattingContext, IndependentLayout}; use crate::formatting_contexts::{IndependentFormattingContext, IndependentLayout, NonReplacedIFC};
use crate::fragments::{AnonymousFragment, BoxFragment, Fragment}; use crate::fragments::{AnonymousFragment, BoxFragment, Fragment};
use crate::fragments::{CollapsedBlockMargins, CollapsedMargin}; use crate::fragments::{CollapsedBlockMargins, CollapsedMargin};
use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::positioned::adjust_static_positions; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment};
use crate::replaced::ReplacedContent; use crate::replaced::ReplacedContent;
use crate::style_ext::ComputedValuesExt; use crate::style_ext::ComputedValuesExt;
use crate::{relative_adjustement, ContainingBlock}; use crate::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;
@ -67,9 +65,9 @@ impl BlockFormattingContext {
pub(super) fn layout<'a>( pub(super) fn layout<'a>(
&'a self, &'a self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
positioning_context: &mut PositioningContext<'a>,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
tree_rank: usize, tree_rank: usize,
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
) -> IndependentLayout { ) -> IndependentLayout {
let mut float_context; let mut float_context;
let float_context = if self.contains_floats { let float_context = if self.contains_floats {
@ -80,9 +78,9 @@ impl BlockFormattingContext {
}; };
let flow_layout = self.contents.layout( let flow_layout = self.contents.layout(
layout_context, layout_context,
positioning_context,
containing_block, containing_block,
tree_rank, tree_rank,
absolutely_positioned_fragments,
float_context, float_context,
CollapsibleWithParentStartMargin(false), CollapsibleWithParentStartMargin(false),
); );
@ -103,27 +101,27 @@ impl BlockContainer {
fn layout<'a>( fn layout<'a>(
&'a self, &'a self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
positioning_context: &mut PositioningContext<'a>,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
tree_rank: usize, tree_rank: usize,
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
float_context: Option<&mut FloatContext>, float_context: Option<&mut FloatContext>,
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin, collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
) -> FlowLayout { ) -> FlowLayout {
match self { match self {
BlockContainer::BlockLevelBoxes(child_boxes) => layout_block_level_children( BlockContainer::BlockLevelBoxes(child_boxes) => layout_block_level_children(
layout_context, layout_context,
positioning_context,
child_boxes, child_boxes,
containing_block, containing_block,
tree_rank, tree_rank,
absolutely_positioned_fragments,
float_context, float_context,
collapsible_with_parent_start_margin, collapsible_with_parent_start_margin,
), ),
BlockContainer::InlineFormattingContext(ifc) => ifc.layout( BlockContainer::InlineFormattingContext(ifc) => ifc.layout(
layout_context, layout_context,
positioning_context,
containing_block, containing_block,
tree_rank, tree_rank,
absolutely_positioned_fragments,
), ),
} }
} }
@ -131,10 +129,10 @@ impl BlockContainer {
fn layout_block_level_children<'a>( fn layout_block_level_children<'a>(
layout_context: &LayoutContext, layout_context: &LayoutContext,
positioning_context: &mut PositioningContext<'a>,
child_boxes: &'a [Arc<BlockLevelBox>], child_boxes: &'a [Arc<BlockLevelBox>],
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
tree_rank: usize, tree_rank: usize,
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
mut float_context: Option<&mut FloatContext>, mut float_context: Option<&mut FloatContext>,
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin, collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
) -> FlowLayout { ) -> FlowLayout {
@ -194,7 +192,6 @@ fn layout_block_level_children<'a>(
current_block_direction_position: Length, current_block_direction_position: Length,
} }
let abspos_so_far = absolutely_positioned_fragments.len();
let mut placement_state = PlacementState { let mut placement_state = PlacementState {
next_in_flow_margin_collapses_with_parent_start_margin: next_in_flow_margin_collapses_with_parent_start_margin:
collapsible_with_parent_start_margin.0, collapsible_with_parent_start_margin.0,
@ -202,56 +199,52 @@ fn layout_block_level_children<'a>(
current_margin: CollapsedMargin::zero(), current_margin: CollapsedMargin::zero(),
current_block_direction_position: Length::zero(), current_block_direction_position: Length::zero(),
}; };
let mut fragments: Vec<_>; let fragments = positioning_context.adjust_static_positions(tree_rank, |positioning_context| {
if float_context.is_some() || !layout_context.use_rayon { if float_context.is_some() || !layout_context.use_rayon {
// Because floats are involved, we do layout for this block formatting context // Because floats are involved, we do layout for this block formatting context
// in tree order without parallelism. This enables mutable access // in tree order without parallelism. This enables mutable access
// to a `FloatContext` that tracks every float encountered so far (again in tree order). // to a `FloatContext` that tracks every float encountered so far (again in tree order).
fragments = child_boxes child_boxes
.iter() .iter()
.enumerate() .enumerate()
.map(|(tree_rank, box_)| { .map(|(tree_rank, box_)| {
let mut fragment = box_.layout( let mut fragment = box_.layout(
layout_context,
containing_block,
tree_rank,
absolutely_positioned_fragments,
float_context.as_mut().map(|c| &mut **c),
);
place_block_level_fragment(&mut fragment, &mut placement_state);
fragment
})
.collect()
} else {
fragments = child_boxes
.par_iter()
.enumerate()
.mapfold_reduce_into(
absolutely_positioned_fragments,
|abspos_fragments, (tree_rank, box_)| {
box_.layout(
layout_context, layout_context,
positioning_context,
containing_block, containing_block,
tree_rank, tree_rank,
abspos_fragments, float_context.as_mut().map(|c| &mut **c),
/* float_context = */ None, );
) place_block_level_fragment(&mut fragment, &mut placement_state);
}, fragment
|left_abspos_fragments, mut right_abspos_fragments| { })
left_abspos_fragments.append(&mut right_abspos_fragments); .collect()
}, } else {
) let has_positioned_ancestor = positioning_context.has_positioned_ancestor();
.collect(); let mut fragments = child_boxes
for fragment in &mut fragments { .par_iter()
place_block_level_fragment(fragment, &mut placement_state) .enumerate()
.mapfold_reduce_into(
positioning_context,
|positioning_context, (tree_rank, box_)| {
box_.layout(
layout_context,
positioning_context,
containing_block,
tree_rank,
/* float_context = */ None,
)
},
|| PositioningContext::new_for_rayon(has_positioned_ancestor),
PositioningContext::append,
)
.collect();
for fragment in &mut fragments {
place_block_level_fragment(fragment, &mut placement_state)
}
fragments
} }
} });
adjust_static_positions(
&mut absolutely_positioned_fragments[abspos_so_far..],
&mut fragments,
tree_rank,
);
FlowLayout { FlowLayout {
fragments, fragments,
@ -269,65 +262,63 @@ impl BlockLevelBox {
fn layout<'a>( fn layout<'a>(
&'a self, &'a self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
positioning_context: &mut PositioningContext<'a>,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
tree_rank: usize, tree_rank: usize,
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
float_context: Option<&mut FloatContext>, float_context: Option<&mut FloatContext>,
) -> Fragment { ) -> Fragment {
match self { match self {
BlockLevelBox::SameFormattingContextBlock { style, contents } => { BlockLevelBox::SameFormattingContextBlock { style, contents } => {
Fragment::Box(layout_in_flow_non_replaced_block_level( Fragment::Box(positioning_context.for_maybe_position_relative(
layout_context, layout_context,
containing_block, containing_block,
absolutely_positioned_fragments,
style, style,
BlockLevelKind::SameFormattingContextBlock, |positioning_context| {
|containing_block, nested_abspos, collapsible_with_parent_start_margin| { layout_in_flow_non_replaced_block_level(
contents.layout(
layout_context, layout_context,
positioning_context,
containing_block, containing_block,
style,
NonReplacedContents::SameFormattingContextBlock(contents),
tree_rank, tree_rank,
nested_abspos,
float_context, float_context,
collapsible_with_parent_start_margin,
) )
}, },
)) ))
}, },
BlockLevelBox::Independent(contents) => match contents.as_replaced() { BlockLevelBox::Independent(contents) => {
Ok(replaced) => Fragment::Box(layout_in_flow_replaced_block_level( Fragment::Box(positioning_context.for_maybe_position_relative(
containing_block,
&contents.style,
replaced,
)),
Err(non_replaced) => Fragment::Box(layout_in_flow_non_replaced_block_level(
layout_context, layout_context,
containing_block, containing_block,
absolutely_positioned_fragments,
&contents.style, &contents.style,
BlockLevelKind::EstablishesAnIndependentFormattingContext, |positioning_context| match contents.as_replaced() {
|containing_block, nested_abspos, _| { Ok(replaced) => layout_in_flow_replaced_block_level(
let independent_layout = non_replaced.layout(
layout_context,
containing_block, containing_block,
&contents.style,
replaced,
),
Err(non_replaced) => layout_in_flow_non_replaced_block_level(
layout_context,
positioning_context,
containing_block,
&contents.style,
NonReplacedContents::EstablishesAnIndependentFormattingContext(
non_replaced,
),
tree_rank, tree_rank,
nested_abspos, float_context,
); ),
FlowLayout {
fragments: independent_layout.fragments,
content_block_size: independent_layout.content_block_size,
collapsible_margins_in_children: CollapsedBlockMargins::zero(),
}
}, },
)), ))
}, },
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => { BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
absolutely_positioned_fragments.push(box_.layout(Vec2::zero(), tree_rank)); positioning_context.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,
)) ))
}, },
BlockLevelBox::OutOfFlowFloatBox(_box_) => { BlockLevelBox::OutOfFlowFloatBox(_box_) => {
// FIXME: call for_maybe_position_relative here
// TODO // TODO
Fragment::Anonymous(AnonymousFragment::no_op( Fragment::Anonymous(AnonymousFragment::no_op(
containing_block.style.writing_mode, containing_block.style.writing_mode,
@ -337,25 +328,21 @@ impl BlockLevelBox {
} }
} }
#[derive(PartialEq)] enum NonReplacedContents<'a> {
enum BlockLevelKind { SameFormattingContextBlock(&'a BlockContainer),
SameFormattingContextBlock, EstablishesAnIndependentFormattingContext(NonReplacedIFC<'a>),
EstablishesAnIndependentFormattingContext,
} }
/// https://drafts.csswg.org/css2/visudet.html#blockwidth /// https://drafts.csswg.org/css2/visudet.html#blockwidth
/// https://drafts.csswg.org/css2/visudet.html#normal-block /// https://drafts.csswg.org/css2/visudet.html#normal-block
fn layout_in_flow_non_replaced_block_level<'a>( fn layout_in_flow_non_replaced_block_level<'a>(
layout_context: &LayoutContext, layout_context: &LayoutContext,
positioning_context: &mut PositioningContext<'a>,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
style: &Arc<ComputedValues>, style: &Arc<ComputedValues>,
block_level_kind: BlockLevelKind, block_level_kind: NonReplacedContents<'a>,
layout_contents: impl FnOnce( tree_rank: usize,
&ContainingBlock, float_context: Option<&mut FloatContext>,
&mut Vec<AbsolutelyPositionedFragment<'a>>,
CollapsibleWithParentStartMargin,
) -> FlowLayout,
) -> BoxFragment { ) -> BoxFragment {
let cbis = containing_block.inline_size; let cbis = containing_block.inline_size;
let padding = style.padding().percentages_relative_to(cbis); let padding = style.padding().percentages_relative_to(cbis);
@ -428,83 +415,81 @@ fn layout_in_flow_non_replaced_block_level<'a>(
"Mixed writing modes are not supported yet" "Mixed writing modes are not supported yet"
); );
let this_start_margin_can_collapse_with_children = CollapsibleWithParentStartMargin(
block_level_kind == BlockLevelKind::SameFormattingContextBlock &&
pb.block_start == Length::zero(),
);
let this_end_margin_can_collapse_with_children = block_size == LengthOrAuto::Auto &&
min_box_size.block == Length::zero() &&
pb.block_end == Length::zero() &&
block_level_kind == BlockLevelKind::SameFormattingContextBlock;
let mut nested_abspos = vec![];
let mut flow_layout = layout_contents(
&containing_block_for_children,
if style.get_box().position == Position::Relative {
&mut nested_abspos
} else {
absolutely_positioned_fragments
},
this_start_margin_can_collapse_with_children,
);
let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin); 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 let fragments;
.start let mut content_block_size;
.adjoin_assign(&flow_layout.collapsible_margins_in_children.start); match block_level_kind {
if flow_layout NonReplacedContents::SameFormattingContextBlock(contents) => {
.collapsible_margins_in_children let this_start_margin_can_collapse_with_children = pb.block_start == Length::zero();
.collapsed_through let this_end_margin_can_collapse_with_children = pb.block_end == Length::zero() &&
{ block_size == LengthOrAuto::Auto &&
block_margins_collapsed_with_children min_box_size.block == Length::zero();
.start
.adjoin_assign(&std::mem::replace( let flow_layout = contents.layout(
&mut flow_layout.collapsible_margins_in_children.end, layout_context,
CollapsedMargin::zero(), positioning_context,
)); &containing_block_for_children,
} tree_rank,
} float_context,
if this_end_margin_can_collapse_with_children { CollapsibleWithParentStartMargin(this_start_margin_can_collapse_with_children),
block_margins_collapsed_with_children );
.end fragments = flow_layout.fragments;
.adjoin_assign(&flow_layout.collapsible_margins_in_children.end); content_block_size = flow_layout.content_block_size;
} else { let mut collapsible_margins_in_children = flow_layout.collapsible_margins_in_children;
flow_layout.content_block_size += flow_layout.collapsible_margins_in_children.end.solve();
} if this_start_margin_can_collapse_with_children {
block_margins_collapsed_with_children.collapsed_through = block_margins_collapsed_with_children
this_start_margin_can_collapse_with_children.0 && .start
this_end_margin_can_collapse_with_children && .adjoin_assign(&collapsible_margins_in_children.start);
flow_layout if collapsible_margins_in_children.collapsed_through {
.collapsible_margins_in_children block_margins_collapsed_with_children
.collapsed_through; .start
let relative_adjustement = relative_adjustement(style, inline_size, block_size); .adjoin_assign(&std::mem::replace(
&mut collapsible_margins_in_children.end,
CollapsedMargin::zero(),
));
}
}
if this_end_margin_can_collapse_with_children {
block_margins_collapsed_with_children
.end
.adjoin_assign(&collapsible_margins_in_children.end);
} else {
content_block_size += collapsible_margins_in_children.end.solve();
}
block_margins_collapsed_with_children.collapsed_through =
this_start_margin_can_collapse_with_children &&
this_end_margin_can_collapse_with_children &&
collapsible_margins_in_children.collapsed_through;
},
NonReplacedContents::EstablishesAnIndependentFormattingContext(non_replaced) => {
let independent_layout = non_replaced.layout(
layout_context,
positioning_context,
&containing_block_for_children,
tree_rank,
);
fragments = independent_layout.fragments;
content_block_size = independent_layout.content_block_size;
},
};
let block_size = block_size.auto_is(|| { let block_size = block_size.auto_is(|| {
flow_layout content_block_size.clamp_between_extremums(min_box_size.block, max_box_size.block)
.content_block_size
.clamp_between_extremums(min_box_size.block, max_box_size.block)
}); });
let content_rect = Rect { let content_rect = Rect {
start_corner: Vec2 { start_corner: Vec2 {
block: pb.block_start + relative_adjustement.block, block: pb.block_start,
inline: pb.inline_start + relative_adjustement.inline + margin.inline_start, inline: pb.inline_start,
}, },
size: Vec2 { size: Vec2 {
block: block_size, block: block_size,
inline: inline_size, inline: inline_size,
}, },
}; };
if style.get_box().position == Position::Relative {
AbsolutelyPositionedFragment::in_positioned_containing_block(
layout_context,
&nested_abspos,
&mut flow_layout.fragments,
&content_rect.size,
&padding,
style,
)
}
BoxFragment { BoxFragment {
style: style.clone(), style: style.clone(),
children: flow_layout.fragments, children: fragments,
content_rect, content_rect,
padding, padding,
border, border,
@ -543,15 +528,10 @@ fn layout_in_flow_replaced_block_level<'a>(
block_end: computed_margin.block_end.auto_is(Length::zero), block_end: computed_margin.block_end.auto_is(Length::zero),
}; };
let fragments = replaced.make_fragments(style, size.clone()); let fragments = replaced.make_fragments(style, size.clone());
let relative_adjustement = relative_adjustement(
style,
size.inline,
LengthOrAuto::LengthPercentage(size.block),
);
let content_rect = Rect { let content_rect = Rect {
start_corner: Vec2 { start_corner: Vec2 {
block: pb.block_start + relative_adjustement.block, block: pb.block_start,
inline: pb.inline_start + relative_adjustement.inline + margin.inline_start, inline: pb.inline_start + margin.inline_start,
}, },
size, size,
}; };

View file

@ -12,12 +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, AbsolutelyPositionedFragment}; use crate::positioned::AbsolutelyPositionedBox;
use crate::positioned::PositioningContext;
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;
@ -110,25 +110,19 @@ impl BoxTreeRoot {
}; };
let dummy_tree_rank = 0; let dummy_tree_rank = 0;
let mut absolutely_positioned_fragments = vec![]; let mut positioning_context = PositioningContext::new_for_initial_containing_block();
let mut independent_layout = self.0.layout( let mut independent_layout = self.0.layout(
layout_context, layout_context,
&mut positioning_context,
&(&initial_containing_block).into(), &(&initial_containing_block).into(),
dummy_tree_rank, dummy_tree_rank,
&mut absolutely_positioned_fragments,
); );
let map = positioning_context.layout_in_initial_containing_block(
|a: &AbsolutelyPositionedFragment| a.layout(layout_context, &initial_containing_block); layout_context,
if layout_context.use_rayon { &initial_containing_block,
independent_layout &mut independent_layout.fragments,
.fragments );
.par_extend(absolutely_positioned_fragments.par_iter().map(map))
} else {
independent_layout
.fragments
.extend(absolutely_positioned_fragments.iter().map(map))
}
FragmentTreeRoot(independent_layout.fragments) FragmentTreeRoot(independent_layout.fragments)
} }

View file

@ -6,7 +6,7 @@ use crate::context::LayoutContext;
use crate::dom_traversal::{Contents, NodeExt}; use crate::dom_traversal::{Contents, NodeExt};
use crate::flow::BlockFormattingContext; use crate::flow::BlockFormattingContext;
use crate::fragments::Fragment; use crate::fragments::Fragment;
use crate::positioned::AbsolutelyPositionedFragment; use crate::positioned::PositioningContext;
use crate::replaced::ReplacedContent; use crate::replaced::ReplacedContent;
use crate::sizing::{BoxContentSizes, ContentSizesRequest}; use crate::sizing::{BoxContentSizes, ContentSizesRequest};
use crate::style_ext::DisplayInside; use crate::style_ext::DisplayInside;
@ -101,16 +101,16 @@ impl<'a> NonReplacedIFC<'a> {
pub fn layout( pub fn layout(
&self, &self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
positioning_context: &mut PositioningContext<'a>,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
tree_rank: usize, tree_rank: usize,
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
) -> IndependentLayout { ) -> IndependentLayout {
match &self.0 { match &self.0 {
NonReplacedIFCKind::Flow(bfc) => bfc.layout( NonReplacedIFCKind::Flow(bfc) => bfc.layout(
layout_context, layout_context,
positioning_context,
containing_block, containing_block,
tree_rank, tree_rank,
absolutely_positioned_fragments,
), ),
} }
} }

View file

@ -27,11 +27,8 @@ pub mod wrapper;
pub use flow::{BoxTreeRoot, FragmentTreeRoot}; pub use flow::{BoxTreeRoot, FragmentTreeRoot};
use crate::geom::flow_relative::Vec2; use crate::geom::flow_relative::Vec2;
use crate::style_ext::ComputedValuesExt;
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;
struct ContainingBlock<'a> { struct ContainingBlock<'a> {
inline_size: Length, inline_size: Length,
@ -53,30 +50,3 @@ impl<'a> From<&'_ DefiniteContainingBlock<'a>> for ContainingBlock<'a> {
} }
} }
} }
/// https://drafts.csswg.org/css2/visuren.html#relative-positioning
fn relative_adjustement(
style: &ComputedValues,
inline_size: Length,
block_size: LengthOrAuto,
) -> Vec2<Length> {
if style.get_box().position != Position::Relative {
return Vec2::zero();
}
fn adjust(start: LengthOrAuto, end: LengthOrAuto) -> Length {
match (start, end) {
(LengthOrAuto::Auto, LengthOrAuto::Auto) => Length::zero(),
(LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => -end,
(LengthOrAuto::LengthPercentage(start), _) => start,
}
}
let block_size = block_size.auto_is(Length::zero);
let box_offsets = style.box_offsets().map_inline_and_block_axes(
|v| v.percentage_relative_to(inline_size),
|v| v.percentage_relative_to(block_size),
);
Vec2 {
inline: adjust(box_offsets.inline_start, box_offsets.inline_end),
block: adjust(box_offsets.block_start, box_offsets.block_end),
}
}

View file

@ -10,8 +10,10 @@ 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};
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, LengthPercentage, LengthPercentageOrAuto}; use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
use style::Zero; use style::Zero;
@ -21,9 +23,14 @@ pub(crate) struct AbsolutelyPositionedBox {
pub contents: IndependentFormattingContext, pub contents: IndependentFormattingContext,
} }
pub(crate) struct PositioningContext<'box_tree> {
for_nearest_positioned_ancestor: Option<Vec<HoistedAbsolutelyPositionedBox<'box_tree>>>,
for_initial_containing_block: Vec<HoistedAbsolutelyPositionedBox<'box_tree>>,
}
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct AbsolutelyPositionedFragment<'box_> { pub(crate) struct HoistedAbsolutelyPositionedBox<'box_tree> {
absolutely_positioned_box: &'box_ AbsolutelyPositionedBox, absolutely_positioned_box: &'box_tree AbsolutelyPositionedBox,
/// The rank of the child from which this absolutely positioned fragment /// The rank of the child from which this absolutely positioned fragment
/// came from, when doing the layout of a block container. Used to compute /// came from, when doing the layout of a block container. Used to compute
@ -77,11 +84,11 @@ 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,
) -> AbsolutelyPositionedFragment { ) -> HoistedAbsolutelyPositionedBox {
fn absolute_box_offsets( fn absolute_box_offsets(
initial_static_start: Length, initial_static_start: Length,
start: LengthPercentageOrAuto, start: LengthPercentageOrAuto,
@ -98,7 +105,7 @@ impl AbsolutelyPositionedBox {
} }
let box_offsets = self.contents.style.box_offsets(); let box_offsets = self.contents.style.box_offsets();
AbsolutelyPositionedFragment { HoistedAbsolutelyPositionedBox {
absolutely_positioned_box: self, absolutely_positioned_box: self,
tree_rank, tree_rank,
box_offsets: Vec2 { box_offsets: Vec2 {
@ -117,46 +124,217 @@ impl AbsolutelyPositionedBox {
} }
} }
impl<'a> AbsolutelyPositionedFragment<'a> { impl<'box_tree> PositioningContext<'box_tree> {
pub(crate) fn in_positioned_containing_block( pub(crate) fn new_for_initial_containing_block() -> Self {
Self {
for_nearest_positioned_ancestor: None,
for_initial_containing_block: Vec::new(),
}
}
pub(crate) fn new_for_rayon(has_positioned_ancestor: bool) -> Self {
Self {
for_nearest_positioned_ancestor: if has_positioned_ancestor {
Some(Vec::new())
} else {
None
},
for_initial_containing_block: Vec::new(),
}
}
pub(crate) fn has_positioned_ancestor(&self) -> bool {
self.for_nearest_positioned_ancestor.is_some()
}
pub(crate) fn for_maybe_position_relative(
&mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
absolute: &[Self], containing_block: &ContainingBlock,
fragments: &mut Vec<Fragment>,
content_rect_size: &Vec2<Length>,
padding: &Sides<Length>,
style: &ComputedValues, style: &ComputedValues,
) { f: impl FnOnce(&mut Self) -> BoxFragment,
if absolute.is_empty() { ) -> BoxFragment {
return; if style.clone_position() == Position::Relative {
} let mut fragment =
let padding_rect = Rect { // Establing a containing block for absolutely positioned descendants
size: content_rect_size.clone(), Self::for_positioned(layout_context, &mut self.for_initial_containing_block, f);
// Ignore the content rects position in its own containing block:
start_corner: Vec2::zero(), fragment.content_rect.start_corner += &relative_adjustement(style, containing_block);
} fragment
.inflate(&padding);
let containing_block = DefiniteContainingBlock {
size: padding_rect.size.clone(),
style,
};
let map = |a: &AbsolutelyPositionedFragment| a.layout(layout_context, &containing_block);
let children = if layout_context.use_rayon {
absolute.par_iter().map(map).collect()
} else { } else {
absolute.iter().map(map).collect() f(self)
}
}
fn for_positioned(
layout_context: &LayoutContext,
for_initial_containing_block: &mut Vec<HoistedAbsolutelyPositionedBox<'box_tree>>,
f: impl FnOnce(&mut Self) -> BoxFragment,
) -> BoxFragment {
let mut new = Self {
for_nearest_positioned_ancestor: Some(Vec::new()),
for_initial_containing_block: std::mem::take(for_initial_containing_block),
}; };
fragments.push(Fragment::Anonymous(AnonymousFragment { let mut positioned_box_fragment = f(&mut new);
children, new.layout_in_positioned_ancestor(layout_context, &mut positioned_box_fragment);
rect: padding_rect, *for_initial_containing_block = new.for_initial_containing_block;
mode: style.writing_mode, positioned_box_fragment
})) }
pub(crate) fn push(&mut self, box_: HoistedAbsolutelyPositionedBox<'box_tree>) {
if let Some(nearest) = &mut self.for_nearest_positioned_ancestor {
match box_
.absolutely_positioned_box
.contents
.style
.clone_position()
{
Position::Fixed => {}, // fall through
Position::Absolute => return nearest.push(box_),
Position::Static | Position::Relative => unreachable!(),
}
}
self.for_initial_containing_block.push(box_)
}
pub(crate) fn append(&mut self, other: Self) {
vec_append_owned(
&mut self.for_initial_containing_block,
other.for_initial_containing_block,
);
match (
self.for_nearest_positioned_ancestor.as_mut(),
other.for_nearest_positioned_ancestor,
) {
(Some(a), Some(b)) => vec_append_owned(a, b),
(None, None) => {},
_ => unreachable!(),
}
}
pub(crate) fn adjust_static_positions(
&mut self,
tree_rank_in_parent: usize,
f: impl FnOnce(&mut Self) -> Vec<Fragment>,
) -> Vec<Fragment> {
let for_icb_so_far = self.for_initial_containing_block.len();
let for_nearest_so_far = self
.for_nearest_positioned_ancestor
.as_ref()
.map(|v| v.len());
let fragments = f(self);
adjust_static_positions(
&mut self.for_initial_containing_block[for_icb_so_far..],
&fragments,
tree_rank_in_parent,
);
if let Some(nearest) = &mut self.for_nearest_positioned_ancestor {
adjust_static_positions(
&mut nearest[for_nearest_so_far.unwrap()..],
&fragments,
tree_rank_in_parent,
);
}
fragments
}
pub(crate) fn layout_in_initial_containing_block(
&mut self,
layout_context: &LayoutContext,
initial_containing_block: &DefiniteContainingBlock,
fragments: &mut Vec<Fragment>,
) {
debug_assert!(self.for_nearest_positioned_ancestor.is_none());
// Loop because its possible that we discover (the static position of)
// more absolutely-positioned boxes while doing layout for others.
while !self.for_initial_containing_block.is_empty() {
HoistedAbsolutelyPositionedBox::layout_many(
layout_context,
&std::mem::take(&mut self.for_initial_containing_block),
fragments,
&mut self.for_initial_containing_block,
initial_containing_block,
)
}
}
fn layout_in_positioned_ancestor(
&mut self,
layout_context: &LayoutContext,
positioned_box_fragment: &mut BoxFragment,
) {
let for_here = self.for_nearest_positioned_ancestor.take().unwrap();
if !for_here.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();
HoistedAbsolutelyPositionedBox::layout_many(
layout_context,
&for_here,
&mut children,
&mut self.for_initial_containing_block,
&containing_block,
);
positioned_box_fragment
.children
.push(Fragment::Anonymous(AnonymousFragment {
children,
rect: padding_rect,
mode: positioned_box_fragment.style.writing_mode,
}))
}
}
}
impl<'box_tree> HoistedAbsolutelyPositionedBox<'box_tree> {
pub(crate) fn layout_many(
layout_context: &LayoutContext,
boxes: &[Self],
fragments: &mut Vec<Fragment>,
for_initial_containing_block: &mut Vec<HoistedAbsolutelyPositionedBox<'box_tree>>,
containing_block: &DefiniteContainingBlock,
) {
if layout_context.use_rayon {
fragments.par_extend(boxes.par_iter().mapfold_reduce_into(
for_initial_containing_block,
|for_initial_containing_block, box_| {
Fragment::Box(box_.layout(
layout_context,
for_initial_containing_block,
containing_block,
))
},
Vec::new,
vec_append_owned,
))
} else {
fragments.extend(boxes.iter().map(|box_| {
Fragment::Box(box_.layout(
layout_context,
for_initial_containing_block,
containing_block,
))
}))
}
} }
pub(crate) fn layout( pub(crate) fn layout(
&self, &self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
for_initial_containing_block: &mut Vec<HoistedAbsolutelyPositionedBox<'box_tree>>,
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;
@ -216,95 +394,89 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
block_end: block_axis.margin_end, block_end: block_axis.margin_end,
}; };
let mut absolutely_positioned_fragments = Vec::new(); let for_icb = for_initial_containing_block;
let (size, mut fragments) = match self.absolutely_positioned_box.contents.as_replaced() { PositioningContext::for_positioned(layout_context, for_icb, |positioning_context| {
Ok(replaced) => { let size;
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width let fragments;
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-height match self.absolutely_positioned_box.contents.as_replaced() {
let style = &self.absolutely_positioned_box.contents.style; Ok(replaced) => {
let size = replaced_used_size.unwrap(); // https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
let fragments = replaced.make_fragments(style, size.clone()); // https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
(size, fragments) let style = &self.absolutely_positioned_box.contents.style;
}, size = replaced_used_size.unwrap();
Err(non_replaced) => { fragments = replaced.make_fragments(style, size.clone());
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width },
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height Err(non_replaced) => {
let inline_size = inline_axis.size.auto_is(|| { // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
let available_size = match inline_axis.anchor { // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height
Anchor::Start(start) => { let inline_size = inline_axis.size.auto_is(|| {
cbis - start - pb.inline_sum() - margin.inline_sum() let available_size = match inline_axis.anchor {
}, Anchor::Start(start) => {
Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(), 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 // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
.contents assert_eq!(
.content_sizes containing_block.style.writing_mode,
.shrink_to_fit(available_size) 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 { size = Vec2 {
inline_size, inline: inline_size,
block_size: block_axis.size, block: block_axis
style, .size
}; .auto_is(|| independent_layout.content_block_size),
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows };
assert_eq!( fragments = independent_layout.fragments
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,
&containing_block_for_children,
dummy_tree_rank,
&mut absolutely_positioned_fragments,
);
let size = Vec2 { let inline_start = match inline_axis.anchor {
inline: inline_size, Anchor::Start(start) => start + pb.inline_start + margin.inline_start,
block: block_axis Anchor::End(end) => cbis - end - pb.inline_end - margin.inline_end - size.inline,
.size };
.auto_is(|| independent_layout.content_block_size), let block_start = match block_axis.anchor {
}; Anchor::Start(start) => start + pb.block_start + margin.block_start,
(size, independent_layout.fragments) Anchor::End(end) => cbbs - end - pb.block_end - margin.block_end - size.block,
}, };
};
let inline_start = match inline_axis.anchor { let content_rect = Rect {
Anchor::Start(start) => start + pb.inline_start + margin.inline_start, start_corner: Vec2 {
Anchor::End(end) => cbbs - end - pb.inline_end - margin.inline_end - size.inline, inline: inline_start,
}; block: block_start,
let block_start = match block_axis.anchor { },
Anchor::Start(start) => start + pb.block_start + margin.block_start, size,
Anchor::End(end) => cbbs - end - pb.block_end - margin.block_end - size.block, };
};
let content_rect = Rect { BoxFragment {
start_corner: Vec2 { style: style.clone(),
inline: inline_start, children: fragments,
block: block_start, content_rect,
}, padding,
size, border,
}; margin,
block_margins_collapsed_with_children: CollapsedBlockMargins::zero(),
AbsolutelyPositionedFragment::in_positioned_containing_block( }
layout_context,
&absolutely_positioned_fragments,
&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(),
}) })
} }
} }
@ -413,9 +585,9 @@ fn solve_axis(
} }
} }
pub(crate) fn adjust_static_positions( fn adjust_static_positions(
absolutely_positioned_fragments: &mut [AbsolutelyPositionedFragment], absolutely_positioned_fragments: &mut [HoistedAbsolutelyPositionedBox],
child_fragments: &mut [Fragment], child_fragments: &[Fragment],
tree_rank_in_parent: usize, tree_rank_in_parent: usize,
) { ) {
for abspos_fragment in absolutely_positioned_fragments { for abspos_fragment in absolutely_positioned_fragments {
@ -436,3 +608,35 @@ pub(crate) 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)
}
}
/// https://drafts.csswg.org/css2/visuren.html#relative-positioning
pub(crate) fn relative_adjustement(
style: &ComputedValues,
containing_block: &ContainingBlock,
) -> Vec2<Length> {
let cbis = containing_block.inline_size;
let cbbs = containing_block.block_size.auto_is(Length::zero);
let box_offsets = style.box_offsets().map_inline_and_block_axes(
|v| v.percentage_relative_to(cbis),
|v| v.percentage_relative_to(cbbs),
);
fn adjust(start: LengthOrAuto, end: LengthOrAuto) -> Length {
match (start, end) {
(LengthOrAuto::Auto, LengthOrAuto::Auto) => Length::zero(),
(LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => -end,
(LengthOrAuto::LengthPercentage(start), _) => start,
}
}
Vec2 {
inline: adjust(box_offsets.inline_start, box_offsets.inline_end),
block: adjust(box_offsets.block_start, box_offsets.block_end),
}
}

View file

@ -55,6 +55,9 @@ impl computed_value::T {
pub fn is_absolutely_positioned(self) -> bool { pub fn is_absolutely_positioned(self) -> bool {
matches!(self, Self::Absolute | Self::Fixed) matches!(self, Self::Absolute | Self::Fixed)
} }
pub fn is_relative(self) -> bool {
self == Self::Relative
}
} }
</%helpers:single_keyword> </%helpers:single_keyword>

View file

@ -193,6 +193,29 @@
} }
</%helpers:shorthand> </%helpers:shorthand>
<%helpers:shorthand name="background"
engines="servo-2020"
sub_properties="background-color"
spec="https://drafts.csswg.org/css-backgrounds/#the-background">
use crate::values::specified::Color;
use crate::parser::Parse;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
Ok(expanded! {
background_color: Color::parse(context, input)?
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
self.background_color.to_css(dest)
}
}
</%helpers:shorthand>
<%helpers:shorthand name="background-position" <%helpers:shorthand name="background-position"
engines="gecko servo-2013" engines="gecko servo-2013"
flags="SHORTHAND_IN_GETCS" flags="SHORTHAND_IN_GETCS"

View file

@ -1,2 +0,0 @@
[block-in-inline-008.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[block-in-inline-relpos-001.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[containing-block-001.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[containing-block-002.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[containing-block-003.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[containing-block-004.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[containing-block-005.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[containing-block-006.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[containing-block-007.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[containing-block-008.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[containing-block-010.xht]
expected: FAIL

View file

@ -0,0 +1,2 @@
[containing-block-023.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[containing-block-026.xht]
expected: FAIL