mirror of
https://github.com/servo/servo.git
synced 2025-07-25 16:20:36 +01:00
Auto merge of #25273 - servo:fixedpos, r=nox
Layout `position: fixed` in the initial containing block
This commit is contained in:
commit
762e67f486
24 changed files with 552 additions and 404 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 rect’s 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 it’s 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 rect’s 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[block-in-inline-008.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[block-in-inline-relpos-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[containing-block-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[containing-block-002.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[containing-block-003.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[containing-block-004.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[containing-block-005.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[containing-block-006.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[containing-block-007.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[containing-block-008.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[containing-block-010.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[containing-block-023.xht]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[containing-block-026.xht]
|
|
||||||
expected: FAIL
|
|
Loading…
Add table
Add a link
Reference in a new issue