mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Auto merge of #29757 - Loirooriol:sequential-context-2020, r=Loirooriol
Lay out floats and handle clearance in layout 2020, but don't flow text around them yet This is a crude rebase of #27539 <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #___ (GitHub issue number if applicable) <!-- Either: --> - [X] There are tests for these changes OR - [ ] These changes do not require tests because ___ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
commit
f26d17096d
231 changed files with 1060 additions and 695 deletions
|
@ -445,6 +445,9 @@ mod gen {
|
||||||
flexbox: {
|
flexbox: {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
},
|
},
|
||||||
|
floats: {
|
||||||
|
enabled: bool,
|
||||||
|
},
|
||||||
#[serde(default = "default_layout_threads")]
|
#[serde(default = "default_layout_threads")]
|
||||||
threads: i64,
|
threads: i64,
|
||||||
viewport: {
|
viewport: {
|
||||||
|
|
|
@ -193,7 +193,7 @@ impl Fragment {
|
||||||
section: StackingContextSection,
|
section: StackingContextSection,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
Fragment::Box(b) => match b.style.get_inherited_box().visibility {
|
Fragment::Box(b) | Fragment::Float(b) => match b.style.get_inherited_box().visibility {
|
||||||
Visibility::Visible => {
|
Visibility::Visible => {
|
||||||
BuilderForBoxFragment::new(b, containing_block).build(builder, section)
|
BuilderForBoxFragment::new(b, containing_block).build(builder, section)
|
||||||
},
|
},
|
||||||
|
|
|
@ -531,7 +531,7 @@ impl Fragment {
|
||||||
) {
|
) {
|
||||||
let containing_block = containing_block_info.get_containing_block_for_fragment(self);
|
let containing_block = containing_block_info.get_containing_block_for_fragment(self);
|
||||||
match self {
|
match self {
|
||||||
Fragment::Box(fragment) => {
|
Fragment::Box(fragment) | Fragment::Float(fragment) => {
|
||||||
if mode == StackingContextBuildMode::SkipHoisted &&
|
if mode == StackingContextBuildMode::SkipHoisted &&
|
||||||
fragment.style.clone_position().is_absolutely_positioned()
|
fragment.style.clone_position().is_absolutely_positioned()
|
||||||
{
|
{
|
||||||
|
|
|
@ -864,6 +864,7 @@ impl FlexLine<'_> {
|
||||||
flex_context.sides_to_flow_relative(item.padding),
|
flex_context.sides_to_flow_relative(item.padding),
|
||||||
flex_context.sides_to_flow_relative(item.border),
|
flex_context.sides_to_flow_relative(item.border),
|
||||||
margin,
|
margin,
|
||||||
|
Length::zero(),
|
||||||
collapsed_margin,
|
collapsed_margin,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -10,12 +10,21 @@ use crate::context::LayoutContext;
|
||||||
use crate::dom::NodeExt;
|
use crate::dom::NodeExt;
|
||||||
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
|
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
|
||||||
use crate::formatting_contexts::IndependentFormattingContext;
|
use crate::formatting_contexts::IndependentFormattingContext;
|
||||||
|
use crate::fragments::{
|
||||||
|
BoxFragment, CollapsedBlockMargins, CollapsedMargin, FloatFragment, Fragment,
|
||||||
|
};
|
||||||
use crate::geom::flow_relative::{Rect, Vec2};
|
use crate::geom::flow_relative::{Rect, Vec2};
|
||||||
use crate::style_ext::DisplayInside;
|
use crate::positioned::PositioningContext;
|
||||||
|
use crate::style_ext::{ComputedValuesExt, DisplayInside};
|
||||||
|
use crate::ContainingBlock;
|
||||||
use euclid::num::Zero;
|
use euclid::num::Zero;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use std::f32;
|
use std::fmt::{Debug, Formatter, Result as FmtResult};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
use std::{f32, mem};
|
||||||
|
use style::computed_values::clear::T as ClearProperty;
|
||||||
|
use style::computed_values::float::T as FloatProperty;
|
||||||
|
use style::properties::ComputedValues;
|
||||||
use style::values::computed::Length;
|
use style::values::computed::Length;
|
||||||
use style::values::specified::text::TextDecorationLine;
|
use style::values::specified::text::TextDecorationLine;
|
||||||
|
|
||||||
|
@ -26,6 +35,53 @@ pub(crate) struct FloatBox {
|
||||||
pub contents: IndependentFormattingContext,
|
pub contents: IndependentFormattingContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `FloatContext` positions floats relative to the independent block formatting
|
||||||
|
/// context which contains the floating elements. The Fragment tree positions
|
||||||
|
/// elements relative to their containing blocks. This data structure is used to
|
||||||
|
/// help map between these two coordinate systems.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct ContainingBlockPositionInfo {
|
||||||
|
/// The distance from the block start of the independent block formatting
|
||||||
|
/// context that contains the floats and the block start of the current
|
||||||
|
/// containing block, excluding uncollapsed block start margins. Note that
|
||||||
|
/// this does not include uncollapsed block start margins because we don't
|
||||||
|
/// know the value of collapsed margins until we lay out children.
|
||||||
|
pub(crate) block_start: Length,
|
||||||
|
/// Any uncollapsed block start margins that we have collected between the
|
||||||
|
/// block start of the float containing independent block formatting context
|
||||||
|
/// and this containing block, including for this containing block.
|
||||||
|
pub(crate) block_start_margins_not_collapsed: CollapsedMargin,
|
||||||
|
/// The distance from the inline start position of the float containing
|
||||||
|
/// independent formatting context and the inline start of this containing
|
||||||
|
/// block.
|
||||||
|
pub inline_start: Length,
|
||||||
|
/// The offset from the inline start position of the float containing
|
||||||
|
/// independent formatting context to the inline end of this containing
|
||||||
|
/// block.
|
||||||
|
pub inline_end: Length,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ContainingBlockPositionInfo {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
block_start: Length::zero(),
|
||||||
|
block_start_margins_not_collapsed: CollapsedMargin::zero(),
|
||||||
|
inline_start: Length::zero(),
|
||||||
|
inline_end: Length::new(f32::INFINITY),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContainingBlockPositionInfo {
|
||||||
|
pub fn new_with_inline_offsets(inline_start: Length, inline_end: Length) -> Self {
|
||||||
|
Self {
|
||||||
|
inline_start,
|
||||||
|
inline_end,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Data kept during layout about the floats in a given block formatting context.
|
/// Data kept during layout about the floats in a given block formatting context.
|
||||||
///
|
///
|
||||||
/// This is a persistent data structure. Each float has its own private copy of the float context,
|
/// This is a persistent data structure. Each float has its own private copy of the float context,
|
||||||
|
@ -40,6 +96,14 @@ pub struct FloatContext {
|
||||||
/// The current (logically) vertical position. No new floats may be placed (logically) above
|
/// The current (logically) vertical position. No new floats may be placed (logically) above
|
||||||
/// this line.
|
/// this line.
|
||||||
pub ceiling: Length,
|
pub ceiling: Length,
|
||||||
|
/// Details about the position of the containing block relative to the
|
||||||
|
/// independent block formatting context that contains all of the floats
|
||||||
|
/// this `FloatContext` positions.
|
||||||
|
pub containing_block_info: ContainingBlockPositionInfo,
|
||||||
|
/// The (logically) lowest margin edge of the last left float.
|
||||||
|
pub clear_left_position: Length,
|
||||||
|
/// The (logically) lowest margin edge of the last right float.
|
||||||
|
pub clear_right_position: Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatContext {
|
impl FloatContext {
|
||||||
|
@ -60,14 +124,12 @@ impl FloatContext {
|
||||||
FloatContext {
|
FloatContext {
|
||||||
bands,
|
bands,
|
||||||
ceiling: Length::zero(),
|
ceiling: Length::zero(),
|
||||||
|
containing_block_info: Default::default(),
|
||||||
|
clear_left_position: Length::zero(),
|
||||||
|
clear_right_position: Length::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current ceiling value. No new floats may be placed (logically) above this line.
|
|
||||||
pub fn ceiling(&self) -> Length {
|
|
||||||
self.ceiling
|
|
||||||
}
|
|
||||||
|
|
||||||
/// (Logically) lowers the ceiling to at least `new_ceiling` units.
|
/// (Logically) lowers the ceiling to at least `new_ceiling` units.
|
||||||
///
|
///
|
||||||
/// If the ceiling is already logically lower (i.e. larger) than this, does nothing.
|
/// If the ceiling is already logically lower (i.e. larger) than this, does nothing.
|
||||||
|
@ -81,9 +143,19 @@ impl FloatContext {
|
||||||
/// This should be used for placing inline elements and block formatting contexts so that they
|
/// This should be used for placing inline elements and block formatting contexts so that they
|
||||||
/// don't collide with floats.
|
/// don't collide with floats.
|
||||||
pub fn place_object(&self, object: &PlacementInfo) -> Vec2<Length> {
|
pub fn place_object(&self, object: &PlacementInfo) -> Vec2<Length> {
|
||||||
|
let ceiling = match object.clear {
|
||||||
|
ClearSide::None => self.ceiling,
|
||||||
|
ClearSide::Left => self.ceiling.max(self.clear_left_position),
|
||||||
|
ClearSide::Right => self.ceiling.max(self.clear_right_position),
|
||||||
|
ClearSide::Both => self
|
||||||
|
.ceiling
|
||||||
|
.max(self.clear_left_position)
|
||||||
|
.max(self.clear_right_position),
|
||||||
|
};
|
||||||
|
|
||||||
// Find the first band this float fits in.
|
// Find the first band this float fits in.
|
||||||
let mut first_band = self.bands.find(self.ceiling).unwrap();
|
let mut first_band = self.bands.find(ceiling).unwrap();
|
||||||
while !first_band.object_fits(&object) {
|
while !first_band.object_fits(&object, &self.containing_block_info) {
|
||||||
let next_band = self.bands.find_next(first_band.top).unwrap();
|
let next_band = self.bands.find_next(first_band.top).unwrap();
|
||||||
if next_band.top.px().is_infinite() {
|
if next_band.top.px().is_infinite() {
|
||||||
break;
|
break;
|
||||||
|
@ -95,8 +167,8 @@ impl FloatContext {
|
||||||
match object.side {
|
match object.side {
|
||||||
FloatSide::Left => {
|
FloatSide::Left => {
|
||||||
let left_object_edge = match first_band.left {
|
let left_object_edge = match first_band.left {
|
||||||
Some(band_left) => band_left.max(object.left_wall),
|
Some(band_left) => band_left.max(self.containing_block_info.inline_start),
|
||||||
None => object.left_wall,
|
None => self.containing_block_info.inline_start,
|
||||||
};
|
};
|
||||||
Vec2 {
|
Vec2 {
|
||||||
inline: left_object_edge,
|
inline: left_object_edge,
|
||||||
|
@ -105,8 +177,8 @@ impl FloatContext {
|
||||||
},
|
},
|
||||||
FloatSide::Right => {
|
FloatSide::Right => {
|
||||||
let right_object_edge = match first_band.right {
|
let right_object_edge = match first_band.right {
|
||||||
Some(band_right) => band_right.min(object.right_wall),
|
Some(band_right) => band_right.min(self.containing_block_info.inline_end),
|
||||||
None => object.right_wall,
|
None => self.containing_block_info.inline_end,
|
||||||
};
|
};
|
||||||
Vec2 {
|
Vec2 {
|
||||||
inline: right_object_edge - object.size.inline,
|
inline: right_object_edge - object.size.inline,
|
||||||
|
@ -129,6 +201,20 @@ impl FloatContext {
|
||||||
size: new_float.size.clone(),
|
size: new_float.size.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Update clear.
|
||||||
|
match new_float.side {
|
||||||
|
FloatSide::Left => {
|
||||||
|
self.clear_left_position = self
|
||||||
|
.clear_left_position
|
||||||
|
.max(new_float_rect.max_block_position())
|
||||||
|
},
|
||||||
|
FloatSide::Right => {
|
||||||
|
self.clear_right_position = self
|
||||||
|
.clear_right_position
|
||||||
|
.max(new_float_rect.max_block_position())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// Split the first band if necessary.
|
// Split the first band if necessary.
|
||||||
let mut first_band = self.bands.find(new_float_rect.start_corner.block).unwrap();
|
let mut first_band = self.bands.find(new_float_rect.start_corner.block).unwrap();
|
||||||
first_band.top = new_float_rect.start_corner.block;
|
first_band.top = new_float_rect.start_corner.block;
|
||||||
|
@ -164,12 +250,6 @@ pub struct PlacementInfo {
|
||||||
pub side: FloatSide,
|
pub side: FloatSide,
|
||||||
/// Which side or sides to clear floats on.
|
/// Which side or sides to clear floats on.
|
||||||
pub clear: ClearSide,
|
pub clear: ClearSide,
|
||||||
/// The distance from the logical left side of the block formatting context to the logical
|
|
||||||
/// left side of this object's containing block.
|
|
||||||
pub left_wall: Length,
|
|
||||||
/// The distance from the logical *left* side of the block formatting context to the logical
|
|
||||||
/// right side of this object's containing block.
|
|
||||||
pub right_wall: Length,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the float is left or right.
|
/// Whether the float is left or right.
|
||||||
|
@ -210,39 +290,41 @@ pub struct FloatBand {
|
||||||
pub right: Option<Length>,
|
pub right: Option<Length>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FloatSide {
|
||||||
|
fn from_style(style: &ComputedValues) -> Option<FloatSide> {
|
||||||
|
match style.get_box().float {
|
||||||
|
FloatProperty::None => None,
|
||||||
|
FloatProperty::Left => Some(FloatSide::Left),
|
||||||
|
FloatProperty::Right => Some(FloatSide::Right),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClearSide {
|
||||||
|
pub(crate) fn from_style(style: &ComputedValues) -> ClearSide {
|
||||||
|
match style.get_box().clear {
|
||||||
|
ClearProperty::None => ClearSide::None,
|
||||||
|
ClearProperty::Left => ClearSide::Left,
|
||||||
|
ClearProperty::Right => ClearSide::Right,
|
||||||
|
ClearProperty::Both => ClearSide::Both,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FloatBand {
|
impl FloatBand {
|
||||||
// Returns true if this band is clear of floats on the given side or sides.
|
|
||||||
fn is_clear(&self, side: ClearSide) -> bool {
|
|
||||||
match (side, self.left, self.right) {
|
|
||||||
(ClearSide::Left, Some(_), _) |
|
|
||||||
(ClearSide::Right, _, Some(_)) |
|
|
||||||
(ClearSide::Both, Some(_), _) |
|
|
||||||
(ClearSide::Both, _, Some(_)) => false,
|
|
||||||
(ClearSide::None, _, _) |
|
|
||||||
(ClearSide::Left, None, _) |
|
|
||||||
(ClearSide::Right, _, None) |
|
|
||||||
(ClearSide::Both, None, None) => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determines whether an object fits in a band.
|
// Determines whether an object fits in a band.
|
||||||
fn object_fits(&self, object: &PlacementInfo) -> bool {
|
fn object_fits(&self, object: &PlacementInfo, walls: &ContainingBlockPositionInfo) -> bool {
|
||||||
// If we must be clear on the given side and we aren't, this object doesn't fit.
|
|
||||||
if !self.is_clear(object.clear) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
match object.side {
|
match object.side {
|
||||||
FloatSide::Left => {
|
FloatSide::Left => {
|
||||||
// Compute a candidate left position for the object.
|
// Compute a candidate left position for the object.
|
||||||
let candidate_left = match self.left {
|
let candidate_left = match self.left {
|
||||||
None => object.left_wall,
|
None => walls.inline_start,
|
||||||
Some(left) => left.max(object.left_wall),
|
Some(left) => left.max(walls.inline_start),
|
||||||
};
|
};
|
||||||
|
|
||||||
// If this band has an existing left float in it, then make sure that the object
|
// If this band has an existing left float in it, then make sure that the object
|
||||||
// doesn't stick out past the right edge (rule 7).
|
// doesn't stick out past the right edge (rule 7).
|
||||||
if self.left.is_some() && candidate_left + object.size.inline > object.right_wall {
|
if self.left.is_some() && candidate_left + object.size.inline > walls.inline_end {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,13 +339,14 @@ impl FloatBand {
|
||||||
FloatSide::Right => {
|
FloatSide::Right => {
|
||||||
// Compute a candidate right position for the object.
|
// Compute a candidate right position for the object.
|
||||||
let candidate_right = match self.right {
|
let candidate_right = match self.right {
|
||||||
None => object.right_wall,
|
None => walls.inline_end,
|
||||||
Some(right) => right.min(object.right_wall),
|
Some(right) => right.min(walls.inline_end),
|
||||||
};
|
};
|
||||||
|
|
||||||
// If this band has an existing right float in it, then make sure that the new
|
// If this band has an existing right float in it, then make sure that the new
|
||||||
// object doesn't stick out past the left edge (rule 7).
|
// object doesn't stick out past the left edge (rule 7).
|
||||||
if self.right.is_some() && candidate_right - object.size.inline < object.left_wall {
|
if self.right.is_some() && candidate_right - object.size.inline < walls.inline_start
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,6 +623,12 @@ impl FloatBandLink {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for FloatFragment {
|
||||||
|
fn fmt(&self, formatter: &mut Formatter) -> FmtResult {
|
||||||
|
write!(formatter, "FloatFragment")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Float boxes
|
// Float boxes
|
||||||
|
|
||||||
impl FloatBox {
|
impl FloatBox {
|
||||||
|
@ -561,4 +650,247 @@ impl FloatBox {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn layout(
|
||||||
|
&mut self,
|
||||||
|
layout_context: &LayoutContext,
|
||||||
|
positioning_context: &mut PositioningContext,
|
||||||
|
containing_block: &ContainingBlock,
|
||||||
|
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||||
|
) -> Fragment {
|
||||||
|
let sequential_layout_state = sequential_layout_state
|
||||||
|
.as_mut()
|
||||||
|
.expect("Tried to lay out a float with no sequential placement state!");
|
||||||
|
|
||||||
|
// Speculate that the float ceiling will be located at the current block position plus the
|
||||||
|
// result of solving any margins we're building up. This is usually right, but it can be
|
||||||
|
// incorrect if there are more in-flow collapsible margins yet to be seen. An example
|
||||||
|
// showing when this can go wrong:
|
||||||
|
//
|
||||||
|
// <div style="margin: 5px"></div>
|
||||||
|
// <div style="float: left"></div>
|
||||||
|
// <div style="margin: 10px"></div>
|
||||||
|
//
|
||||||
|
// Assuming these are all in-flow, the float should be placed 10px down from the start, not
|
||||||
|
// 5px, but we can't know that because we haven't seen the block after this float yet.
|
||||||
|
//
|
||||||
|
// FIXME(pcwalton): Implement the proper behavior when speculation fails. Either detect it
|
||||||
|
// afterward and fix it up, or detect this situation ahead of time via lookahead and make
|
||||||
|
// sure `current_margin` is accurate before calling this method.
|
||||||
|
sequential_layout_state
|
||||||
|
.floats
|
||||||
|
.lower_ceiling(sequential_layout_state.current_block_position_including_margins());
|
||||||
|
|
||||||
|
let style = match self.contents {
|
||||||
|
IndependentFormattingContext::Replaced(ref replaced) => replaced.style.clone(),
|
||||||
|
IndependentFormattingContext::NonReplaced(ref non_replaced) => {
|
||||||
|
non_replaced.style.clone()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let float_context = &mut sequential_layout_state.floats;
|
||||||
|
let box_fragment = positioning_context.layout_maybe_position_relative_fragment(
|
||||||
|
layout_context,
|
||||||
|
containing_block,
|
||||||
|
&style,
|
||||||
|
|mut positioning_context| {
|
||||||
|
// Margin is computed this way regardless of whether the element is replaced
|
||||||
|
// or non-replaced.
|
||||||
|
let pbm = style.padding_border_margin(containing_block);
|
||||||
|
let margin = pbm.margin.auto_is(|| Length::zero());
|
||||||
|
let pbm_sums = &(&pbm.padding + &pbm.border) + &margin;
|
||||||
|
|
||||||
|
let (content_size, fragments);
|
||||||
|
match self.contents {
|
||||||
|
IndependentFormattingContext::NonReplaced(ref mut non_replaced) => {
|
||||||
|
// Calculate inline size.
|
||||||
|
// https://drafts.csswg.org/css2/#float-width
|
||||||
|
let box_size = non_replaced.style.content_box_size(&containing_block, &pbm);
|
||||||
|
let max_box_size = non_replaced
|
||||||
|
.style
|
||||||
|
.content_max_box_size(&containing_block, &pbm);
|
||||||
|
let min_box_size = non_replaced
|
||||||
|
.style
|
||||||
|
.content_min_box_size(&containing_block, &pbm)
|
||||||
|
.auto_is(Length::zero);
|
||||||
|
|
||||||
|
let tentative_inline_size = box_size.inline.auto_is(|| {
|
||||||
|
let available_size =
|
||||||
|
containing_block.inline_size - pbm_sums.inline_sum();
|
||||||
|
non_replaced
|
||||||
|
.inline_content_sizes(layout_context)
|
||||||
|
.shrink_to_fit(available_size)
|
||||||
|
});
|
||||||
|
let inline_size = tentative_inline_size
|
||||||
|
.clamp_between_extremums(min_box_size.inline, max_box_size.inline);
|
||||||
|
|
||||||
|
// Calculate block size.
|
||||||
|
// https://drafts.csswg.org/css2/#block-root-margin
|
||||||
|
// FIXME(pcwalton): Is a tree rank of zero correct here?
|
||||||
|
let containing_block_for_children = ContainingBlock {
|
||||||
|
inline_size,
|
||||||
|
block_size: box_size.block,
|
||||||
|
style: &non_replaced.style,
|
||||||
|
};
|
||||||
|
let independent_layout = non_replaced.layout(
|
||||||
|
layout_context,
|
||||||
|
&mut positioning_context,
|
||||||
|
&containing_block_for_children,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
content_size = Vec2 {
|
||||||
|
inline: inline_size,
|
||||||
|
block: box_size
|
||||||
|
.block
|
||||||
|
.auto_is(|| independent_layout.content_block_size),
|
||||||
|
};
|
||||||
|
fragments = independent_layout.fragments;
|
||||||
|
},
|
||||||
|
IndependentFormattingContext::Replaced(ref replaced) => {
|
||||||
|
// https://drafts.csswg.org/css2/#float-replaced-width
|
||||||
|
// https://drafts.csswg.org/css2/#inline-replaced-height
|
||||||
|
content_size = replaced.contents.used_size_as_if_inline_element(
|
||||||
|
&containing_block,
|
||||||
|
&replaced.style,
|
||||||
|
None,
|
||||||
|
&pbm,
|
||||||
|
);
|
||||||
|
fragments = replaced
|
||||||
|
.contents
|
||||||
|
.make_fragments(&replaced.style, content_size.clone());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let margin_box_start_corner = float_context.add_float(&PlacementInfo {
|
||||||
|
size: &content_size + &pbm_sums.sum(),
|
||||||
|
side: FloatSide::from_style(&style).expect("Float box wasn't floated!"),
|
||||||
|
clear: ClearSide::from_style(&style),
|
||||||
|
});
|
||||||
|
|
||||||
|
let content_rect = Rect {
|
||||||
|
start_corner: &margin_box_start_corner + &pbm_sums.start_offset(),
|
||||||
|
size: content_size.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Clearance is handled internally by the float placement logic, so there's no need
|
||||||
|
// to store it explicitly in the fragment.
|
||||||
|
let clearance = Length::zero();
|
||||||
|
|
||||||
|
BoxFragment::new(
|
||||||
|
self.contents.base_fragment_info(),
|
||||||
|
style.clone(),
|
||||||
|
fragments,
|
||||||
|
content_rect,
|
||||||
|
pbm.padding,
|
||||||
|
pbm.border,
|
||||||
|
margin,
|
||||||
|
clearance,
|
||||||
|
CollapsedBlockMargins::zero(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Fragment::Float(box_fragment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float fragment storage
|
||||||
|
|
||||||
|
// Layout state that we maintain when doing sequential traversals of the box tree in document
|
||||||
|
// order.
|
||||||
|
//
|
||||||
|
// This data is only needed for float placement and float interaction, and as such is only present
|
||||||
|
// if the current block formatting context contains floats.
|
||||||
|
//
|
||||||
|
// All coordinates here are relative to the start of the nearest ancestor block formatting context.
|
||||||
|
//
|
||||||
|
// This structure is expected to be cheap to clone, in order to allow for "snapshots" that enable
|
||||||
|
// restarting layout at any point in the tree.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct SequentialLayoutState {
|
||||||
|
// Holds all floats in this block formatting context.
|
||||||
|
pub(crate) floats: FloatContext,
|
||||||
|
// The (logically) bottom border edge or top padding edge of the last in-flow block. Floats
|
||||||
|
// cannot be placed above this line.
|
||||||
|
//
|
||||||
|
// This is often, but not always, the same as the float ceiling. The float ceiling can be lower
|
||||||
|
// than this value because this value is calculated based on in-flow boxes only, while
|
||||||
|
// out-of-flow floats can affect the ceiling as well (see CSS 2.1 § 9.5.1 rule 6).
|
||||||
|
pub(crate) bfc_relative_block_position: Length,
|
||||||
|
// Any collapsible margins that we've encountered after `bfc_relative_block_position`.
|
||||||
|
pub(crate) current_margin: CollapsedMargin,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SequentialLayoutState {
|
||||||
|
// Creates a new empty `SequentialLayoutState`.
|
||||||
|
pub(crate) fn new() -> SequentialLayoutState {
|
||||||
|
SequentialLayoutState {
|
||||||
|
floats: FloatContext::new(),
|
||||||
|
current_margin: CollapsedMargin::zero(),
|
||||||
|
bfc_relative_block_position: Length::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moves the current block position (logically) down by `block_distance`.
|
||||||
|
//
|
||||||
|
// Floats may not be placed higher than the current block position.
|
||||||
|
pub(crate) fn advance_block_position(&mut self, block_distance: Length) {
|
||||||
|
self.bfc_relative_block_position += block_distance;
|
||||||
|
self.floats.lower_ceiling(self.bfc_relative_block_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn update_all_containing_block_offsets(
|
||||||
|
&mut self,
|
||||||
|
mut new_distance: ContainingBlockPositionInfo,
|
||||||
|
) -> ContainingBlockPositionInfo {
|
||||||
|
mem::swap(&mut new_distance, &mut self.floats.containing_block_info);
|
||||||
|
new_distance
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn current_block_position_including_margins(&self) -> Length {
|
||||||
|
self.bfc_relative_block_position + self.current_margin.solve()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collapses margins, moving the block position down by the collapsed value of `current_margin`
|
||||||
|
// and resetting `current_margin` to zero.
|
||||||
|
//
|
||||||
|
// Call this method before laying out children when it is known that the start margin of the
|
||||||
|
// current fragment can't collapse with the margins of any of its children.
|
||||||
|
pub(crate) fn collapse_margins(&mut self) {
|
||||||
|
self.advance_block_position(self.current_margin.solve());
|
||||||
|
self.current_margin = CollapsedMargin::zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the amount of clearance that a block with the given `clear` value at the current
|
||||||
|
// `bfc_relative_block_position` (with top margin included in `current_margin` if applicable)
|
||||||
|
// needs to have.
|
||||||
|
//
|
||||||
|
// https://www.w3.org/TR/2011/REC-CSS2-20110607/visuren.html#flow-control
|
||||||
|
pub(crate) fn calculate_clearance(&self, clear_side: ClearSide) -> Length {
|
||||||
|
if clear_side == ClearSide::None {
|
||||||
|
return Length::zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
let hypothetical_block_position = self.current_block_position_including_margins();
|
||||||
|
let clear_position = match clear_side {
|
||||||
|
ClearSide::None => unreachable!(),
|
||||||
|
ClearSide::Left => self
|
||||||
|
.floats
|
||||||
|
.clear_left_position
|
||||||
|
.max(hypothetical_block_position),
|
||||||
|
ClearSide::Right => self
|
||||||
|
.floats
|
||||||
|
.clear_right_position
|
||||||
|
.max(hypothetical_block_position),
|
||||||
|
ClearSide::Both => self
|
||||||
|
.floats
|
||||||
|
.clear_left_position
|
||||||
|
.max(self.floats.clear_right_position)
|
||||||
|
.max(hypothetical_block_position),
|
||||||
|
};
|
||||||
|
clear_position - hypothetical_block_position
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new adjoining margin.
|
||||||
|
pub(crate) fn adjoin_assign(&mut self, margin: &CollapsedMargin) {
|
||||||
|
self.current_margin.adjoin_assign(margin)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use crate::cell::ArcRefCell;
|
use crate::cell::ArcRefCell;
|
||||||
use crate::context::LayoutContext;
|
use crate::context::LayoutContext;
|
||||||
use crate::flow::float::FloatBox;
|
use crate::flow::float::{FloatBox, SequentialLayoutState};
|
||||||
use crate::flow::FlowLayout;
|
use crate::flow::FlowLayout;
|
||||||
use crate::formatting_contexts::IndependentFormattingContext;
|
use crate::formatting_contexts::IndependentFormattingContext;
|
||||||
use crate::fragment_tree::BaseFragmentInfo;
|
use crate::fragment_tree::BaseFragmentInfo;
|
||||||
|
@ -99,6 +99,7 @@ struct InlineFormattingContextState<'box_tree, 'a, 'b> {
|
||||||
inline_position: Length,
|
inline_position: Length,
|
||||||
partial_inline_boxes_stack: Vec<PartialInlineBoxFragment<'box_tree>>,
|
partial_inline_boxes_stack: Vec<PartialInlineBoxFragment<'box_tree>>,
|
||||||
current_nesting_level: InlineNestingLevelState<'box_tree>,
|
current_nesting_level: InlineNestingLevelState<'box_tree>,
|
||||||
|
sequential_layout_state: Option<&'a mut SequentialLayoutState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'box_tree, 'a, 'b> InlineFormattingContextState<'box_tree, 'a, 'b> {
|
impl<'box_tree, 'a, 'b> InlineFormattingContextState<'box_tree, 'a, 'b> {
|
||||||
|
@ -271,6 +272,7 @@ impl InlineFormattingContext {
|
||||||
positioning_context: &mut PositioningContext,
|
positioning_context: &mut PositioningContext,
|
||||||
containing_block: &ContainingBlock,
|
containing_block: &ContainingBlock,
|
||||||
tree_rank: usize,
|
tree_rank: usize,
|
||||||
|
sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||||
) -> FlowLayout {
|
) -> FlowLayout {
|
||||||
let mut ifc = InlineFormattingContextState {
|
let mut ifc = InlineFormattingContextState {
|
||||||
positioning_context,
|
positioning_context,
|
||||||
|
@ -298,8 +300,15 @@ impl InlineFormattingContext {
|
||||||
positioning_context: None,
|
positioning_context: None,
|
||||||
text_decoration_line: self.text_decoration_line,
|
text_decoration_line: self.text_decoration_line,
|
||||||
},
|
},
|
||||||
|
sequential_layout_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// FIXME(pcwalton): This assumes that margins never collapse through inline formatting
|
||||||
|
// contexts (i.e. that inline formatting contexts are never empty). Is that right?
|
||||||
|
if let Some(ref mut sequential_layout_state) = ifc.sequential_layout_state {
|
||||||
|
sequential_layout_state.collapse_margins();
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Some(child) = ifc.current_nesting_level.remaining_boxes.next() {
|
if let Some(child) = ifc.current_nesting_level.remaining_boxes.next() {
|
||||||
match &mut *child.borrow_mut() {
|
match &mut *child.borrow_mut() {
|
||||||
|
@ -342,8 +351,30 @@ impl InlineFormattingContext {
|
||||||
.fragments_so_far
|
.fragments_so_far
|
||||||
.push(Fragment::AbsoluteOrFixedPositioned(hoisted_fragment));
|
.push(Fragment::AbsoluteOrFixedPositioned(hoisted_fragment));
|
||||||
},
|
},
|
||||||
InlineLevelBox::OutOfFlowFloatBox(_box_) => {
|
InlineLevelBox::OutOfFlowFloatBox(box_) => {
|
||||||
// TODO
|
let mut fragment = box_.layout(
|
||||||
|
layout_context,
|
||||||
|
ifc.positioning_context,
|
||||||
|
containing_block,
|
||||||
|
ifc.sequential_layout_state.as_mut().map(|c| &mut **c),
|
||||||
|
);
|
||||||
|
if let Some(state) = &ifc.sequential_layout_state {
|
||||||
|
let offset_from_formatting_context_to_containing_block = Vec2 {
|
||||||
|
inline: state.floats.containing_block_info.inline_start,
|
||||||
|
block: state.floats.containing_block_info.block_start +
|
||||||
|
state
|
||||||
|
.floats
|
||||||
|
.containing_block_info
|
||||||
|
.block_start_margins_not_collapsed
|
||||||
|
.solve(),
|
||||||
|
};
|
||||||
|
if let Fragment::Float(ref mut box_fragment) = &mut fragment {
|
||||||
|
box_fragment.content_rect.start_corner =
|
||||||
|
&box_fragment.content_rect.start_corner -
|
||||||
|
&offset_from_formatting_context_to_containing_block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ifc.current_nesting_level.fragments_so_far.push(fragment);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
@ -360,6 +391,7 @@ impl InlineFormattingContext {
|
||||||
ifc.lines.finish_line(
|
ifc.lines.finish_line(
|
||||||
&mut ifc.current_nesting_level,
|
&mut ifc.current_nesting_level,
|
||||||
containing_block,
|
containing_block,
|
||||||
|
ifc.sequential_layout_state,
|
||||||
ifc.inline_position,
|
ifc.inline_position,
|
||||||
);
|
);
|
||||||
return FlowLayout {
|
return FlowLayout {
|
||||||
|
@ -377,6 +409,7 @@ impl Lines {
|
||||||
&mut self,
|
&mut self,
|
||||||
top_nesting_level: &mut InlineNestingLevelState,
|
top_nesting_level: &mut InlineNestingLevelState,
|
||||||
containing_block: &ContainingBlock,
|
containing_block: &ContainingBlock,
|
||||||
|
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||||
line_content_inline_size: Length,
|
line_content_inline_size: Length,
|
||||||
) {
|
) {
|
||||||
let mut line_contents = std::mem::take(&mut top_nesting_level.fragments_so_far);
|
let mut line_contents = std::mem::take(&mut top_nesting_level.fragments_so_far);
|
||||||
|
@ -430,7 +463,11 @@ impl Lines {
|
||||||
inline: containing_block.inline_size,
|
inline: containing_block.inline_size,
|
||||||
block: line_block_size,
|
block: line_block_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.next_line_block_position += size.block;
|
self.next_line_block_position += size.block;
|
||||||
|
if let Some(ref mut sequential_layout_state) = sequential_layout_state {
|
||||||
|
sequential_layout_state.advance_block_position(size.block);
|
||||||
|
}
|
||||||
|
|
||||||
self.fragments
|
self.fragments
|
||||||
.push(Fragment::Anonymous(AnonymousFragment::new(
|
.push(Fragment::Anonymous(AnonymousFragment::new(
|
||||||
|
@ -519,6 +556,7 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
|
||||||
self.padding.clone(),
|
self.padding.clone(),
|
||||||
self.border.clone(),
|
self.border.clone(),
|
||||||
self.margin.clone(),
|
self.margin.clone(),
|
||||||
|
Length::zero(),
|
||||||
CollapsedBlockMargins::zero(),
|
CollapsedBlockMargins::zero(),
|
||||||
);
|
);
|
||||||
let last_fragment = self.last_box_tree_fragment && !at_line_break;
|
let last_fragment = self.last_box_tree_fragment && !at_line_break;
|
||||||
|
@ -583,6 +621,7 @@ fn layout_atomic(
|
||||||
pbm.padding,
|
pbm.padding,
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
|
Length::zero(),
|
||||||
CollapsedBlockMargins::zero(),
|
CollapsedBlockMargins::zero(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -658,6 +697,7 @@ fn layout_atomic(
|
||||||
pbm.padding,
|
pbm.padding,
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
|
Length::zero(),
|
||||||
CollapsedBlockMargins::zero(),
|
CollapsedBlockMargins::zero(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -861,8 +901,12 @@ impl TextRun {
|
||||||
partial.parent_nesting_level.inline_start = Length::zero();
|
partial.parent_nesting_level.inline_start = Length::zero();
|
||||||
nesting_level = &mut partial.parent_nesting_level;
|
nesting_level = &mut partial.parent_nesting_level;
|
||||||
}
|
}
|
||||||
ifc.lines
|
ifc.lines.finish_line(
|
||||||
.finish_line(nesting_level, ifc.containing_block, ifc.inline_position);
|
nesting_level,
|
||||||
|
ifc.containing_block,
|
||||||
|
ifc.sequential_layout_state.as_mut().map(|c| &mut **c),
|
||||||
|
ifc.inline_position,
|
||||||
|
);
|
||||||
ifc.inline_position = Length::zero();
|
ifc.inline_position = Length::zero();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,17 @@
|
||||||
|
|
||||||
//! Flow layout, also known as block-and-inline layout.
|
//! Flow layout, also known as block-and-inline layout.
|
||||||
|
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
use crate::cell::ArcRefCell;
|
use crate::cell::ArcRefCell;
|
||||||
use crate::context::LayoutContext;
|
use crate::context::LayoutContext;
|
||||||
use crate::flow::float::{FloatBox, FloatContext};
|
use crate::flow::float::{ClearSide, ContainingBlockPositionInfo, FloatBox, SequentialLayoutState};
|
||||||
use crate::flow::inline::InlineFormattingContext;
|
use crate::flow::inline::InlineFormattingContext;
|
||||||
use crate::formatting_contexts::{
|
use crate::formatting_contexts::{
|
||||||
IndependentFormattingContext, IndependentLayout, NonReplacedFormattingContext,
|
IndependentFormattingContext, IndependentLayout, NonReplacedFormattingContext,
|
||||||
};
|
};
|
||||||
use crate::fragment_tree::BaseFragmentInfo;
|
use crate::fragment_tree::BaseFragmentInfo;
|
||||||
use crate::fragments::{
|
use crate::fragments::{BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment};
|
||||||
AnonymousFragment, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment,
|
|
||||||
};
|
|
||||||
use crate::geom::flow_relative::{Rect, Sides, Vec2};
|
use crate::geom::flow_relative::{Rect, Sides, Vec2};
|
||||||
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
|
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
|
||||||
use crate::replaced::ReplacedContent;
|
use crate::replaced::ReplacedContent;
|
||||||
|
@ -78,26 +78,26 @@ impl BlockFormattingContext {
|
||||||
containing_block: &ContainingBlock,
|
containing_block: &ContainingBlock,
|
||||||
tree_rank: usize,
|
tree_rank: usize,
|
||||||
) -> IndependentLayout {
|
) -> IndependentLayout {
|
||||||
let mut float_context;
|
let mut sequential_layout_state = if self.contains_floats || !layout_context.use_rayon {
|
||||||
let float_context = if self.contains_floats {
|
Some(SequentialLayoutState::new())
|
||||||
float_context = FloatContext::new();
|
|
||||||
Some(&mut float_context)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let flow_layout = self.contents.layout(
|
let flow_layout = self.contents.layout(
|
||||||
layout_context,
|
layout_context,
|
||||||
positioning_context,
|
positioning_context,
|
||||||
containing_block,
|
containing_block,
|
||||||
tree_rank,
|
tree_rank,
|
||||||
float_context,
|
sequential_layout_state.as_mut(),
|
||||||
CollapsibleWithParentStartMargin(false),
|
CollapsibleWithParentStartMargin(false),
|
||||||
);
|
);
|
||||||
assert!(
|
debug_assert!(
|
||||||
!flow_layout
|
!flow_layout
|
||||||
.collapsible_margins_in_children
|
.collapsible_margins_in_children
|
||||||
.collapsed_through
|
.collapsed_through
|
||||||
);
|
);
|
||||||
|
|
||||||
IndependentLayout {
|
IndependentLayout {
|
||||||
fragments: flow_layout.fragments,
|
fragments: flow_layout.fragments,
|
||||||
content_block_size: flow_layout.content_block_size +
|
content_block_size: flow_layout.content_block_size +
|
||||||
|
@ -113,7 +113,7 @@ impl BlockContainer {
|
||||||
positioning_context: &mut PositioningContext,
|
positioning_context: &mut PositioningContext,
|
||||||
containing_block: &ContainingBlock,
|
containing_block: &ContainingBlock,
|
||||||
tree_rank: usize,
|
tree_rank: usize,
|
||||||
float_context: Option<&mut FloatContext>,
|
sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||||
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
|
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
|
||||||
) -> FlowLayout {
|
) -> FlowLayout {
|
||||||
match self {
|
match self {
|
||||||
|
@ -123,7 +123,7 @@ impl BlockContainer {
|
||||||
child_boxes,
|
child_boxes,
|
||||||
containing_block,
|
containing_block,
|
||||||
tree_rank,
|
tree_rank,
|
||||||
float_context,
|
sequential_layout_state,
|
||||||
collapsible_with_parent_start_margin,
|
collapsible_with_parent_start_margin,
|
||||||
),
|
),
|
||||||
BlockContainer::InlineFormattingContext(ifc) => ifc.layout(
|
BlockContainer::InlineFormattingContext(ifc) => ifc.layout(
|
||||||
|
@ -131,6 +131,7 @@ impl BlockContainer {
|
||||||
positioning_context,
|
positioning_context,
|
||||||
containing_block,
|
containing_block,
|
||||||
tree_rank,
|
tree_rank,
|
||||||
|
sequential_layout_state,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,97 +170,44 @@ fn layout_block_level_children(
|
||||||
child_boxes: &[ArcRefCell<BlockLevelBox>],
|
child_boxes: &[ArcRefCell<BlockLevelBox>],
|
||||||
containing_block: &ContainingBlock,
|
containing_block: &ContainingBlock,
|
||||||
tree_rank: usize,
|
tree_rank: usize,
|
||||||
mut float_context: Option<&mut FloatContext>,
|
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||||
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
|
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
|
||||||
) -> FlowLayout {
|
) -> FlowLayout {
|
||||||
fn place_block_level_fragment(fragment: &mut Fragment, placement_state: &mut PlacementState) {
|
match sequential_layout_state {
|
||||||
match fragment {
|
Some(ref mut sequential_layout_state) => layout_block_level_children_sequentially(
|
||||||
Fragment::Box(fragment) => {
|
|
||||||
let fragment_block_margins = &fragment.block_margins_collapsed_with_children;
|
|
||||||
let fragment_block_size = fragment.padding.block_sum() +
|
|
||||||
fragment.border.block_sum() +
|
|
||||||
fragment.content_rect.size.block;
|
|
||||||
|
|
||||||
if placement_state.next_in_flow_margin_collapses_with_parent_start_margin {
|
|
||||||
assert_eq!(placement_state.current_margin.solve(), Length::zero());
|
|
||||||
placement_state
|
|
||||||
.start_margin
|
|
||||||
.adjoin_assign(&fragment_block_margins.start);
|
|
||||||
if fragment_block_margins.collapsed_through {
|
|
||||||
placement_state
|
|
||||||
.start_margin
|
|
||||||
.adjoin_assign(&fragment_block_margins.end);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
placement_state.next_in_flow_margin_collapses_with_parent_start_margin = false;
|
|
||||||
} else {
|
|
||||||
placement_state
|
|
||||||
.current_margin
|
|
||||||
.adjoin_assign(&fragment_block_margins.start);
|
|
||||||
}
|
|
||||||
fragment.content_rect.start_corner.block += placement_state.current_margin.solve() +
|
|
||||||
placement_state.current_block_direction_position;
|
|
||||||
if fragment_block_margins.collapsed_through {
|
|
||||||
placement_state
|
|
||||||
.current_margin
|
|
||||||
.adjoin_assign(&fragment_block_margins.end);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
placement_state.current_block_direction_position +=
|
|
||||||
placement_state.current_margin.solve() + fragment_block_size;
|
|
||||||
placement_state.current_margin = fragment_block_margins.end;
|
|
||||||
},
|
|
||||||
Fragment::AbsoluteOrFixedPositioned(fragment) => {
|
|
||||||
let offset = Vec2 {
|
|
||||||
block: placement_state.current_margin.solve() +
|
|
||||||
placement_state.current_block_direction_position,
|
|
||||||
inline: Length::new(0.),
|
|
||||||
};
|
|
||||||
fragment.borrow_mut().adjust_offsets(offset);
|
|
||||||
},
|
|
||||||
Fragment::Anonymous(_) => {},
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PlacementState {
|
|
||||||
next_in_flow_margin_collapses_with_parent_start_margin: bool,
|
|
||||||
start_margin: CollapsedMargin,
|
|
||||||
current_margin: CollapsedMargin,
|
|
||||||
current_block_direction_position: Length,
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut placement_state = PlacementState {
|
|
||||||
next_in_flow_margin_collapses_with_parent_start_margin:
|
|
||||||
collapsible_with_parent_start_margin.0,
|
|
||||||
start_margin: CollapsedMargin::zero(),
|
|
||||||
current_margin: CollapsedMargin::zero(),
|
|
||||||
current_block_direction_position: Length::zero(),
|
|
||||||
};
|
|
||||||
let fragments = positioning_context.adjust_static_positions(tree_rank, |positioning_context| {
|
|
||||||
if float_context.is_some() || !layout_context.use_rayon {
|
|
||||||
// Because floats are involved, we do layout for this block formatting context
|
|
||||||
// in tree order without parallelism. This enables mutable access
|
|
||||||
// to a `FloatContext` that tracks every float encountered so far (again in tree order).
|
|
||||||
child_boxes
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(tree_rank, box_)| {
|
|
||||||
let mut fragment = box_.borrow_mut().layout(
|
|
||||||
layout_context,
|
layout_context,
|
||||||
positioning_context,
|
positioning_context,
|
||||||
|
child_boxes,
|
||||||
containing_block,
|
containing_block,
|
||||||
tree_rank,
|
tree_rank,
|
||||||
float_context.as_mut().map(|c| &mut **c),
|
sequential_layout_state,
|
||||||
);
|
collapsible_with_parent_start_margin,
|
||||||
place_block_level_fragment(&mut fragment, &mut placement_state);
|
),
|
||||||
fragment
|
None => layout_block_level_children_in_parallel(
|
||||||
})
|
layout_context,
|
||||||
.collect()
|
positioning_context,
|
||||||
} else {
|
child_boxes,
|
||||||
|
containing_block,
|
||||||
|
tree_rank,
|
||||||
|
collapsible_with_parent_start_margin,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout_block_level_children_in_parallel(
|
||||||
|
layout_context: &LayoutContext,
|
||||||
|
positioning_context: &mut PositioningContext,
|
||||||
|
child_boxes: &[ArcRefCell<BlockLevelBox>],
|
||||||
|
containing_block: &ContainingBlock,
|
||||||
|
tree_rank: usize,
|
||||||
|
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
|
||||||
|
) -> FlowLayout {
|
||||||
|
let mut placement_state = PlacementState::new(collapsible_with_parent_start_margin);
|
||||||
|
|
||||||
|
let fragments = positioning_context.adjust_static_positions(tree_rank, |positioning_context| {
|
||||||
let collects_for_nearest_positioned_ancestor =
|
let collects_for_nearest_positioned_ancestor =
|
||||||
positioning_context.collects_for_nearest_positioned_ancestor();
|
positioning_context.collects_for_nearest_positioned_ancestor();
|
||||||
let mut fragments = child_boxes
|
let mut fragments: Vec<Fragment> = child_boxes
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.mapfold_reduce_into(
|
.mapfold_reduce_into(
|
||||||
|
@ -270,29 +218,65 @@ fn layout_block_level_children(
|
||||||
positioning_context,
|
positioning_context,
|
||||||
containing_block,
|
containing_block,
|
||||||
tree_rank,
|
tree_rank,
|
||||||
/* float_context = */ None,
|
/* sequential_layout_state = */ None,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|| PositioningContext::new_for_rayon(collects_for_nearest_positioned_ancestor),
|
|| PositioningContext::new_for_rayon(collects_for_nearest_positioned_ancestor),
|
||||||
PositioningContext::append,
|
PositioningContext::append,
|
||||||
)
|
)
|
||||||
.collect();
|
.collect();
|
||||||
for fragment in &mut fragments {
|
for fragment in fragments.iter_mut() {
|
||||||
place_block_level_fragment(fragment, &mut placement_state)
|
placement_state.place_fragment(fragment);
|
||||||
}
|
}
|
||||||
fragments
|
fragments
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
FlowLayout {
|
FlowLayout {
|
||||||
fragments,
|
fragments,
|
||||||
content_block_size: placement_state.current_block_direction_position,
|
content_block_size: placement_state.current_block_direction_position,
|
||||||
collapsible_margins_in_children: CollapsedBlockMargins {
|
collapsible_margins_in_children: placement_state.collapsible_margins_in_children(),
|
||||||
collapsed_through: placement_state
|
}
|
||||||
.next_in_flow_margin_collapses_with_parent_start_margin,
|
}
|
||||||
start: placement_state.start_margin,
|
|
||||||
end: placement_state.current_margin,
|
fn layout_block_level_children_sequentially(
|
||||||
},
|
layout_context: &LayoutContext,
|
||||||
|
positioning_context: &mut PositioningContext,
|
||||||
|
child_boxes: &[ArcRefCell<BlockLevelBox>],
|
||||||
|
containing_block: &ContainingBlock,
|
||||||
|
tree_rank: usize,
|
||||||
|
sequential_layout_state: &mut SequentialLayoutState,
|
||||||
|
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
|
||||||
|
) -> FlowLayout {
|
||||||
|
let mut placement_state = PlacementState::new(collapsible_with_parent_start_margin);
|
||||||
|
|
||||||
|
let fragments = positioning_context.adjust_static_positions(tree_rank, |positioning_context| {
|
||||||
|
// Because floats are involved, we do layout for this block formatting context in tree
|
||||||
|
// order without parallelism. This enables mutable access to a `SequentialLayoutState` that
|
||||||
|
// tracks every float encountered so far (again in tree order).
|
||||||
|
child_boxes
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(tree_rank, child_box)| {
|
||||||
|
let mut fragment = child_box.borrow_mut().layout(
|
||||||
|
layout_context,
|
||||||
|
positioning_context,
|
||||||
|
containing_block,
|
||||||
|
tree_rank,
|
||||||
|
Some(&mut *sequential_layout_state),
|
||||||
|
);
|
||||||
|
|
||||||
|
placement_state.place_fragment(&mut fragment);
|
||||||
|
placement_state
|
||||||
|
.adjust_positions_of_float_children(&mut fragment, sequential_layout_state);
|
||||||
|
fragment
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
FlowLayout {
|
||||||
|
fragments,
|
||||||
|
content_block_size: placement_state.current_block_direction_position,
|
||||||
|
collapsible_margins_in_children: placement_state.collapsible_margins_in_children(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +287,7 @@ impl BlockLevelBox {
|
||||||
positioning_context: &mut PositioningContext,
|
positioning_context: &mut PositioningContext,
|
||||||
containing_block: &ContainingBlock,
|
containing_block: &ContainingBlock,
|
||||||
tree_rank: usize,
|
tree_rank: usize,
|
||||||
float_context: Option<&mut FloatContext>,
|
sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||||
) -> Fragment {
|
) -> Fragment {
|
||||||
match self {
|
match self {
|
||||||
BlockLevelBox::SameFormattingContextBlock {
|
BlockLevelBox::SameFormattingContextBlock {
|
||||||
|
@ -323,7 +307,7 @@ impl BlockLevelBox {
|
||||||
style,
|
style,
|
||||||
NonReplacedContents::SameFormattingContextBlock(contents),
|
NonReplacedContents::SameFormattingContextBlock(contents),
|
||||||
tree_rank,
|
tree_rank,
|
||||||
float_context,
|
sequential_layout_state,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
|
@ -339,6 +323,7 @@ impl BlockLevelBox {
|
||||||
replaced.base_fragment_info,
|
replaced.base_fragment_info,
|
||||||
&replaced.style,
|
&replaced.style,
|
||||||
&replaced.contents,
|
&replaced.contents,
|
||||||
|
sequential_layout_state,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
@ -359,7 +344,7 @@ impl BlockLevelBox {
|
||||||
non_replaced,
|
non_replaced,
|
||||||
),
|
),
|
||||||
tree_rank,
|
tree_rank,
|
||||||
float_context,
|
sequential_layout_state,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
@ -379,12 +364,12 @@ impl BlockLevelBox {
|
||||||
positioning_context.push(hoisted_box);
|
positioning_context.push(hoisted_box);
|
||||||
Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)
|
Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)
|
||||||
},
|
},
|
||||||
BlockLevelBox::OutOfFlowFloatBox(_box_) => {
|
BlockLevelBox::OutOfFlowFloatBox(box_) => box_.layout(
|
||||||
// FIXME: call layout_maybe_position_relative_fragment here
|
layout_context,
|
||||||
Fragment::Anonymous(AnonymousFragment::no_op(
|
positioning_context,
|
||||||
containing_block.style.writing_mode,
|
containing_block,
|
||||||
))
|
sequential_layout_state,
|
||||||
},
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +410,7 @@ fn layout_in_flow_non_replaced_block_level(
|
||||||
style: &Arc<ComputedValues>,
|
style: &Arc<ComputedValues>,
|
||||||
block_level_kind: NonReplacedContents,
|
block_level_kind: NonReplacedContents,
|
||||||
tree_rank: usize,
|
tree_rank: usize,
|
||||||
float_context: Option<&mut FloatContext>,
|
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||||
) -> BoxFragment {
|
) -> BoxFragment {
|
||||||
let pbm = style.padding_border_margin(containing_block);
|
let pbm = style.padding_border_margin(containing_block);
|
||||||
let box_size = style.content_box_size(containing_block, &pbm);
|
let box_size = style.content_box_size(containing_block, &pbm);
|
||||||
|
@ -479,37 +464,90 @@ fn layout_in_flow_non_replaced_block_level(
|
||||||
block_size,
|
block_size,
|
||||||
style,
|
style,
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
|
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
containing_block.style.writing_mode, containing_block_for_children.style.writing_mode,
|
containing_block.style.writing_mode, containing_block_for_children.style.writing_mode,
|
||||||
"Mixed writing modes are not supported yet"
|
"Mixed writing modes are not supported yet"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let block_is_same_formatting_context = match block_level_kind {
|
||||||
|
NonReplacedContents::SameFormattingContextBlock(_) => true,
|
||||||
|
NonReplacedContents::EstablishesAnIndependentFormattingContext(_) => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let start_margin_can_collapse_with_children = block_is_same_formatting_context &&
|
||||||
|
pbm.padding.block_start == Length::zero() &&
|
||||||
|
pbm.border.block_start == Length::zero();
|
||||||
|
let end_margin_can_collapse_with_children = block_is_same_formatting_context &&
|
||||||
|
pbm.padding.block_end == Length::zero() &&
|
||||||
|
pbm.border.block_end == Length::zero() &&
|
||||||
|
block_size == LengthOrAuto::Auto &&
|
||||||
|
min_box_size.block == Length::zero();
|
||||||
|
|
||||||
|
let mut clearance = Length::zero();
|
||||||
|
let parent_containing_block_position_info;
|
||||||
|
match sequential_layout_state {
|
||||||
|
None => parent_containing_block_position_info = None,
|
||||||
|
Some(ref mut sequential_layout_state) => {
|
||||||
|
sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_start));
|
||||||
|
if !start_margin_can_collapse_with_children {
|
||||||
|
sequential_layout_state.collapse_margins();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Introduce clearance if necessary.
|
||||||
|
let clear_side = ClearSide::from_style(style);
|
||||||
|
clearance = sequential_layout_state.calculate_clearance(clear_side);
|
||||||
|
|
||||||
|
// NB: This will be a no-op if we're collapsing margins with our children since that
|
||||||
|
// can only happen if we have no block-start padding and border.
|
||||||
|
sequential_layout_state.advance_block_position(
|
||||||
|
pbm.padding.block_start + pbm.border.block_start + clearance,
|
||||||
|
);
|
||||||
|
|
||||||
|
// We are about to lay out children. Update the offset between the block formatting
|
||||||
|
// context and the containing block that we create for them. This offset is used to
|
||||||
|
// ajust BFC relative coordinates to coordinates that are relative to our content box.
|
||||||
|
// Our content box establishes the containing block for non-abspos children, including
|
||||||
|
// floats.
|
||||||
|
let inline_start = sequential_layout_state
|
||||||
|
.floats
|
||||||
|
.containing_block_info
|
||||||
|
.inline_start +
|
||||||
|
pbm.padding.inline_start +
|
||||||
|
pbm.border.inline_start +
|
||||||
|
margin.inline_start;
|
||||||
|
let new_cb_offsets = ContainingBlockPositionInfo {
|
||||||
|
block_start: sequential_layout_state.bfc_relative_block_position,
|
||||||
|
block_start_margins_not_collapsed: sequential_layout_state.current_margin,
|
||||||
|
inline_start,
|
||||||
|
inline_end: inline_start + inline_size,
|
||||||
|
};
|
||||||
|
parent_containing_block_position_info =
|
||||||
|
Some(sequential_layout_state.update_all_containing_block_offsets(new_cb_offsets));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
|
let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
|
||||||
|
|
||||||
let fragments;
|
let fragments;
|
||||||
let mut content_block_size;
|
let mut content_block_size;
|
||||||
match block_level_kind {
|
match block_level_kind {
|
||||||
NonReplacedContents::SameFormattingContextBlock(contents) => {
|
NonReplacedContents::SameFormattingContextBlock(contents) => {
|
||||||
let start_margin_can_collapse_with_children = pbm.padding.block_start == Length::zero() &&
|
|
||||||
pbm.border.block_start == Length::zero();
|
|
||||||
let end_margin_can_collapse_with_children = pbm.padding.block_end == Length::zero() &&
|
|
||||||
pbm.border.block_end == Length::zero() &&
|
|
||||||
block_size == LengthOrAuto::Auto &&
|
|
||||||
min_box_size.block == Length::zero();
|
|
||||||
|
|
||||||
let flow_layout = contents.layout(
|
let flow_layout = contents.layout(
|
||||||
layout_context,
|
layout_context,
|
||||||
positioning_context,
|
positioning_context,
|
||||||
&containing_block_for_children,
|
&containing_block_for_children,
|
||||||
tree_rank,
|
tree_rank,
|
||||||
float_context,
|
sequential_layout_state.as_mut().map(|x| &mut **x),
|
||||||
CollapsibleWithParentStartMargin(start_margin_can_collapse_with_children),
|
CollapsibleWithParentStartMargin(start_margin_can_collapse_with_children),
|
||||||
);
|
);
|
||||||
|
|
||||||
fragments = flow_layout.fragments;
|
fragments = flow_layout.fragments;
|
||||||
content_block_size = flow_layout.content_block_size;
|
content_block_size = flow_layout.content_block_size;
|
||||||
let mut collapsible_margins_in_children = flow_layout.collapsible_margins_in_children;
|
|
||||||
|
|
||||||
|
// Update margins.
|
||||||
|
let mut collapsible_margins_in_children = flow_layout.collapsible_margins_in_children;
|
||||||
if start_margin_can_collapse_with_children {
|
if start_margin_can_collapse_with_children {
|
||||||
block_margins_collapsed_with_children
|
block_margins_collapsed_with_children
|
||||||
.start
|
.start
|
||||||
|
@ -546,12 +584,33 @@ fn layout_in_flow_non_replaced_block_level(
|
||||||
content_block_size = independent_layout.content_block_size;
|
content_block_size = independent_layout.content_block_size;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let block_size = block_size.auto_is(|| {
|
let block_size = block_size.auto_is(|| {
|
||||||
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)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if let Some(ref mut sequential_layout_state) = sequential_layout_state {
|
||||||
|
// Now that we're done laying out our children, we can restore the
|
||||||
|
// parent's containing block position information.
|
||||||
|
sequential_layout_state
|
||||||
|
.update_all_containing_block_offsets(parent_containing_block_position_info.unwrap());
|
||||||
|
|
||||||
|
// Account for padding and border. We also might have to readjust the
|
||||||
|
// `bfc_relative_block_position` if it was different from the content size (i.e. was
|
||||||
|
// non-`auto` and/or was affected by min/max block size).
|
||||||
|
sequential_layout_state.advance_block_position(
|
||||||
|
(block_size - content_block_size) + pbm.padding.block_end + pbm.border.block_end,
|
||||||
|
);
|
||||||
|
|
||||||
|
if !end_margin_can_collapse_with_children {
|
||||||
|
sequential_layout_state.collapse_margins();
|
||||||
|
}
|
||||||
|
sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
|
||||||
|
}
|
||||||
|
|
||||||
let content_rect = Rect {
|
let content_rect = Rect {
|
||||||
start_corner: Vec2 {
|
start_corner: Vec2 {
|
||||||
block: pbm.padding.block_start + pbm.border.block_start,
|
block: pbm.padding.block_start + pbm.border.block_start + clearance,
|
||||||
inline: pbm.padding.inline_start + pbm.border.inline_start + margin.inline_start,
|
inline: pbm.padding.inline_start + pbm.border.inline_start + margin.inline_start,
|
||||||
},
|
},
|
||||||
size: Vec2 {
|
size: Vec2 {
|
||||||
|
@ -559,6 +618,7 @@ fn layout_in_flow_non_replaced_block_level(
|
||||||
inline: inline_size,
|
inline: inline_size,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
BoxFragment::new(
|
BoxFragment::new(
|
||||||
base_fragment_info,
|
base_fragment_info,
|
||||||
style.clone(),
|
style.clone(),
|
||||||
|
@ -567,6 +627,7 @@ fn layout_in_flow_non_replaced_block_level(
|
||||||
pbm.padding,
|
pbm.padding,
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
|
clearance,
|
||||||
block_margins_collapsed_with_children,
|
block_margins_collapsed_with_children,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -579,6 +640,7 @@ fn layout_in_flow_replaced_block_level<'a>(
|
||||||
base_fragment_info: BaseFragmentInfo,
|
base_fragment_info: BaseFragmentInfo,
|
||||||
style: &Arc<ComputedValues>,
|
style: &Arc<ComputedValues>,
|
||||||
replaced: &ReplacedContent,
|
replaced: &ReplacedContent,
|
||||||
|
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||||
) -> BoxFragment {
|
) -> BoxFragment {
|
||||||
let pbm = style.padding_border_margin(containing_block);
|
let pbm = style.padding_border_margin(containing_block);
|
||||||
let size = replaced.used_size_as_if_inline_element(containing_block, style, None, &pbm);
|
let size = replaced.used_size_as_if_inline_element(containing_block, style, None, &pbm);
|
||||||
|
@ -592,14 +654,24 @@ fn layout_in_flow_replaced_block_level<'a>(
|
||||||
block_end: pbm.margin.block_end.auto_is(Length::zero),
|
block_end: pbm.margin.block_end.auto_is(Length::zero),
|
||||||
};
|
};
|
||||||
let fragments = replaced.make_fragments(style, size.clone());
|
let fragments = replaced.make_fragments(style, size.clone());
|
||||||
|
|
||||||
|
let mut clearance = Length::zero();
|
||||||
|
if let Some(ref mut sequential_layout_state) = sequential_layout_state {
|
||||||
|
sequential_layout_state.collapse_margins();
|
||||||
|
clearance = sequential_layout_state.calculate_clearance(ClearSide::from_style(style));
|
||||||
|
sequential_layout_state
|
||||||
|
.advance_block_position(pbm.border.block_sum() + pbm.padding.block_sum() + size.block);
|
||||||
|
};
|
||||||
|
|
||||||
let content_rect = Rect {
|
let content_rect = Rect {
|
||||||
start_corner: Vec2 {
|
start_corner: Vec2 {
|
||||||
block: pbm.padding.block_start + pbm.border.block_start,
|
block: pbm.padding.block_start + pbm.border.block_start + clearance,
|
||||||
inline: pbm.padding.inline_start + pbm.border.inline_start + margin.inline_start,
|
inline: pbm.padding.inline_start + pbm.border.inline_start + margin.inline_start,
|
||||||
},
|
},
|
||||||
size,
|
size,
|
||||||
};
|
};
|
||||||
let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
|
let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
|
||||||
|
|
||||||
BoxFragment::new(
|
BoxFragment::new(
|
||||||
base_fragment_info,
|
base_fragment_info,
|
||||||
style.clone(),
|
style.clone(),
|
||||||
|
@ -608,6 +680,7 @@ fn layout_in_flow_replaced_block_level<'a>(
|
||||||
pbm.padding,
|
pbm.padding,
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
|
Length::zero(),
|
||||||
block_margins_collapsed_with_children,
|
block_margins_collapsed_with_children,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -624,3 +697,130 @@ fn solve_inline_margins_for_in_flow_block_level(
|
||||||
(LengthOrAuto::LengthPercentage(start), _) => (start, available - start),
|
(LengthOrAuto::LengthPercentage(start), _) => (start, available - start),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// State that we maintain when placing blocks.
|
||||||
|
//
|
||||||
|
// In parallel mode, this placement is done after all child blocks are laid out. In sequential
|
||||||
|
// mode, this is done right after each block is laid out.
|
||||||
|
pub(crate) struct PlacementState {
|
||||||
|
next_in_flow_margin_collapses_with_parent_start_margin: bool,
|
||||||
|
start_margin: CollapsedMargin,
|
||||||
|
current_margin: CollapsedMargin,
|
||||||
|
current_block_direction_position: Length,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlacementState {
|
||||||
|
fn new(
|
||||||
|
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
|
||||||
|
) -> PlacementState {
|
||||||
|
PlacementState {
|
||||||
|
next_in_flow_margin_collapses_with_parent_start_margin:
|
||||||
|
collapsible_with_parent_start_margin.0,
|
||||||
|
start_margin: CollapsedMargin::zero(),
|
||||||
|
current_margin: CollapsedMargin::zero(),
|
||||||
|
current_block_direction_position: Length::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_fragment(&mut self, fragment: &mut Fragment) {
|
||||||
|
match fragment {
|
||||||
|
Fragment::Box(fragment) => {
|
||||||
|
let fragment_block_margins = &fragment.block_margins_collapsed_with_children;
|
||||||
|
let fragment_block_size = fragment.clearance +
|
||||||
|
fragment.padding.block_sum() +
|
||||||
|
fragment.border.block_sum() +
|
||||||
|
fragment.content_rect.size.block;
|
||||||
|
|
||||||
|
if self.next_in_flow_margin_collapses_with_parent_start_margin {
|
||||||
|
debug_assert_eq!(self.current_margin.solve(), Length::zero());
|
||||||
|
self.start_margin
|
||||||
|
.adjoin_assign(&fragment_block_margins.start);
|
||||||
|
if fragment_block_margins.collapsed_through {
|
||||||
|
self.start_margin.adjoin_assign(&fragment_block_margins.end);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.next_in_flow_margin_collapses_with_parent_start_margin = false;
|
||||||
|
} else {
|
||||||
|
self.current_margin
|
||||||
|
.adjoin_assign(&fragment_block_margins.start);
|
||||||
|
}
|
||||||
|
fragment.content_rect.start_corner.block +=
|
||||||
|
self.current_margin.solve() + self.current_block_direction_position;
|
||||||
|
if fragment_block_margins.collapsed_through {
|
||||||
|
self.current_margin
|
||||||
|
.adjoin_assign(&fragment_block_margins.end);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.current_block_direction_position +=
|
||||||
|
self.current_margin.solve() + fragment_block_size;
|
||||||
|
self.current_margin = fragment_block_margins.end;
|
||||||
|
},
|
||||||
|
Fragment::AbsoluteOrFixedPositioned(fragment) => {
|
||||||
|
let offset = Vec2 {
|
||||||
|
block: self.current_margin.solve() + self.current_block_direction_position,
|
||||||
|
inline: Length::new(0.),
|
||||||
|
};
|
||||||
|
fragment.borrow_mut().adjust_offsets(offset);
|
||||||
|
},
|
||||||
|
Fragment::Anonymous(_) | Fragment::Float(_) => {},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collapsible_margins_in_children(&self) -> CollapsedBlockMargins {
|
||||||
|
CollapsedBlockMargins {
|
||||||
|
collapsed_through: self.next_in_flow_margin_collapses_with_parent_start_margin,
|
||||||
|
start: self.start_margin,
|
||||||
|
end: self.current_margin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When Float fragments are created in block flows, they are positioned
|
||||||
|
/// relative to the float containing independent block formatting context.
|
||||||
|
/// Once we place a float's containing block, this function can be used to
|
||||||
|
/// fix up the float position to be relative to the containing block.
|
||||||
|
fn adjust_positions_of_float_children(
|
||||||
|
&self,
|
||||||
|
fragment: &mut Fragment,
|
||||||
|
sequential_layout_state: &mut SequentialLayoutState,
|
||||||
|
) {
|
||||||
|
let fragment = match fragment {
|
||||||
|
Fragment::Box(ref mut fragment) => fragment,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO(mrobinson): Will these margins be accurate if this fragment
|
||||||
|
// collapses through. Can a fragment collapse through when it has a
|
||||||
|
// non-zero sized float inside? The float won't be positioned correctly
|
||||||
|
// anyway (see the comment in `floats.rs` about margin collapse), but
|
||||||
|
// this might make the result even worse.
|
||||||
|
let collapsed_margins = self.collapsible_margins_in_children().start.adjoin(
|
||||||
|
&sequential_layout_state
|
||||||
|
.floats
|
||||||
|
.containing_block_info
|
||||||
|
.block_start_margins_not_collapsed,
|
||||||
|
);
|
||||||
|
|
||||||
|
let parent_fragment_offset_in_cb = &fragment.content_rect.start_corner;
|
||||||
|
let parent_fragment_offset_in_formatting_context = Vec2 {
|
||||||
|
inline: sequential_layout_state
|
||||||
|
.floats
|
||||||
|
.containing_block_info
|
||||||
|
.inline_start +
|
||||||
|
parent_fragment_offset_in_cb.inline,
|
||||||
|
block: sequential_layout_state
|
||||||
|
.floats
|
||||||
|
.containing_block_info
|
||||||
|
.block_start +
|
||||||
|
collapsed_margins.solve() +
|
||||||
|
parent_fragment_offset_in_cb.block,
|
||||||
|
};
|
||||||
|
|
||||||
|
for child_fragment in fragment.children.iter_mut() {
|
||||||
|
if let Fragment::Float(box_fragment) = child_fragment.borrow_mut().deref_mut() {
|
||||||
|
box_fragment.content_rect.start_corner = &box_fragment.content_rect.start_corner -
|
||||||
|
&parent_fragment_offset_in_formatting_context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -440,7 +440,7 @@ impl FragmentTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
let fragment_relative_rect = match fragment {
|
let fragment_relative_rect = match fragment {
|
||||||
Fragment::Box(fragment) => fragment
|
Fragment::Box(fragment) | Fragment::Float(fragment) => fragment
|
||||||
.border_rect()
|
.border_rect()
|
||||||
.to_physical(fragment.style.writing_mode, &containing_block),
|
.to_physical(fragment.style.writing_mode, &containing_block),
|
||||||
Fragment::Text(fragment) => fragment
|
Fragment::Text(fragment) => fragment
|
||||||
|
@ -519,7 +519,7 @@ impl FragmentTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
scroll_area = match fragment {
|
scroll_area = match fragment {
|
||||||
Fragment::Box(fragment) => fragment
|
Fragment::Box(fragment) | Fragment::Float(fragment) => fragment
|
||||||
.scrollable_overflow(&containing_block)
|
.scrollable_overflow(&containing_block)
|
||||||
.translate(containing_block.origin.to_vector()),
|
.translate(containing_block.origin.to_vector()),
|
||||||
Fragment::Text(_) |
|
Fragment::Text(_) |
|
||||||
|
|
|
@ -26,6 +26,12 @@ use webrender_api::{FontInstanceKey, ImageKey};
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub(crate) enum Fragment {
|
pub(crate) enum Fragment {
|
||||||
Box(BoxFragment),
|
Box(BoxFragment),
|
||||||
|
/// Floating content. A floated fragment is very similar to a normal
|
||||||
|
/// [BoxFragment] but it isn't positioned using normal in block flow
|
||||||
|
/// positioning rules (margin collapse, etc). Instead, they are laid out by
|
||||||
|
/// the [SequentialLayoutState] of their float containing block formatting
|
||||||
|
/// context.
|
||||||
|
Float(BoxFragment),
|
||||||
Anonymous(AnonymousFragment),
|
Anonymous(AnonymousFragment),
|
||||||
/// Absolute and fixed position fragments are hoisted up so that they
|
/// Absolute and fixed position fragments are hoisted up so that they
|
||||||
/// are children of the BoxFragment that establishes their containing
|
/// are children of the BoxFragment that establishes their containing
|
||||||
|
@ -57,6 +63,8 @@ pub(crate) struct BoxFragment {
|
||||||
pub border: Sides<Length>,
|
pub border: Sides<Length>,
|
||||||
pub margin: Sides<Length>,
|
pub margin: Sides<Length>,
|
||||||
|
|
||||||
|
pub clearance: Length,
|
||||||
|
|
||||||
pub block_margins_collapsed_with_children: CollapsedBlockMargins,
|
pub block_margins_collapsed_with_children: CollapsedBlockMargins,
|
||||||
|
|
||||||
/// The scrollable overflow of this box fragment.
|
/// The scrollable overflow of this box fragment.
|
||||||
|
@ -66,6 +74,11 @@ pub(crate) struct BoxFragment {
|
||||||
overconstrained: PhysicalSize<bool>,
|
overconstrained: PhysicalSize<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct FloatFragment {
|
||||||
|
pub box_fragment: BoxFragment,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub(crate) struct CollapsedBlockMargins {
|
pub(crate) struct CollapsedBlockMargins {
|
||||||
pub collapsed_through: bool,
|
pub collapsed_through: bool,
|
||||||
|
@ -73,7 +86,7 @@ pub(crate) struct CollapsedBlockMargins {
|
||||||
pub end: CollapsedMargin,
|
pub end: CollapsedMargin,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Serialize)]
|
#[derive(Clone, Copy, Debug, Serialize)]
|
||||||
pub(crate) struct CollapsedMargin {
|
pub(crate) struct CollapsedMargin {
|
||||||
max_positive: Length,
|
max_positive: Length,
|
||||||
min_negative: Length,
|
min_negative: Length,
|
||||||
|
@ -152,7 +165,7 @@ impl Fragment {
|
||||||
pub fn offset_inline(&mut self, offset: &Length) {
|
pub fn offset_inline(&mut self, offset: &Length) {
|
||||||
let position = match self {
|
let position = match self {
|
||||||
Fragment::Box(f) => &mut f.content_rect.start_corner,
|
Fragment::Box(f) => &mut f.content_rect.start_corner,
|
||||||
Fragment::AbsoluteOrFixedPositioned(_) => return,
|
Fragment::Float(_) | Fragment::AbsoluteOrFixedPositioned(_) => return,
|
||||||
Fragment::Anonymous(f) => &mut f.rect.start_corner,
|
Fragment::Anonymous(f) => &mut f.rect.start_corner,
|
||||||
Fragment::Text(f) => &mut f.rect.start_corner,
|
Fragment::Text(f) => &mut f.rect.start_corner,
|
||||||
Fragment::Image(f) => &mut f.rect.start_corner,
|
Fragment::Image(f) => &mut f.rect.start_corner,
|
||||||
|
@ -170,6 +183,7 @@ impl Fragment {
|
||||||
Fragment::Anonymous(fragment) => &fragment.base,
|
Fragment::Anonymous(fragment) => &fragment.base,
|
||||||
Fragment::Image(fragment) => &fragment.base,
|
Fragment::Image(fragment) => &fragment.base,
|
||||||
Fragment::IFrame(fragment) => &fragment.base,
|
Fragment::IFrame(fragment) => &fragment.base,
|
||||||
|
Fragment::Float(fragment) => &fragment.base,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,6 +194,11 @@ impl Fragment {
|
||||||
pub fn print(&self, tree: &mut PrintTree) {
|
pub fn print(&self, tree: &mut PrintTree) {
|
||||||
match self {
|
match self {
|
||||||
Fragment::Box(fragment) => fragment.print(tree),
|
Fragment::Box(fragment) => fragment.print(tree),
|
||||||
|
Fragment::Float(fragment) => {
|
||||||
|
tree.new_level(format!("Float"));
|
||||||
|
fragment.print(tree);
|
||||||
|
tree.end_level();
|
||||||
|
},
|
||||||
Fragment::AbsoluteOrFixedPositioned(_) => {
|
Fragment::AbsoluteOrFixedPositioned(_) => {
|
||||||
tree.add_item("AbsoluteOrFixedPositioned".to_string());
|
tree.add_item("AbsoluteOrFixedPositioned".to_string());
|
||||||
},
|
},
|
||||||
|
@ -195,7 +214,9 @@ impl Fragment {
|
||||||
containing_block: &PhysicalRect<Length>,
|
containing_block: &PhysicalRect<Length>,
|
||||||
) -> PhysicalRect<Length> {
|
) -> PhysicalRect<Length> {
|
||||||
match self {
|
match self {
|
||||||
Fragment::Box(fragment) => fragment.scrollable_overflow_for_parent(&containing_block),
|
Fragment::Box(fragment) | Fragment::Float(fragment) => {
|
||||||
|
fragment.scrollable_overflow_for_parent(&containing_block)
|
||||||
|
},
|
||||||
Fragment::AbsoluteOrFixedPositioned(_) => PhysicalRect::zero(),
|
Fragment::AbsoluteOrFixedPositioned(_) => PhysicalRect::zero(),
|
||||||
Fragment::Anonymous(fragment) => fragment.scrollable_overflow.clone(),
|
Fragment::Anonymous(fragment) => fragment.scrollable_overflow.clone(),
|
||||||
Fragment::Text(fragment) => fragment
|
Fragment::Text(fragment) => fragment
|
||||||
|
@ -222,7 +243,7 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Fragment::Box(fragment) => {
|
Fragment::Box(fragment) | Fragment::Float(fragment) => {
|
||||||
let content_rect = fragment
|
let content_rect = fragment
|
||||||
.content_rect
|
.content_rect
|
||||||
.to_physical(fragment.style.writing_mode, containing_block)
|
.to_physical(fragment.style.writing_mode, containing_block)
|
||||||
|
@ -267,16 +288,6 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnonymousFragment {
|
impl AnonymousFragment {
|
||||||
pub fn no_op(mode: WritingMode) -> Self {
|
|
||||||
Self {
|
|
||||||
base: BaseFragment::anonymous(),
|
|
||||||
children: vec![],
|
|
||||||
rect: Rect::zero(),
|
|
||||||
mode,
|
|
||||||
scrollable_overflow: PhysicalRect::zero(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(rect: Rect<Length>, children: Vec<Fragment>, mode: WritingMode) -> Self {
|
pub fn new(rect: Rect<Length>, children: Vec<Fragment>, mode: WritingMode) -> Self {
|
||||||
// FIXME(mrobinson, bug 25564): We should be using the containing block
|
// FIXME(mrobinson, bug 25564): We should be using the containing block
|
||||||
// here to properly convert scrollable overflow to physical geometry.
|
// here to properly convert scrollable overflow to physical geometry.
|
||||||
|
@ -325,6 +336,7 @@ impl BoxFragment {
|
||||||
padding: Sides<Length>,
|
padding: Sides<Length>,
|
||||||
border: Sides<Length>,
|
border: Sides<Length>,
|
||||||
margin: Sides<Length>,
|
margin: Sides<Length>,
|
||||||
|
clearance: Length,
|
||||||
block_margins_collapsed_with_children: CollapsedBlockMargins,
|
block_margins_collapsed_with_children: CollapsedBlockMargins,
|
||||||
) -> BoxFragment {
|
) -> BoxFragment {
|
||||||
let position = style.get_box().position;
|
let position = style.get_box().position;
|
||||||
|
@ -344,6 +356,7 @@ impl BoxFragment {
|
||||||
padding,
|
padding,
|
||||||
border,
|
border,
|
||||||
margin,
|
margin,
|
||||||
|
clearance,
|
||||||
block_margins_collapsed_with_children,
|
block_margins_collapsed_with_children,
|
||||||
PhysicalSize::new(width_overconstrained, height_overconstrained),
|
PhysicalSize::new(width_overconstrained, height_overconstrained),
|
||||||
)
|
)
|
||||||
|
@ -357,6 +370,7 @@ impl BoxFragment {
|
||||||
padding: Sides<Length>,
|
padding: Sides<Length>,
|
||||||
border: Sides<Length>,
|
border: Sides<Length>,
|
||||||
margin: Sides<Length>,
|
margin: Sides<Length>,
|
||||||
|
clearance: Length,
|
||||||
block_margins_collapsed_with_children: CollapsedBlockMargins,
|
block_margins_collapsed_with_children: CollapsedBlockMargins,
|
||||||
overconstrained: PhysicalSize<bool>,
|
overconstrained: PhysicalSize<bool>,
|
||||||
) -> BoxFragment {
|
) -> BoxFragment {
|
||||||
|
@ -379,6 +393,7 @@ impl BoxFragment {
|
||||||
padding,
|
padding,
|
||||||
border,
|
border,
|
||||||
margin,
|
margin,
|
||||||
|
clearance,
|
||||||
block_margins_collapsed_with_children,
|
block_margins_collapsed_with_children,
|
||||||
scrollable_overflow_from_children,
|
scrollable_overflow_from_children,
|
||||||
overconstrained,
|
overconstrained,
|
||||||
|
@ -419,19 +434,17 @@ impl BoxFragment {
|
||||||
\ncontent={:?}\
|
\ncontent={:?}\
|
||||||
\npadding rect={:?}\
|
\npadding rect={:?}\
|
||||||
\nborder rect={:?}\
|
\nborder rect={:?}\
|
||||||
|
\nclearance={:?}\
|
||||||
\nscrollable_overflow={:?}\
|
\nscrollable_overflow={:?}\
|
||||||
\noverflow={:?} / {:?}\
|
\noverflow={:?} / {:?}",
|
||||||
\noverconstrained={:?}
|
|
||||||
\nstyle={:p}",
|
|
||||||
self.base,
|
self.base,
|
||||||
self.content_rect,
|
self.content_rect,
|
||||||
self.padding_rect(),
|
self.padding_rect(),
|
||||||
self.border_rect(),
|
self.border_rect(),
|
||||||
|
self.clearance,
|
||||||
self.scrollable_overflow(&PhysicalRect::zero()),
|
self.scrollable_overflow(&PhysicalRect::zero()),
|
||||||
self.style.get_box().overflow_x,
|
self.style.get_box().overflow_x,
|
||||||
self.style.get_box().overflow_y,
|
self.style.get_box().overflow_y,
|
||||||
self.overconstrained,
|
|
||||||
self.style,
|
|
||||||
));
|
));
|
||||||
|
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
|
|
|
@ -251,6 +251,16 @@ impl<T> flow_relative::Sides<T> {
|
||||||
self.block_start + self.block_end
|
self.block_start + self.block_end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sum(&self) -> flow_relative::Vec2<T::Output>
|
||||||
|
where
|
||||||
|
T: Add + Copy,
|
||||||
|
{
|
||||||
|
flow_relative::Vec2 {
|
||||||
|
inline: self.inline_sum(),
|
||||||
|
block: self.block_sum(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_physical(&self, mode: WritingMode) -> PhysicalSides<T>
|
pub fn to_physical(&self, mode: WritingMode) -> PhysicalSides<T>
|
||||||
where
|
where
|
||||||
T: Clone,
|
T: Clone,
|
||||||
|
@ -290,6 +300,18 @@ impl<T> flow_relative::Sides<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> flow_relative::Sides<T>
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
pub fn start_offset(&self) -> flow_relative::Vec2<T> {
|
||||||
|
flow_relative::Vec2 {
|
||||||
|
inline: self.inline_start,
|
||||||
|
block: self.block_start,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl flow_relative::Sides<&'_ LengthPercentage> {
|
impl flow_relative::Sides<&'_ LengthPercentage> {
|
||||||
pub fn percentages_relative_to(&self, basis: Length) -> flow_relative::Sides<Length> {
|
pub fn percentages_relative_to(&self, basis: Length) -> flow_relative::Sides<Length> {
|
||||||
self.map(|s| s.percentage_relative_to(basis))
|
self.map(|s| s.percentage_relative_to(basis))
|
||||||
|
|
|
@ -666,6 +666,7 @@ impl HoistedAbsolutelyPositionedBox {
|
||||||
pbm.padding,
|
pbm.padding,
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
|
Length::zero(),
|
||||||
CollapsedBlockMargins::zero(),
|
CollapsedBlockMargins::zero(),
|
||||||
physical_overconstrained,
|
physical_overconstrained,
|
||||||
)
|
)
|
||||||
|
@ -808,7 +809,7 @@ fn adjust_static_positions(
|
||||||
|
|
||||||
let child_fragment_rect = match &child_fragments[original_tree_rank] {
|
let child_fragment_rect = match &child_fragments[original_tree_rank] {
|
||||||
Fragment::Box(b) => &b.content_rect,
|
Fragment::Box(b) => &b.content_rect,
|
||||||
Fragment::AbsoluteOrFixedPositioned(_) => continue,
|
Fragment::AbsoluteOrFixedPositioned(_) | Fragment::Float(_) => continue,
|
||||||
Fragment::Anonymous(a) => &a.rect,
|
Fragment::Anonymous(a) => &a.rect,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -425,7 +425,7 @@ fn process_offset_parent_query_inner(
|
||||||
//
|
//
|
||||||
// [1]: https://github.com/w3c/csswg-drafts/issues/4541
|
// [1]: https://github.com/w3c/csswg-drafts/issues/4541
|
||||||
let fragment_relative_rect = match fragment {
|
let fragment_relative_rect = match fragment {
|
||||||
Fragment::Box(fragment) => fragment
|
Fragment::Box(fragment) | Fragment::Float(fragment) => fragment
|
||||||
.border_rect()
|
.border_rect()
|
||||||
.to_physical(fragment.style.writing_mode, &containing_block),
|
.to_physical(fragment.style.writing_mode, &containing_block),
|
||||||
Fragment::Text(fragment) => fragment
|
Fragment::Text(fragment) => fragment
|
||||||
|
@ -483,7 +483,7 @@ fn process_offset_parent_query_inner(
|
||||||
} else {
|
} else {
|
||||||
// Record the paths of the nodes being traversed.
|
// Record the paths of the nodes being traversed.
|
||||||
let parent_node_address = match fragment {
|
let parent_node_address = match fragment {
|
||||||
Fragment::Box(fragment) => {
|
Fragment::Box(fragment) | Fragment::Float(fragment) => {
|
||||||
let is_eligible_parent =
|
let is_eligible_parent =
|
||||||
match (is_body_element, fragment.style.get_box().position) {
|
match (is_body_element, fragment.style.get_box().position) {
|
||||||
// Spec says the element is eligible as `offsetParent` if any of
|
// Spec says the element is eligible as `offsetParent` if any of
|
||||||
|
@ -540,9 +540,8 @@ fn process_offset_parent_query_inner(
|
||||||
fragment_tree
|
fragment_tree
|
||||||
.find(|fragment, _, containing_block| {
|
.find(|fragment, _, containing_block| {
|
||||||
match fragment {
|
match fragment {
|
||||||
Fragment::Box(fragment)
|
Fragment::Box(fragment) | Fragment::Float(fragment) => {
|
||||||
if fragment.base.tag == Some(offset_parent_node_tag) =>
|
if fragment.base.tag == Some(offset_parent_node_tag) {
|
||||||
{
|
|
||||||
// Again, take the *first* associated CSS layout box.
|
// Again, take the *first* associated CSS layout box.
|
||||||
let padding_box_corner = fragment
|
let padding_box_corner = fragment
|
||||||
.padding_rect()
|
.padding_rect()
|
||||||
|
@ -555,9 +554,11 @@ fn process_offset_parent_query_inner(
|
||||||
Au::from_f32_px(padding_box_corner.y.px()),
|
Au::from_f32_px(padding_box_corner.y.px()),
|
||||||
);
|
);
|
||||||
Some(padding_box_corner)
|
Some(padding_box_corner)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Fragment::AbsoluteOrFixedPositioned(_) |
|
Fragment::AbsoluteOrFixedPositioned(_) |
|
||||||
Fragment::Box(_) |
|
|
||||||
Fragment::Text(_) |
|
Fragment::Text(_) |
|
||||||
Fragment::Image(_) |
|
Fragment::Image(_) |
|
||||||
Fragment::IFrame(_) |
|
Fragment::IFrame(_) |
|
||||||
|
|
|
@ -9,7 +9,7 @@ extern crate lazy_static;
|
||||||
|
|
||||||
use euclid::num::Zero;
|
use euclid::num::Zero;
|
||||||
use layout::flow::float::{ClearSide, FloatBand, FloatBandNode, FloatBandTree, FloatContext};
|
use layout::flow::float::{ClearSide, FloatBand, FloatBandNode, FloatBandTree, FloatContext};
|
||||||
use layout::flow::float::{FloatSide, PlacementInfo};
|
use layout::flow::float::{ContainingBlockPositionInfo, FloatSide, PlacementInfo};
|
||||||
use layout::geom::flow_relative::{Rect, Vec2};
|
use layout::geom::flow_relative::{Rect, Vec2};
|
||||||
use quickcheck::{Arbitrary, Gen};
|
use quickcheck::{Arbitrary, Gen};
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
@ -57,10 +57,7 @@ impl<'a> Drop for PanicMsgSuppressor<'a> {
|
||||||
struct FloatBandWrapper(FloatBand);
|
struct FloatBandWrapper(FloatBand);
|
||||||
|
|
||||||
impl Arbitrary for FloatBandWrapper {
|
impl Arbitrary for FloatBandWrapper {
|
||||||
fn arbitrary<G>(generator: &mut G) -> FloatBandWrapper
|
fn arbitrary(generator: &mut Gen) -> FloatBandWrapper {
|
||||||
where
|
|
||||||
G: Gen,
|
|
||||||
{
|
|
||||||
let top: u32 = Arbitrary::arbitrary(generator);
|
let top: u32 = Arbitrary::arbitrary(generator);
|
||||||
let left: Option<u32> = Arbitrary::arbitrary(generator);
|
let left: Option<u32> = Arbitrary::arbitrary(generator);
|
||||||
let right: Option<u32> = Arbitrary::arbitrary(generator);
|
let right: Option<u32> = Arbitrary::arbitrary(generator);
|
||||||
|
@ -75,23 +72,17 @@ impl Arbitrary for FloatBandWrapper {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct FloatRangeInput {
|
struct FloatRangeInput {
|
||||||
start_index: u32,
|
start_index: u32,
|
||||||
band_count: u32,
|
|
||||||
side: FloatSide,
|
side: FloatSide,
|
||||||
length: u32,
|
length: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arbitrary for FloatRangeInput {
|
impl Arbitrary for FloatRangeInput {
|
||||||
fn arbitrary<G>(generator: &mut G) -> FloatRangeInput
|
fn arbitrary(generator: &mut Gen) -> FloatRangeInput {
|
||||||
where
|
|
||||||
G: Gen,
|
|
||||||
{
|
|
||||||
let start_index: u32 = Arbitrary::arbitrary(generator);
|
let start_index: u32 = Arbitrary::arbitrary(generator);
|
||||||
let band_count: u32 = Arbitrary::arbitrary(generator);
|
|
||||||
let is_left: bool = Arbitrary::arbitrary(generator);
|
let is_left: bool = Arbitrary::arbitrary(generator);
|
||||||
let length: u32 = Arbitrary::arbitrary(generator);
|
let length: u32 = Arbitrary::arbitrary(generator);
|
||||||
FloatRangeInput {
|
FloatRangeInput {
|
||||||
start_index,
|
start_index,
|
||||||
band_count,
|
|
||||||
side: if is_left {
|
side: if is_left {
|
||||||
FloatSide::Left
|
FloatSide::Left
|
||||||
} else {
|
} else {
|
||||||
|
@ -341,20 +332,25 @@ struct FloatInput {
|
||||||
// The float may be placed no higher than this line. This simulates the effect of line boxes
|
// The float may be placed no higher than this line. This simulates the effect of line boxes
|
||||||
// per CSS 2.1 § 9.5.1 rule 6.
|
// per CSS 2.1 § 9.5.1 rule 6.
|
||||||
ceiling: u32,
|
ceiling: u32,
|
||||||
|
/// Containing block positioning information, which is used to track the current offsets
|
||||||
|
/// from the float containing block formatting context to the current containing block.
|
||||||
|
containing_block_info: ContainingBlockPositionInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arbitrary for FloatInput {
|
impl Arbitrary for FloatInput {
|
||||||
fn arbitrary<G>(generator: &mut G) -> FloatInput
|
fn arbitrary(generator: &mut Gen) -> FloatInput {
|
||||||
where
|
// See #29819: Limit the maximum size of all f32 values here because
|
||||||
G: Gen,
|
// massive float values will start to introduce very bad floating point
|
||||||
{
|
// errors.
|
||||||
let width: u32 = Arbitrary::arbitrary(generator);
|
// TODO: This should be be addressed in a better way. Perhaps we should
|
||||||
let height: u32 = Arbitrary::arbitrary(generator);
|
// reintroduce the use of app_units in Layout 2020.
|
||||||
let is_left: bool = Arbitrary::arbitrary(generator);
|
let width = u32::arbitrary(generator) % 12345;
|
||||||
let ceiling: u32 = Arbitrary::arbitrary(generator);
|
let height = u32::arbitrary(generator) % 12345;
|
||||||
let left_wall: u32 = Arbitrary::arbitrary(generator);
|
let is_left = bool::arbitrary(generator);
|
||||||
let right_wall: u32 = Arbitrary::arbitrary(generator);
|
let ceiling = u32::arbitrary(generator) % 12345;
|
||||||
let clear: u8 = Arbitrary::arbitrary(generator);
|
let left = u32::arbitrary(generator) % 12345;
|
||||||
|
let containing_block_width = u32::arbitrary(generator) % 12345;
|
||||||
|
let clear = u8::arbitrary(generator);
|
||||||
FloatInput {
|
FloatInput {
|
||||||
info: PlacementInfo {
|
info: PlacementInfo {
|
||||||
size: Vec2 {
|
size: Vec2 {
|
||||||
|
@ -367,10 +363,12 @@ impl Arbitrary for FloatInput {
|
||||||
FloatSide::Right
|
FloatSide::Right
|
||||||
},
|
},
|
||||||
clear: new_clear_side(clear),
|
clear: new_clear_side(clear),
|
||||||
left_wall: Length::new(left_wall as f32),
|
|
||||||
right_wall: Length::new(right_wall as f32),
|
|
||||||
},
|
},
|
||||||
ceiling,
|
ceiling,
|
||||||
|
containing_block_info: ContainingBlockPositionInfo::new_with_inline_offsets(
|
||||||
|
Length::new(left as f32),
|
||||||
|
Length::new(left as f32 + containing_block_width as f32),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,12 +387,12 @@ impl Arbitrary for FloatInput {
|
||||||
this.info.clear = new_clear_side(clear_side);
|
this.info.clear = new_clear_side(clear_side);
|
||||||
shrunk = true;
|
shrunk = true;
|
||||||
}
|
}
|
||||||
if let Some(left_wall) = self.info.left_wall.px().shrink().next() {
|
if let Some(left) = self.containing_block_info.inline_start.px().shrink().next() {
|
||||||
this.info.left_wall = Length::new(left_wall);
|
this.containing_block_info.inline_start = Length::new(left);
|
||||||
shrunk = true;
|
shrunk = true;
|
||||||
}
|
}
|
||||||
if let Some(right_wall) = self.info.right_wall.px().shrink().next() {
|
if let Some(right) = self.containing_block_info.inline_end.px().shrink().next() {
|
||||||
this.info.right_wall = Length::new(right_wall);
|
this.containing_block_info.inline_end = Length::new(right);
|
||||||
shrunk = true;
|
shrunk = true;
|
||||||
}
|
}
|
||||||
if let Some(ceiling) = self.ceiling.shrink().next() {
|
if let Some(ceiling) = self.ceiling.shrink().next() {
|
||||||
|
@ -430,6 +428,7 @@ struct PlacedFloat {
|
||||||
origin: Vec2<Length>,
|
origin: Vec2<Length>,
|
||||||
info: PlacementInfo,
|
info: PlacementInfo,
|
||||||
ceiling: Length,
|
ceiling: Length,
|
||||||
|
containing_block_info: ContainingBlockPositionInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for FloatPlacement {
|
impl Drop for FloatPlacement {
|
||||||
|
@ -442,8 +441,12 @@ impl Drop for FloatPlacement {
|
||||||
eprintln!("Failing float placement:");
|
eprintln!("Failing float placement:");
|
||||||
for placed_float in &self.placed_floats {
|
for placed_float in &self.placed_floats {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
" * {:?} @ {:?}, {:?}",
|
" * {:?} @ {:?}, T {:?} L {:?} R {:?}",
|
||||||
placed_float.info, placed_float.origin, placed_float.ceiling
|
placed_float.info,
|
||||||
|
placed_float.origin,
|
||||||
|
placed_float.ceiling,
|
||||||
|
placed_float.containing_block_info.inline_start,
|
||||||
|
placed_float.containing_block_info.inline_end,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
eprintln!("Bands:\n{:?}\n", self.float_context.bands);
|
eprintln!("Bands:\n{:?}\n", self.float_context.bands);
|
||||||
|
@ -466,10 +469,12 @@ impl FloatPlacement {
|
||||||
for float in floats {
|
for float in floats {
|
||||||
let ceiling = Length::new(float.ceiling as f32);
|
let ceiling = Length::new(float.ceiling as f32);
|
||||||
float_context.lower_ceiling(ceiling);
|
float_context.lower_ceiling(ceiling);
|
||||||
|
float_context.containing_block_info = float.containing_block_info;
|
||||||
placed_floats.push(PlacedFloat {
|
placed_floats.push(PlacedFloat {
|
||||||
origin: float_context.add_float(&float.info),
|
origin: float_context.add_float(&float.info),
|
||||||
info: float.info,
|
info: float.info,
|
||||||
ceiling,
|
ceiling,
|
||||||
|
containing_block_info: float.containing_block_info,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
FloatPlacement {
|
FloatPlacement {
|
||||||
|
@ -488,9 +493,14 @@ impl FloatPlacement {
|
||||||
fn check_floats_rule_1(placement: &FloatPlacement) {
|
fn check_floats_rule_1(placement: &FloatPlacement) {
|
||||||
for placed_float in &placement.placed_floats {
|
for placed_float in &placement.placed_floats {
|
||||||
match placed_float.info.side {
|
match placed_float.info.side {
|
||||||
FloatSide::Left => assert!(placed_float.origin.inline >= placed_float.info.left_wall),
|
FloatSide::Left => assert!(
|
||||||
|
placed_float.origin.inline >= placed_float.containing_block_info.inline_start
|
||||||
|
),
|
||||||
FloatSide::Right => {
|
FloatSide::Right => {
|
||||||
assert!(placed_float.rect().max_inline_position() <= placed_float.info.right_wall)
|
assert!(
|
||||||
|
placed_float.rect().max_inline_position() <=
|
||||||
|
placed_float.containing_block_info.inline_end
|
||||||
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -596,12 +606,14 @@ fn check_floats_rule_7(placement: &FloatPlacement) {
|
||||||
// Only consider floats that stick out.
|
// Only consider floats that stick out.
|
||||||
match placed_float.info.side {
|
match placed_float.info.side {
|
||||||
FloatSide::Left => {
|
FloatSide::Left => {
|
||||||
if placed_float.rect().max_inline_position() <= placed_float.info.right_wall {
|
if placed_float.rect().max_inline_position() <=
|
||||||
|
placed_float.containing_block_info.inline_end
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
FloatSide::Right => {
|
FloatSide::Right => {
|
||||||
if placed_float.origin.inline >= placed_float.info.left_wall {
|
if placed_float.origin.inline >= placed_float.containing_block_info.inline_start {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -661,7 +673,7 @@ fn check_floats_rule_9(floats_and_perturbations: Vec<(FloatInput, u32)>) {
|
||||||
|
|
||||||
let mut placement = placement.clone();
|
let mut placement = placement.clone();
|
||||||
{
|
{
|
||||||
let mut placed_float = &mut placement.placed_floats[float_index];
|
let placed_float = &mut placement.placed_floats[float_index];
|
||||||
let perturbation = Length::new(perturbation as f32);
|
let perturbation = Length::new(perturbation as f32);
|
||||||
match placed_float.info.side {
|
match placed_float.info.side {
|
||||||
FloatSide::Left => {
|
FloatSide::Left => {
|
||||||
|
@ -747,6 +759,7 @@ fn check_basic_float_rules(placement: &FloatPlacement) {
|
||||||
fn test_floats_rule_1() {
|
fn test_floats_rule_1() {
|
||||||
let f: fn(Vec<FloatInput>) = check;
|
let f: fn(Vec<FloatInput>) = check;
|
||||||
quickcheck::quickcheck(f);
|
quickcheck::quickcheck(f);
|
||||||
|
|
||||||
fn check(floats: Vec<FloatInput>) {
|
fn check(floats: Vec<FloatInput>) {
|
||||||
check_floats_rule_1(&FloatPlacement::place(floats));
|
check_floats_rule_1(&FloatPlacement::place(floats));
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,8 +66,8 @@ ${helpers.predefined_type(
|
||||||
"Float",
|
"Float",
|
||||||
"computed::Float::None",
|
"computed::Float::None",
|
||||||
engines="gecko servo-2013 servo-2020",
|
engines="gecko servo-2013 servo-2020",
|
||||||
servo_2020_pref="layout.2020.unimplemented",
|
|
||||||
initial_specified_value="specified::Float::None",
|
initial_specified_value="specified::Float::None",
|
||||||
|
servo_2020_pref="layout.floats.enabled",
|
||||||
spec="https://drafts.csswg.org/css-box/#propdef-float",
|
spec="https://drafts.csswg.org/css-box/#propdef-float",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
servo_restyle_damage="rebuild_and_reflow",
|
servo_restyle_damage="rebuild_and_reflow",
|
||||||
|
@ -78,7 +78,8 @@ ${helpers.predefined_type(
|
||||||
"clear",
|
"clear",
|
||||||
"Clear",
|
"Clear",
|
||||||
"computed::Clear::None",
|
"computed::Clear::None",
|
||||||
engines="gecko servo-2013",
|
engines="gecko servo-2013 servo-2020",
|
||||||
|
servo_2020_pref="layout.floats.enabled",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
gecko_ffi_name="mBreakType",
|
gecko_ffi_name="mBreakType",
|
||||||
spec="https://drafts.csswg.org/css-box/#propdef-clear",
|
spec="https://drafts.csswg.org/css-box/#propdef-clear",
|
||||||
|
|
|
@ -18,7 +18,7 @@ use crate::values::{specified, CSSFloat};
|
||||||
use crate::Zero;
|
use crate::Zero;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub};
|
use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||||
use style_traits::{CSSPixel, CssWriter, ToCss};
|
use style_traits::{CSSPixel, CssWriter, ToCss};
|
||||||
|
|
||||||
pub use super::image::Image;
|
pub use super::image::Image;
|
||||||
|
@ -399,6 +399,13 @@ impl Sub for CSSPixelLength {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SubAssign for CSSPixelLength {
|
||||||
|
#[inline]
|
||||||
|
fn sub_assign(&mut self, other: Self) {
|
||||||
|
self.0 -= other.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<CSSPixelLength> for Au {
|
impl From<CSSPixelLength> for Au {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(len: CSSPixelLength) -> Self {
|
fn from(len: CSSPixelLength) -> Self {
|
||||||
|
|
|
@ -100,6 +100,7 @@
|
||||||
"layout.animations.test.enabled": false,
|
"layout.animations.test.enabled": false,
|
||||||
"layout.columns.enabled": false,
|
"layout.columns.enabled": false,
|
||||||
"layout.flexbox.enabled": false,
|
"layout.flexbox.enabled": false,
|
||||||
|
"layout.floats.enabled": false,
|
||||||
"layout.threads": 3,
|
"layout.threads": 3,
|
||||||
"layout.viewport.enabled": false,
|
"layout.viewport.enabled": false,
|
||||||
"layout.writing-mode.enabled": false,
|
"layout.writing-mode.enabled": false,
|
||||||
|
|
1
tests/wpt/metadata-layout-2020/css/CSS2/__dir__.ini
Normal file
1
tests/wpt/metadata-layout-2020/css/CSS2/__dir__.ini
Normal file
|
@ -0,0 +1 @@
|
||||||
|
prefs: ["layout.floats.enabled:true"]
|
|
@ -1,2 +0,0 @@
|
||||||
[c414-flt-fit-006.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[c414-flt-wrap-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[c5525-fltwidth-001.xht]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[adjacent-floats-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-002.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-003.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-applies-to-000.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-applies-to-009.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-clearance-calculation-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-clearance-calculation-002.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-clearance-calculation-004.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-clearance-calculation-005.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-default-inheritance-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-float-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-float-002.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-float-004.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-float-005.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-float-006.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-float-007.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-float-008.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-float-009.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-initial-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,25 +1,4 @@
|
||||||
[clear-no-interpolation.html]
|
[clear-no-interpolation.html]
|
||||||
[CSS Transitions: property <clear> from [initial\] to [both\] at (-0.3) should be [initial\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <clear> from [initial\] to [both\] at (0) should be [initial\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <clear> from [initial\] to [both\] at (0.3) should be [initial\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <clear> from [initial\] to [both\] at (0.5) should be [both\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <clear> from [initial\] to [both\] at (0.6) should be [both\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <clear> from [initial\] to [both\] at (1) should be [both\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <clear> from [initial\] to [both\] at (1.5) should be [both\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <clear> from [initial\] to [both\] at (-0.3) should be [both\]]
|
[CSS Transitions with transition: all: property <clear> from [initial\] to [both\] at (-0.3) should be [both\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -29,39 +8,6 @@
|
||||||
[CSS Transitions with transition: all: property <clear> from [initial\] to [both\] at (0.3) should be [both\]]
|
[CSS Transitions with transition: all: property <clear> from [initial\] to [both\] at (0.3) should be [both\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <clear> from [initial\] to [both\] at (0.5) should be [both\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <clear> from [initial\] to [both\] at (0.6) should be [both\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <clear> from [initial\] to [both\] at (1) should be [both\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <clear> from [initial\] to [both\] at (1.5) should be [both\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <clear> from [initial\] to [both\] at (-0.3) should be [initial\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <clear> from [initial\] to [both\] at (0) should be [initial\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <clear> from [initial\] to [both\] at (0.3) should be [initial\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <clear> from [initial\] to [both\] at (0.5) should be [both\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <clear> from [initial\] to [both\] at (0.6) should be [both\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <clear> from [initial\] to [both\] at (1) should be [both\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <clear> from [initial\] to [both\] at (1.5) should be [both\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Web Animations: property <clear> from [initial\] to [both\] at (-0.3) should be [initial\]]
|
[Web Animations: property <clear> from [initial\] to [both\] at (-0.3) should be [initial\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-on-parent-and-child.html]
|
|
||||||
expected: FAIL
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[clear-with-top-margin-after-cleared-empty-block.html]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[clearance-006.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-applies-to-001a.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-applies-to-004a.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-applies-to-005.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-applies-to-006.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-applies-to-007.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-applies-to-008.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-applies-to-008a.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-applies-to-009.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-applies-to-012.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-non-replaced-height-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-non-replaced-width-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-non-replaced-width-003.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-non-replaced-width-004.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-non-replaced-width-005.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[float-replaced-height-001.xht]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[float-replaced-width-003.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-replaced-width-004.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-replaced-width-006.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[float-replaced-width-011.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-002.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-003.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-004.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-007.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-008.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-009.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-023.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-024.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-025.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-028.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-041.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-043.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-119.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-120.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-121.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-123.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +1,2 @@
|
||||||
[float-005.xht]
|
[floats-132.xht]
|
||||||
expected: FAIL
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-135.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +1,2 @@
|
||||||
[clear-004.xht]
|
[floats-139.xht]
|
||||||
expected: FAIL
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-141.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-144.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-146.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-147.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-154.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[floats-bfc-001.xht]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-clear-multicol-002.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-clear-multicol-003.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-clear-multicol-balancing-002.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[floats-clear-multicol-balancing-003.html]
|
|
||||||
expected: FAIL
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[margin-collapse-018.xht]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[margin-collapse-023.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[margin-collapse-027.xht]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[margin-collapse-165.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[margin-collapse-166.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[no-clearance-due-to-large-margin-after-left-right.html]
|
|
||||||
expected: FAIL
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[remove-block-before-self-collapsing-sibling-with-clearance.html]
|
||||||
|
expected: FAIL
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue