mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
layout: Simplify PositioningContext
by having it hold a single Vec
(#36795)
`PositioningContext` held two vectors, one inside an `Option`, to differentiate between the version used for a containing block for all descendants (including `position: absolute` and `position: fixed`) or only for `position: absolute` descendants. This distinction was really hard to reason about and required a lot of bookkeeping about what kind of `PositioningContext` a layout box's parent expected. In addition, it led to a lot of mistakes. This change simplifies things so that `PositioningContext` only holds a single vector. When it comes time to lay out hoisted absolutely positioned fragments, the code then: - lays out all of them (in the case of a `PositioningContext` for all descendants), or - only lays out the `position: absolute` descendants and preserves the `position: fixed` descendants (in the case the `PositioningContext` is only for `position: absolute`.), or - lays out none of them if the `PositioningContext` was created for box that did not establish a containing block for absolutes. It's possible that this way of dealing with hoisted absolutes is a bit less efficient, but, the number of these descendants is typically quite small, so it should not be significant. In addition, this decreases the size in memory of all `PositioningContexts` which are created in more situations as time goes on. Testing: There is a new WPT test with this change. Fixes: #36696. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
e25e63b587
commit
9bc16482a3
12 changed files with 203 additions and 323 deletions
|
@ -49,7 +49,6 @@ use crate::{
|
|||
struct FlexContext<'a> {
|
||||
config: FlexContainerConfig,
|
||||
layout_context: &'a LayoutContext<'a>,
|
||||
positioning_context: &'a mut PositioningContext,
|
||||
containing_block: &'a ContainingBlock<'a>, // For items
|
||||
container_inner_size_constraint: FlexRelativeVec2<SizeConstraint>,
|
||||
}
|
||||
|
@ -657,7 +656,6 @@ impl FlexContainer {
|
|||
let mut flex_context = FlexContext {
|
||||
config: self.config.clone(),
|
||||
layout_context,
|
||||
positioning_context,
|
||||
containing_block,
|
||||
// https://drafts.csswg.org/css-flexbox/#definite-sizes
|
||||
container_inner_size_constraint: self.config.flex_axis.vec2_to_flex_relative(
|
||||
|
@ -1775,16 +1773,7 @@ impl FlexItem<'_> {
|
|||
) -> Option<FlexItemLayoutResult> {
|
||||
let containing_block = flex_context.containing_block;
|
||||
let independent_formatting_context = &self.box_.independent_formatting_context;
|
||||
let mut positioning_context = independent_formatting_context
|
||||
.new_positioning_context()
|
||||
.unwrap_or_else(|| {
|
||||
PositioningContext::new_for_subtree(
|
||||
flex_context
|
||||
.positioning_context
|
||||
.collects_for_nearest_positioned_ancestor(),
|
||||
)
|
||||
});
|
||||
|
||||
let mut positioning_context = PositioningContext::default();
|
||||
let item_writing_mode = independent_formatting_context.style().writing_mode;
|
||||
let item_is_horizontal = item_writing_mode.is_horizontal();
|
||||
let flex_axis = flex_context.config.flex_axis;
|
||||
|
@ -2617,17 +2606,7 @@ impl FlexItemBox {
|
|||
cross_size_stretches_to_container_size: bool,
|
||||
intrinsic_sizing_mode: IntrinsicSizingMode,
|
||||
) -> Au {
|
||||
let mut positioning_context = self
|
||||
.independent_formatting_context
|
||||
.new_positioning_context()
|
||||
.unwrap_or_else(|| {
|
||||
PositioningContext::new_for_subtree(
|
||||
flex_context
|
||||
.positioning_context
|
||||
.collects_for_nearest_positioned_ancestor(),
|
||||
)
|
||||
});
|
||||
|
||||
let mut positioning_context = PositioningContext::default();
|
||||
let style = self.independent_formatting_context.style();
|
||||
match &self.independent_formatting_context.contents {
|
||||
IndependentFormattingContextContents::Replaced(replaced) => {
|
||||
|
|
|
@ -331,7 +331,7 @@ impl LineItemLayout<'_, '_> {
|
|||
self.calculate_inline_box_block_start(inline_box_state, space_above_baseline);
|
||||
|
||||
let positioning_context_or_start_offset_in_parent =
|
||||
match inline_box.base.new_positioning_context() {
|
||||
match PositioningContext::new_for_layout_box_base(&inline_box.base) {
|
||||
Some(positioning_context) => Either::Left(positioning_context),
|
||||
None => Either::Right(self.current_positioning_context_mut().len()),
|
||||
};
|
||||
|
|
|
@ -2004,9 +2004,7 @@ impl IndependentFormattingContext {
|
|||
bidi_level: Level,
|
||||
) {
|
||||
// We need to know the inline size of the atomic before deciding whether to do the line break.
|
||||
let mut child_positioning_context = self
|
||||
.new_positioning_context()
|
||||
.unwrap_or_else(|| PositioningContext::new_for_subtree(true));
|
||||
let mut child_positioning_context = PositioningContext::default();
|
||||
let IndependentFloatOrAtomicLayoutResult {
|
||||
mut fragment,
|
||||
baselines,
|
||||
|
|
|
@ -689,16 +689,13 @@ fn layout_block_level_children_in_parallel(
|
|||
placement_state: &mut PlacementState,
|
||||
ignore_block_margins_for_stretch: LogicalSides1D<bool>,
|
||||
) -> Vec<Fragment> {
|
||||
let collects_for_nearest_positioned_ancestor =
|
||||
positioning_context.collects_for_nearest_positioned_ancestor();
|
||||
let mut layout_results: Vec<(Fragment, PositioningContext)> =
|
||||
Vec::with_capacity(child_boxes.len());
|
||||
|
||||
child_boxes
|
||||
.par_iter()
|
||||
.map(|child_box| {
|
||||
let mut child_positioning_context =
|
||||
PositioningContext::new_for_subtree(collects_for_nearest_positioned_ancestor);
|
||||
let mut child_positioning_context = PositioningContext::default();
|
||||
let fragment = child_box.borrow().layout(
|
||||
layout_context,
|
||||
&mut child_positioning_context,
|
||||
|
|
|
@ -385,8 +385,7 @@ impl BoxTree {
|
|||
style,
|
||||
};
|
||||
|
||||
let mut positioning_context =
|
||||
PositioningContext::new_for_containing_block_for_all_descendants();
|
||||
let mut positioning_context = PositioningContext::default();
|
||||
let independent_layout = self.root.layout(
|
||||
layout_context,
|
||||
&mut positioning_context,
|
||||
|
|
|
@ -295,10 +295,7 @@ impl IndependentNonReplacedContents {
|
|||
);
|
||||
}
|
||||
|
||||
let mut child_positioning_context = PositioningContext::new_for_subtree(
|
||||
positioning_context.collects_for_nearest_positioned_ancestor(),
|
||||
);
|
||||
|
||||
let mut child_positioning_context = PositioningContext::default();
|
||||
let result = self.layout_without_caching(
|
||||
layout_context,
|
||||
&mut child_positioning_context,
|
||||
|
|
|
@ -42,16 +42,6 @@ pub(crate) struct AbsolutelyPositionedBox {
|
|||
pub context: IndependentFormattingContext,
|
||||
}
|
||||
|
||||
#[derive(Clone, MallocSizeOf)]
|
||||
pub(crate) struct PositioningContext {
|
||||
for_nearest_positioned_ancestor: Option<Vec<HoistedAbsolutelyPositionedBox>>,
|
||||
|
||||
// For nearest `containing block for all descendants` as defined by the CSS transforms
|
||||
// spec.
|
||||
// https://www.w3.org/TR/css-transforms-1/#containing-block-for-all-descendants
|
||||
for_nearest_containing_block_for_all_descendants: Vec<HoistedAbsolutelyPositionedBox>,
|
||||
}
|
||||
|
||||
#[derive(Clone, MallocSizeOf)]
|
||||
pub(crate) struct HoistedAbsolutelyPositionedBox {
|
||||
absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
|
||||
|
@ -104,55 +94,26 @@ impl AbsolutelyPositionedBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl IndependentFormattingContext {
|
||||
#[inline]
|
||||
pub(crate) fn new_positioning_context(&self) -> Option<PositioningContext> {
|
||||
self.base.new_positioning_context()
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutBoxBase {
|
||||
#[inline]
|
||||
pub(crate) fn new_positioning_context(&self) -> Option<PositioningContext> {
|
||||
PositioningContext::new_for_style(&self.style, &self.base_fragment_info.flags)
|
||||
}
|
||||
#[derive(Clone, Default, MallocSizeOf)]
|
||||
pub(crate) struct PositioningContext {
|
||||
absolutes: Vec<HoistedAbsolutelyPositionedBox>,
|
||||
}
|
||||
|
||||
impl PositioningContext {
|
||||
pub(crate) fn new_for_containing_block_for_all_descendants() -> Self {
|
||||
Self {
|
||||
for_nearest_positioned_ancestor: None,
|
||||
for_nearest_containing_block_for_all_descendants: Vec::new(),
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn new_for_layout_box_base(layout_box_base: &LayoutBoxBase) -> Option<Self> {
|
||||
Self::new_for_style_and_fragment_flags(
|
||||
&layout_box_base.style,
|
||||
&layout_box_base.base_fragment_info.flags,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a [PositioningContext] to use for laying out a subtree. The idea is that
|
||||
/// when subtree layout is finished, the newly hoisted boxes can be processed
|
||||
/// (normally adjusting their static insets) and then appended to the parent
|
||||
/// [PositioningContext].
|
||||
pub(crate) fn new_for_subtree(collects_for_nearest_positioned_ancestor: bool) -> Self {
|
||||
Self {
|
||||
for_nearest_positioned_ancestor: if collects_for_nearest_positioned_ancestor {
|
||||
Some(Vec::new())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
for_nearest_containing_block_for_all_descendants: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn collects_for_nearest_positioned_ancestor(&self) -> bool {
|
||||
self.for_nearest_positioned_ancestor.is_some()
|
||||
}
|
||||
|
||||
fn new_for_style(style: &ComputedValues, flags: &FragmentFlags) -> Option<Self> {
|
||||
if style.establishes_containing_block_for_all_descendants(*flags) {
|
||||
Some(Self::new_for_containing_block_for_all_descendants())
|
||||
} else if style.establishes_containing_block_for_absolute_descendants(*flags) {
|
||||
Some(Self {
|
||||
for_nearest_positioned_ancestor: Some(Vec::new()),
|
||||
for_nearest_containing_block_for_all_descendants: Vec::new(),
|
||||
})
|
||||
fn new_for_style_and_fragment_flags(
|
||||
style: &ComputedValues,
|
||||
flags: &FragmentFlags,
|
||||
) -> Option<Self> {
|
||||
if style.establishes_containing_block_for_absolute_descendants(*flags) {
|
||||
Some(Self::default())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -195,20 +156,9 @@ impl PositioningContext {
|
|||
offset: &PhysicalVec<Au>,
|
||||
index: PositioningContextLength,
|
||||
) {
|
||||
if let Some(hoisted_boxes) = self.for_nearest_positioned_ancestor.as_mut() {
|
||||
hoisted_boxes
|
||||
.iter_mut()
|
||||
.skip(index.for_nearest_positioned_ancestor)
|
||||
.for_each(|hoisted_fragment| {
|
||||
hoisted_fragment
|
||||
.fragment
|
||||
.borrow_mut()
|
||||
.adjust_offsets(offset)
|
||||
})
|
||||
}
|
||||
self.for_nearest_containing_block_for_all_descendants
|
||||
self.absolutes
|
||||
.iter_mut()
|
||||
.skip(index.for_nearest_containing_block_for_all_descendants)
|
||||
.skip(index.0)
|
||||
.for_each(|hoisted_fragment| {
|
||||
hoisted_fragment
|
||||
.fragment
|
||||
|
@ -227,19 +177,23 @@ impl PositioningContext {
|
|||
base: &LayoutBoxBase,
|
||||
fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment,
|
||||
) -> BoxFragment {
|
||||
// Try to create a context, but if one isn't necessary, simply create the fragment
|
||||
// using the given closure and the current `PositioningContext`.
|
||||
let mut new_context = match base.new_positioning_context() {
|
||||
Some(new_context) => new_context,
|
||||
None => return fragment_layout_fn(self),
|
||||
};
|
||||
// If a new `PositioningContext` isn't necessary, simply create the fragment using
|
||||
// the given closure and the current `PositioningContext`.
|
||||
let establishes_containing_block_for_absolutes = base
|
||||
.style
|
||||
.establishes_containing_block_for_absolute_descendants(base.base_fragment_info.flags);
|
||||
if !establishes_containing_block_for_absolutes {
|
||||
return fragment_layout_fn(self);
|
||||
}
|
||||
|
||||
let mut new_context = PositioningContext::default();
|
||||
let mut new_fragment = fragment_layout_fn(&mut new_context);
|
||||
new_context.layout_collected_children(layout_context, &mut new_fragment);
|
||||
|
||||
// If the new context has any hoisted boxes for the nearest containing block for
|
||||
// pass them up the tree.
|
||||
// Lay out all of the absolutely positioned children for this fragment, and, if it
|
||||
// isn't a containing block for fixed elements, then pass those up to the parent.
|
||||
new_context.layout_collected_children(layout_context, &mut new_fragment);
|
||||
self.append(new_context);
|
||||
|
||||
if base.style.clone_position() == Position::Relative {
|
||||
new_fragment.content_rect.origin += relative_adjustement(&base.style, containing_block)
|
||||
.to_physical_vector(containing_block.style.writing_mode)
|
||||
|
@ -248,13 +202,61 @@ impl PositioningContext {
|
|||
new_fragment
|
||||
}
|
||||
|
||||
fn take_boxes_for_fragment(
|
||||
&mut self,
|
||||
new_fragment: &BoxFragment,
|
||||
boxes_to_layout_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
|
||||
boxes_to_continue_hoisting_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
|
||||
) {
|
||||
debug_assert!(
|
||||
new_fragment
|
||||
.style
|
||||
.establishes_containing_block_for_absolute_descendants(new_fragment.base.flags)
|
||||
);
|
||||
|
||||
if new_fragment
|
||||
.style
|
||||
.establishes_containing_block_for_all_descendants(new_fragment.base.flags)
|
||||
{
|
||||
boxes_to_layout_out.append(&mut self.absolutes);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: This could potentially use `extract_if` when that is stabilized.
|
||||
let (mut boxes_to_layout, mut boxes_to_continue_hoisting) = self
|
||||
.absolutes
|
||||
.drain(..)
|
||||
.partition(|hoisted_box| hoisted_box.position() != Position::Fixed);
|
||||
boxes_to_layout_out.append(&mut boxes_to_layout);
|
||||
boxes_to_continue_hoisting_out.append(&mut boxes_to_continue_hoisting);
|
||||
}
|
||||
|
||||
// Lay out the hoisted boxes collected into this `PositioningContext` and add them
|
||||
// to the given `BoxFragment`.
|
||||
pub fn layout_collected_children(
|
||||
pub(crate) fn layout_collected_children(
|
||||
&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
new_fragment: &mut BoxFragment,
|
||||
) {
|
||||
if self.absolutes.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sometimes we create temporary PositioningContexts just to collect hoisted absolutes and
|
||||
// then these are processed later. In that case and if this fragment doesn't establish a
|
||||
// containing block for absolutes at all, we just do nothing. All hoisted fragments will
|
||||
// later be passed up to a parent PositioningContext.
|
||||
//
|
||||
// Handling this case here, when the PositioningContext is completely ineffectual other than
|
||||
// as a temporary container for hoisted boxes, means that callers can execute less conditional
|
||||
// code.
|
||||
if !new_fragment
|
||||
.style
|
||||
.establishes_containing_block_for_absolute_descendants(new_fragment.base.flags)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let padding_rect = PhysicalRect::new(
|
||||
// Ignore the content rect’s position in its own containing block:
|
||||
PhysicalPoint::origin(),
|
||||
|
@ -268,83 +270,58 @@ impl PositioningContext {
|
|||
style: &new_fragment.style,
|
||||
};
|
||||
|
||||
let take_hoisted_boxes_pending_layout =
|
||||
|context: &mut Self| match context.for_nearest_positioned_ancestor.as_mut() {
|
||||
Some(fragments) => mem::take(fragments),
|
||||
None => mem::take(&mut context.for_nearest_containing_block_for_all_descendants),
|
||||
};
|
||||
let mut fixed_position_boxes_to_hoist = Vec::new();
|
||||
let mut boxes_to_layout = Vec::new();
|
||||
self.take_boxes_for_fragment(
|
||||
new_fragment,
|
||||
&mut boxes_to_layout,
|
||||
&mut fixed_position_boxes_to_hoist,
|
||||
);
|
||||
|
||||
// Loop because it’s possible that we discover (the static position of)
|
||||
// more absolutely-positioned boxes while doing layout for others.
|
||||
let mut hoisted_boxes = take_hoisted_boxes_pending_layout(self);
|
||||
let mut laid_out_child_fragments = Vec::new();
|
||||
while !hoisted_boxes.is_empty() {
|
||||
// Laying out a `position: absolute` child (which only establishes a containing block for
|
||||
// `position: absolute` descendants) can result in more `position: fixed` descendants
|
||||
// collecting in `self.absolutes`. We need to loop here in order to keep either laying them
|
||||
// out or putting them into `fixed_position_boxes_to_hoist`. We know there aren't any more
|
||||
// when `self.absolutes` is empty.
|
||||
while !boxes_to_layout.is_empty() {
|
||||
HoistedAbsolutelyPositionedBox::layout_many(
|
||||
layout_context,
|
||||
&mut hoisted_boxes,
|
||||
&mut laid_out_child_fragments,
|
||||
&mut self.for_nearest_containing_block_for_all_descendants,
|
||||
std::mem::take(&mut boxes_to_layout),
|
||||
&mut new_fragment.children,
|
||||
&mut self.absolutes,
|
||||
&containing_block,
|
||||
new_fragment.padding,
|
||||
);
|
||||
hoisted_boxes = take_hoisted_boxes_pending_layout(self);
|
||||
|
||||
self.take_boxes_for_fragment(
|
||||
new_fragment,
|
||||
&mut boxes_to_layout,
|
||||
&mut fixed_position_boxes_to_hoist,
|
||||
);
|
||||
}
|
||||
|
||||
new_fragment.children.extend(laid_out_child_fragments);
|
||||
// We replace here instead of simply preserving these in `take_boxes_for_fragment`
|
||||
// so that we don't have to continually re-iterate over them when laying out in the
|
||||
// loop above.
|
||||
self.absolutes = fixed_position_boxes_to_hoist;
|
||||
}
|
||||
|
||||
pub(crate) fn push(&mut self, box_: HoistedAbsolutelyPositionedBox) {
|
||||
if let Some(nearest) = &mut self.for_nearest_positioned_ancestor {
|
||||
let position = box_
|
||||
.absolutely_positioned_box
|
||||
.borrow()
|
||||
.context
|
||||
.style()
|
||||
.clone_position();
|
||||
match position {
|
||||
Position::Fixed => {}, // fall through
|
||||
Position::Absolute => return nearest.push(box_),
|
||||
Position::Static | Position::Relative | Position::Sticky => unreachable!(),
|
||||
}
|
||||
}
|
||||
self.for_nearest_containing_block_for_all_descendants
|
||||
.push(box_)
|
||||
pub(crate) fn push(&mut self, hoisted_box: HoistedAbsolutelyPositionedBox) {
|
||||
debug_assert!(matches!(
|
||||
hoisted_box.position(),
|
||||
Position::Absolute | Position::Fixed
|
||||
));
|
||||
self.absolutes.push(hoisted_box);
|
||||
}
|
||||
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.for_nearest_containing_block_for_all_descendants
|
||||
.is_empty() &&
|
||||
self.for_nearest_positioned_ancestor
|
||||
.as_ref()
|
||||
.is_none_or(|vector| vector.is_empty())
|
||||
}
|
||||
|
||||
pub(crate) fn append(&mut self, other: Self) {
|
||||
if other.is_empty() {
|
||||
pub(crate) fn append(&mut self, mut other: Self) {
|
||||
if other.absolutes.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
vec_append_owned(
|
||||
&mut self.for_nearest_containing_block_for_all_descendants,
|
||||
other.for_nearest_containing_block_for_all_descendants,
|
||||
);
|
||||
|
||||
match (
|
||||
self.for_nearest_positioned_ancestor.as_mut(),
|
||||
other.for_nearest_positioned_ancestor,
|
||||
) {
|
||||
(Some(us), Some(them)) => vec_append_owned(us, them),
|
||||
(None, Some(them)) => {
|
||||
// This is the case where we have laid out the absolute children in a containing
|
||||
// block for absolutes and we then are passing up the fixed-position descendants
|
||||
// to the containing block for all descendants.
|
||||
vec_append_owned(
|
||||
&mut self.for_nearest_containing_block_for_all_descendants,
|
||||
them,
|
||||
);
|
||||
},
|
||||
(None, None) => {},
|
||||
_ => unreachable!(),
|
||||
if self.absolutes.is_empty() {
|
||||
self.absolutes = other.absolutes;
|
||||
} else {
|
||||
self.absolutes.append(&mut other.absolutes)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,19 +331,16 @@ impl PositioningContext {
|
|||
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_nearest_containing_block_for_all_descendants
|
||||
.is_empty()
|
||||
{
|
||||
// Laying out a `position: absolute` child (which only establishes a containing block for
|
||||
// `position: absolute` descendants) can result in more `position: fixed` descendants
|
||||
// collecting in `self.absolutes`. We need to loop here in order to keep laying them out. We
|
||||
// know there aren't any more when `self.absolutes` is empty.
|
||||
while !self.absolutes.is_empty() {
|
||||
HoistedAbsolutelyPositionedBox::layout_many(
|
||||
layout_context,
|
||||
&mut mem::take(&mut self.for_nearest_containing_block_for_all_descendants),
|
||||
mem::take(&mut self.absolutes),
|
||||
fragments,
|
||||
&mut self.for_nearest_containing_block_for_all_descendants,
|
||||
&mut self.absolutes,
|
||||
initial_containing_block,
|
||||
Default::default(),
|
||||
)
|
||||
|
@ -375,58 +349,46 @@ impl PositioningContext {
|
|||
|
||||
/// Get the length of this [PositioningContext].
|
||||
pub(crate) fn len(&self) -> PositioningContextLength {
|
||||
PositioningContextLength {
|
||||
for_nearest_positioned_ancestor: self
|
||||
.for_nearest_positioned_ancestor
|
||||
.as_ref()
|
||||
.map_or(0, |vec| vec.len()),
|
||||
for_nearest_containing_block_for_all_descendants: self
|
||||
.for_nearest_containing_block_for_all_descendants
|
||||
.len(),
|
||||
}
|
||||
PositioningContextLength(self.absolutes.len())
|
||||
}
|
||||
|
||||
/// Truncate this [PositioningContext] to the given [PositioningContextLength]. This
|
||||
/// is useful for "unhoisting" boxes in this context and returning it to the state at
|
||||
/// the time that [`PositioningContext::len()`] was called.
|
||||
pub(crate) fn truncate(&mut self, length: &PositioningContextLength) {
|
||||
if let Some(vec) = self.for_nearest_positioned_ancestor.as_mut() {
|
||||
vec.truncate(length.for_nearest_positioned_ancestor);
|
||||
}
|
||||
self.for_nearest_containing_block_for_all_descendants
|
||||
.truncate(length.for_nearest_containing_block_for_all_descendants);
|
||||
self.absolutes.truncate(length.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A data structure which stores the size of a positioning context.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub(crate) struct PositioningContextLength {
|
||||
/// The number of boxes that will be hoisted the the nearest positioned ancestor for
|
||||
/// layout.
|
||||
for_nearest_positioned_ancestor: usize,
|
||||
/// The number of boxes that will be hoisted the the nearest ancestor which
|
||||
/// establishes a containing block for all descendants for layout.
|
||||
for_nearest_containing_block_for_all_descendants: usize,
|
||||
}
|
||||
pub(crate) struct PositioningContextLength(usize);
|
||||
|
||||
impl Zero for PositioningContextLength {
|
||||
fn zero() -> Self {
|
||||
PositioningContextLength {
|
||||
for_nearest_positioned_ancestor: 0,
|
||||
for_nearest_containing_block_for_all_descendants: 0,
|
||||
}
|
||||
Self(0)
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.for_nearest_positioned_ancestor == 0 &&
|
||||
self.for_nearest_containing_block_for_all_descendants == 0
|
||||
self.0.is_zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl HoistedAbsolutelyPositionedBox {
|
||||
fn position(&self) -> Position {
|
||||
let position = self
|
||||
.absolutely_positioned_box
|
||||
.borrow()
|
||||
.context
|
||||
.style()
|
||||
.clone_position();
|
||||
assert!(position == Position::Fixed || position == Position::Absolute);
|
||||
position
|
||||
}
|
||||
|
||||
pub(crate) fn layout_many(
|
||||
layout_context: &LayoutContext,
|
||||
boxes: &mut [Self],
|
||||
mut boxes: Vec<Self>,
|
||||
fragments: &mut Vec<Fragment>,
|
||||
for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>,
|
||||
containing_block: &DefiniteContainingBlock,
|
||||
|
@ -473,7 +435,7 @@ impl HoistedAbsolutelyPositionedBox {
|
|||
pub(crate) fn layout(
|
||||
&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>,
|
||||
hoisted_absolutes_from_children: &mut Vec<HoistedAbsolutelyPositionedBox>,
|
||||
containing_block: &DefiniteContainingBlock,
|
||||
containing_block_padding: PhysicalSides<Au>,
|
||||
) -> Fragment {
|
||||
|
@ -596,7 +558,7 @@ impl HoistedAbsolutelyPositionedBox {
|
|||
.sizes
|
||||
}));
|
||||
|
||||
let mut positioning_context = context.new_positioning_context().unwrap();
|
||||
let mut positioning_context = PositioningContext::default();
|
||||
let mut new_fragment = {
|
||||
let content_size: LogicalVec2<Au>;
|
||||
let fragments;
|
||||
|
@ -709,6 +671,10 @@ impl HoistedAbsolutelyPositionedBox {
|
|||
)
|
||||
.with_specific_layout_info(specific_layout_info)
|
||||
};
|
||||
|
||||
// This is an absolutely positioned element, which means it also establishes a
|
||||
// containing block for absolutes. We lay out any absolutely positioned children
|
||||
// here and pass the rest to `hoisted_absolutes_from_children.`
|
||||
positioning_context.layout_collected_children(layout_context, &mut new_fragment);
|
||||
|
||||
// Any hoisted boxes that remain in this positioning context are going to be hoisted
|
||||
|
@ -721,8 +687,7 @@ impl HoistedAbsolutelyPositionedBox {
|
|||
PositioningContextLength::zero(),
|
||||
);
|
||||
|
||||
for_nearest_containing_block_for_all_descendants
|
||||
.extend(positioning_context.for_nearest_containing_block_for_all_descendants);
|
||||
hoisted_absolutes_from_children.extend(positioning_context.absolutes);
|
||||
|
||||
let fragment = Fragment::Box(ArcRefCell::new(new_fragment));
|
||||
context.base.set_fragment(fragment.clone());
|
||||
|
@ -1024,14 +989,6 @@ impl AbsoluteAxisSolver<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
|
@ -1068,7 +1068,6 @@ impl<'a> TableLayout<'a> {
|
|||
&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
containing_block_for_table: &ContainingBlock,
|
||||
parent_positioning_context: &mut PositioningContext,
|
||||
) {
|
||||
self.cells_laid_out = self
|
||||
.table
|
||||
|
@ -1076,30 +1075,6 @@ impl<'a> TableLayout<'a> {
|
|||
.par_iter()
|
||||
.enumerate()
|
||||
.map(|(row_index, row_slots)| {
|
||||
// When building the PositioningContext for this cell, we want it to have the same
|
||||
// configuration for whatever PositioningContext the contents are ultimately added to.
|
||||
let collect_for_nearest_positioned_ancestor = parent_positioning_context
|
||||
.collects_for_nearest_positioned_ancestor() ||
|
||||
self.table.rows.get(row_index).is_some_and(|row| {
|
||||
let row = row.borrow();
|
||||
let row_group_collects_for_nearest_positioned_ancestor =
|
||||
row.group_index.is_some_and(|group_index| {
|
||||
self.table.row_groups[group_index]
|
||||
.borrow()
|
||||
.base
|
||||
.style
|
||||
.establishes_containing_block_for_absolute_descendants(
|
||||
FragmentFlags::empty(),
|
||||
)
|
||||
});
|
||||
row_group_collects_for_nearest_positioned_ancestor ||
|
||||
row.base
|
||||
.style
|
||||
.establishes_containing_block_for_absolute_descendants(
|
||||
FragmentFlags::empty(),
|
||||
)
|
||||
});
|
||||
|
||||
row_slots
|
||||
.par_iter()
|
||||
.enumerate()
|
||||
|
@ -1141,10 +1116,7 @@ impl<'a> TableLayout<'a> {
|
|||
style: &cell.base.style,
|
||||
};
|
||||
|
||||
let mut positioning_context = PositioningContext::new_for_subtree(
|
||||
collect_for_nearest_positioned_ancestor,
|
||||
);
|
||||
|
||||
let mut positioning_context = PositioningContext::default();
|
||||
let layout = cell.contents.layout(
|
||||
layout_context,
|
||||
&mut positioning_context,
|
||||
|
@ -1503,7 +1475,6 @@ impl<'a> TableLayout<'a> {
|
|||
layout_context: &LayoutContext,
|
||||
parent_positioning_context: &mut PositioningContext,
|
||||
) -> BoxFragment {
|
||||
let mut positioning_context = caption.context.new_positioning_context();
|
||||
let containing_block = &ContainingBlock {
|
||||
size: ContainingBlockSize {
|
||||
inline: self.table_width + self.pbm.padding_border_sums.inline,
|
||||
|
@ -1517,6 +1488,8 @@ impl<'a> TableLayout<'a> {
|
|||
// stretch block size. https://drafts.csswg.org/css-sizing-4/#stretch-fit-sizing
|
||||
let ignore_block_margins_for_stretch = LogicalSides1D::new(false, false);
|
||||
|
||||
let mut positioning_context =
|
||||
PositioningContext::new_for_layout_box_base(&caption.context.base);
|
||||
let mut box_fragment = caption.context.layout_in_flow_block_level(
|
||||
layout_context,
|
||||
positioning_context
|
||||
|
@ -1769,11 +1742,7 @@ impl<'a> TableLayout<'a> {
|
|||
) -> BoxFragment {
|
||||
self.distributed_column_widths =
|
||||
Self::distribute_width_to_columns(self.assignable_width, &self.columns);
|
||||
self.layout_cells_in_row(
|
||||
layout_context,
|
||||
containing_block_for_children,
|
||||
positioning_context,
|
||||
);
|
||||
self.layout_cells_in_row(layout_context, containing_block_for_children);
|
||||
let table_writing_mode = containing_block_for_children.style.writing_mode;
|
||||
let first_layout_row_heights = self.do_first_row_layout(table_writing_mode);
|
||||
self.compute_table_height_and_final_row_heights(
|
||||
|
@ -2325,7 +2294,7 @@ impl<'a> RowFragmentLayout<'a> {
|
|||
Self {
|
||||
row: table_row,
|
||||
rect,
|
||||
positioning_context: table_row.base.new_positioning_context(),
|
||||
positioning_context: PositioningContext::new_for_layout_box_base(&table_row.base),
|
||||
containing_block,
|
||||
fragments: Vec::new(),
|
||||
}
|
||||
|
@ -2379,11 +2348,11 @@ impl<'a> RowFragmentLayout<'a> {
|
|||
if let Some(mut row_positioning_context) = self.positioning_context.take() {
|
||||
row_positioning_context.layout_collected_children(layout_context, &mut row_fragment);
|
||||
|
||||
let positioning_context = row_group_fragment_layout
|
||||
let parent_positioning_context = row_group_fragment_layout
|
||||
.as_mut()
|
||||
.and_then(|layout| layout.positioning_context.as_mut())
|
||||
.unwrap_or(table_positioning_context);
|
||||
positioning_context.append(row_positioning_context);
|
||||
parent_positioning_context.append(row_positioning_context);
|
||||
}
|
||||
|
||||
let fragment = Fragment::Box(ArcRefCell::new(row_fragment));
|
||||
|
@ -2410,7 +2379,7 @@ impl RowGroupFragmentLayout {
|
|||
let row_group = row_group.borrow();
|
||||
(
|
||||
dimensions.get_row_group_rect(&row_group),
|
||||
row_group.base.new_positioning_context(),
|
||||
PositioningContext::new_for_layout_box_base(&row_group.base),
|
||||
)
|
||||
};
|
||||
Self {
|
||||
|
|
|
@ -29,7 +29,7 @@ use crate::geom::{
|
|||
use crate::layout_box_base::CacheableLayoutResult;
|
||||
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
|
||||
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
|
||||
use crate::style_ext::{ComputedValuesExt, LayoutStyle};
|
||||
use crate::style_ext::LayoutStyle;
|
||||
use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize};
|
||||
|
||||
const DUMMY_NODE_ID: taffy::NodeId = taffy::NodeId::new(u64::MAX);
|
||||
|
@ -250,29 +250,15 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
|
|||
},
|
||||
style,
|
||||
};
|
||||
let layout = {
|
||||
let mut child_positioning_context = independent_context
|
||||
.new_positioning_context()
|
||||
.unwrap_or_else(|| {
|
||||
PositioningContext::new_for_subtree(
|
||||
self.positioning_context
|
||||
.collects_for_nearest_positioned_ancestor(),
|
||||
)
|
||||
});
|
||||
|
||||
let layout = non_replaced.layout_without_caching(
|
||||
self.layout_context,
|
||||
&mut child_positioning_context,
|
||||
&content_box_size_override,
|
||||
containing_block,
|
||||
false, /* depends_on_block_constraints */
|
||||
);
|
||||
|
||||
// Store layout data on child for later access
|
||||
child.positioning_context = child_positioning_context;
|
||||
|
||||
layout
|
||||
};
|
||||
child.positioning_context = PositioningContext::default();
|
||||
let layout = non_replaced.layout_without_caching(
|
||||
self.layout_context,
|
||||
&mut child.positioning_context,
|
||||
&content_box_size_override,
|
||||
containing_block,
|
||||
false, /* depends_on_block_constraints */
|
||||
);
|
||||
|
||||
child.child_fragments = layout.fragments;
|
||||
self.child_specific_layout_infos[usize::from(node_id)] =
|
||||
|
@ -373,8 +359,7 @@ impl ComputeInlineContentSizes for TaffyContainer {
|
|||
|
||||
let mut grid_context = TaffyContainerContext {
|
||||
layout_context,
|
||||
positioning_context:
|
||||
&mut PositioningContext::new_for_containing_block_for_all_descendants(),
|
||||
positioning_context: &mut PositioningContext::default(),
|
||||
content_box_size_override: containing_block,
|
||||
style,
|
||||
source_child_nodes: &self.children,
|
||||
|
@ -540,17 +525,6 @@ impl TaffyContainer {
|
|||
let child_specific_layout_info: Option<SpecificLayoutInfo> =
|
||||
std::mem::take(&mut container_ctx.child_specific_layout_infos[child_id]);
|
||||
|
||||
let establishes_containing_block_for_absolute_descendants =
|
||||
if let TaffyItemBoxInner::InFlowBox(independent_box) = &child.taffy_level_box {
|
||||
child
|
||||
.style
|
||||
.establishes_containing_block_for_absolute_descendants(
|
||||
independent_box.base_fragment_info().flags,
|
||||
)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let fragment = match &mut child.taffy_level_box {
|
||||
TaffyItemBoxInner::InFlowBox(independent_box) => {
|
||||
let mut fragment_info = independent_box.base_fragment_info();
|
||||
|
@ -573,29 +547,21 @@ impl TaffyContainer {
|
|||
})
|
||||
.with_specific_layout_info(child_specific_layout_info);
|
||||
|
||||
if establishes_containing_block_for_absolute_descendants {
|
||||
child.positioning_context.layout_collected_children(
|
||||
container_ctx.layout_context,
|
||||
&mut box_fragment,
|
||||
);
|
||||
}
|
||||
|
||||
let fragment = Fragment::Box(ArcRefCell::new(box_fragment));
|
||||
|
||||
child.positioning_context.layout_collected_children(
|
||||
container_ctx.layout_context,
|
||||
&mut box_fragment,
|
||||
);
|
||||
child
|
||||
.positioning_context
|
||||
.adjust_static_position_of_hoisted_fragments(
|
||||
&fragment,
|
||||
.adjust_static_position_of_hoisted_fragments_with_offset(
|
||||
&box_fragment.content_rect.origin.to_vector(),
|
||||
PositioningContextLength::zero(),
|
||||
);
|
||||
let child_positioning_context = std::mem::replace(
|
||||
&mut child.positioning_context,
|
||||
PositioningContext::new_for_containing_block_for_all_descendants(),
|
||||
);
|
||||
container_ctx
|
||||
.positioning_context
|
||||
.append(child_positioning_context);
|
||||
fragment
|
||||
.append(std::mem::take(&mut child.positioning_context));
|
||||
|
||||
Fragment::Box(ArcRefCell::new(box_fragment))
|
||||
},
|
||||
TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(abs_pos_box) => {
|
||||
fn resolve_alignment(value: AlignFlags, auto: AlignFlags) -> AlignFlags {
|
||||
|
|
|
@ -110,7 +110,7 @@ impl TaffyItemBox {
|
|||
Self {
|
||||
taffy_layout: Default::default(),
|
||||
child_fragments: Vec::new(),
|
||||
positioning_context: PositioningContext::new_for_containing_block_for_all_descendants(),
|
||||
positioning_context: PositioningContext::default(),
|
||||
style,
|
||||
taffy_level_box: inner,
|
||||
}
|
||||
|
@ -118,8 +118,7 @@ impl TaffyItemBox {
|
|||
|
||||
pub(crate) fn invalidate_cached_fragment(&mut self) {
|
||||
self.taffy_layout = Default::default();
|
||||
self.positioning_context =
|
||||
PositioningContext::new_for_containing_block_for_all_descendants();
|
||||
self.positioning_context = PositioningContext::default();
|
||||
match self.taffy_level_box {
|
||||
TaffyItemBoxInner::InFlowBox(ref independent_formatting_context) => {
|
||||
independent_formatting_context
|
||||
|
|
7
tests/wpt/meta/MANIFEST.json
vendored
7
tests/wpt/meta/MANIFEST.json
vendored
|
@ -2794,6 +2794,13 @@
|
|||
},
|
||||
"css-grid": {
|
||||
"abspos": {
|
||||
"abspos-in-flexbox-in-grid-crash.html": [
|
||||
"a2f71fb78e7f6fd82ea539e3af6c76d36f21e134",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
]
|
||||
],
|
||||
"positioned-grid-items-crash.html": [
|
||||
"228a6264df77fe82587a4116f231ff09537c8372",
|
||||
[
|
||||
|
|
12
tests/wpt/tests/css/css-grid/abspos/abspos-in-flexbox-in-grid-crash.html
vendored
Normal file
12
tests/wpt/tests/css/css-grid/abspos/abspos-in-flexbox-in-grid-crash.html
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="help" href="https://github.com/servo/servo/issues/36696">
|
||||
|
||||
<div style="display: flex; position: relative">
|
||||
<div style="display:grid">
|
||||
<div>
|
||||
<div style="display: flex">
|
||||
<div style="position: absolute"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Add table
Add a link
Reference in a new issue