mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Place floats in layout 2020, but don't flow text around the floats yet.
This commit puts floats behind the `layout.floats.enabled` pref, because of the following issues and unimplemented features: * Inline formatting contexts don't take floats into account, so text doesn't flow around the floats yet. * Non-floated block formatting contexts don't take floats into account, so BFCs can overlap floats. * Block formatting contexts that contain floats don't expand vertically to contain all the floats. That is, floats can stick out the bottom of BFCs, contra spec.
This commit is contained in:
parent
053a0aa4fd
commit
cdec48328e
235 changed files with 1018 additions and 623 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: {
|
||||||
|
|
|
@ -200,6 +200,8 @@ impl Fragment {
|
||||||
Visibility::Hidden => (),
|
Visibility::Hidden => (),
|
||||||
Visibility::Collapse => (),
|
Visibility::Collapse => (),
|
||||||
},
|
},
|
||||||
|
Fragment::HoistedFloat(_) => {},
|
||||||
|
Fragment::Float => {},
|
||||||
Fragment::AbsoluteOrFixedPositioned(_) => {},
|
Fragment::AbsoluteOrFixedPositioned(_) => {},
|
||||||
Fragment::Anonymous(_) => {},
|
Fragment::Anonymous(_) => {},
|
||||||
Fragment::Image(i) => match i.style.get_inherited_box().visibility {
|
Fragment::Image(i) => match i.style.get_inherited_box().visibility {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::cell::ArcRefCell;
|
||||||
use crate::display_list::conversions::ToWebRender;
|
use crate::display_list::conversions::ToWebRender;
|
||||||
use crate::display_list::DisplayListBuilder;
|
use crate::display_list::DisplayListBuilder;
|
||||||
use crate::fragment_tree::ContainingBlockManager;
|
use crate::fragment_tree::ContainingBlockManager;
|
||||||
use crate::fragments::{AnonymousFragment, BoxFragment, Fragment};
|
use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, HoistedFloatFragment};
|
||||||
use crate::geom::PhysicalRect;
|
use crate::geom::PhysicalRect;
|
||||||
use crate::style_ext::ComputedValuesExt;
|
use crate::style_ext::ComputedValuesExt;
|
||||||
use crate::FragmentTree;
|
use crate::FragmentTree;
|
||||||
|
@ -554,6 +554,14 @@ impl Fragment {
|
||||||
stacking_context,
|
stacking_context,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
Fragment::HoistedFloat(fragment) => {
|
||||||
|
fragment.build_stacking_context_tree(
|
||||||
|
display_list,
|
||||||
|
containing_block_info,
|
||||||
|
stacking_context,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Fragment::Float => {},
|
||||||
Fragment::AbsoluteOrFixedPositioned(fragment) => {
|
Fragment::AbsoluteOrFixedPositioned(fragment) => {
|
||||||
let shared_fragment = fragment.borrow();
|
let shared_fragment = fragment.borrow();
|
||||||
let fragment_ref = match shared_fragment.fragment.as_ref() {
|
let fragment_ref = match shared_fragment.fragment.as_ref() {
|
||||||
|
@ -1095,3 +1103,20 @@ impl AnonymousFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HoistedFloatFragment {
|
||||||
|
fn build_stacking_context_tree(
|
||||||
|
&self,
|
||||||
|
display_list: &mut DisplayList,
|
||||||
|
containing_block_info: &ContainingBlockInfo,
|
||||||
|
stacking_context: &mut StackingContext,
|
||||||
|
) {
|
||||||
|
self.fragment.borrow().build_stacking_context_tree(
|
||||||
|
&self.fragment,
|
||||||
|
display_list,
|
||||||
|
containing_block_info,
|
||||||
|
stacking_context,
|
||||||
|
StackingContextBuildMode::SkipHoisted,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -800,6 +800,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,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,16 +6,25 @@
|
||||||
//!
|
//!
|
||||||
//! See CSS 2.1 § 9.5.1: https://www.w3.org/TR/CSS2/visuren.html#float-position
|
//! See CSS 2.1 § 9.5.1: https://www.w3.org/TR/CSS2/visuren.html#float-position
|
||||||
|
|
||||||
|
use crate::cell::ArcRefCell;
|
||||||
use crate::context::LayoutContext;
|
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};
|
||||||
|
use crate::fragments::{Fragment, HoistedFloatFragment};
|
||||||
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::f32;
|
||||||
|
use std::fmt::{Debug, Formatter, Result as FmtResult};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@ -40,6 +49,25 @@ 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,
|
||||||
|
/// Distances from the logical left side of the block formatting context to the logical sides
|
||||||
|
/// of the current containing block.
|
||||||
|
pub walls: InlineWalls,
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Distances from the logical left side of the block formatting context to the logical sides of
|
||||||
|
/// the current containing block.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct InlineWalls {
|
||||||
|
/// The distance from the logical left side of the block formatting context to the logical
|
||||||
|
/// left side of the current containing block.
|
||||||
|
pub left: 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: Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatContext {
|
impl FloatContext {
|
||||||
|
@ -60,6 +88,9 @@ impl FloatContext {
|
||||||
FloatContext {
|
FloatContext {
|
||||||
bands,
|
bands,
|
||||||
ceiling: Length::zero(),
|
ceiling: Length::zero(),
|
||||||
|
walls: InlineWalls::new(),
|
||||||
|
clear_left_position: Length::zero(),
|
||||||
|
clear_right_position: Length::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,9 +112,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.walls) {
|
||||||
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 +136,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.walls.left),
|
||||||
None => object.left_wall,
|
None => self.walls.left,
|
||||||
};
|
};
|
||||||
Vec2 {
|
Vec2 {
|
||||||
inline: left_object_edge,
|
inline: left_object_edge,
|
||||||
|
@ -105,8 +146,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.walls.right),
|
||||||
None => object.right_wall,
|
None => self.walls.right,
|
||||||
};
|
};
|
||||||
Vec2 {
|
Vec2 {
|
||||||
inline: right_object_edge - object.size.inline,
|
inline: right_object_edge - object.size.inline,
|
||||||
|
@ -129,6 +170,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;
|
||||||
|
@ -155,6 +210,15 @@ impl FloatContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl InlineWalls {
|
||||||
|
fn new() -> InlineWalls {
|
||||||
|
InlineWalls {
|
||||||
|
left: Length::zero(),
|
||||||
|
right: Length::new(f32::INFINITY),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Information needed to place an object so that it doesn't collide with existing floats.
|
/// Information needed to place an object so that it doesn't collide with existing floats.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PlacementInfo {
|
pub struct PlacementInfo {
|
||||||
|
@ -164,12 +228,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 +268,41 @@ pub struct FloatBand {
|
||||||
pub right: Option<Length>,
|
pub right: Option<Length>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatBand {
|
impl FloatSide {
|
||||||
// Returns true if this band is clear of floats on the given side or sides.
|
fn from_style(style: &ComputedValues) -> Option<FloatSide> {
|
||||||
fn is_clear(&self, side: ClearSide) -> bool {
|
match style.get_box().float {
|
||||||
match (side, self.left, self.right) {
|
FloatProperty::None => None,
|
||||||
(ClearSide::Left, Some(_), _) |
|
FloatProperty::Left => Some(FloatSide::Left),
|
||||||
(ClearSide::Right, _, Some(_)) |
|
FloatProperty::Right => Some(FloatSide::Right),
|
||||||
(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.
|
impl ClearSide {
|
||||||
fn object_fits(&self, object: &PlacementInfo) -> bool {
|
pub(crate) fn from_style(style: &ComputedValues) -> ClearSide {
|
||||||
// If we must be clear on the given side and we aren't, this object doesn't fit.
|
match style.get_box().clear {
|
||||||
if !self.is_clear(object.clear) {
|
ClearProperty::None => ClearSide::None,
|
||||||
return false;
|
ClearProperty::Left => ClearSide::Left,
|
||||||
|
ClearProperty::Right => ClearSide::Right,
|
||||||
|
ClearProperty::Both => ClearSide::Both,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatBand {
|
||||||
|
// Determines whether an object fits in a band.
|
||||||
|
fn object_fits(&self, object: &PlacementInfo, walls: &InlineWalls) -> bool {
|
||||||
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.left,
|
||||||
Some(left) => left.max(object.left_wall),
|
Some(left) => left.max(walls.left),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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.right {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,13 +317,13 @@ 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.right,
|
||||||
Some(right) => right.min(object.right_wall),
|
Some(right) => right.min(walls.right),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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.left {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,6 +600,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 +627,292 @@ impl FloatBox {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn layout(
|
||||||
|
&mut self,
|
||||||
|
layout_context: &LayoutContext,
|
||||||
|
positioning_context: &mut PositioningContext,
|
||||||
|
containing_block: &ContainingBlock,
|
||||||
|
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||||
|
) {
|
||||||
|
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.bfc_relative_block_position +
|
||||||
|
sequential_layout_state.current_margin.solve(),
|
||||||
|
);
|
||||||
|
|
||||||
|
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(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
sequential_layout_state.push_float_fragment(ArcRefCell::new(Fragment::Box(box_fragment)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float fragment storage
|
||||||
|
|
||||||
|
// A persistent linked list that stores float fragments that need to be hoisted to their nearest
|
||||||
|
// ancestor containing block.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct FloatFragmentList {
|
||||||
|
root: FloatFragmentLink,
|
||||||
|
}
|
||||||
|
|
||||||
|
// A single link in the float fragment list.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct FloatFragmentLink(Option<Arc<FloatFragmentNode>>);
|
||||||
|
|
||||||
|
// A single node in the float fragment list.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct FloatFragmentNode {
|
||||||
|
// The fragment.
|
||||||
|
fragment: ArcRefCell<Fragment>,
|
||||||
|
// The next fragment (previous in document order).
|
||||||
|
next: FloatFragmentLink,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatFragmentList {
|
||||||
|
fn new() -> FloatFragmentList {
|
||||||
|
FloatFragmentList {
|
||||||
|
root: FloatFragmentLink(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sequential layout state
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
// A list of all float fragments in this block formatting context. These are gathered up and
|
||||||
|
// hoisted to the top of the BFC.
|
||||||
|
bfc_float_fragments: FloatFragmentList,
|
||||||
|
// 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).
|
||||||
|
bfc_relative_block_position: Length,
|
||||||
|
// Any collapsible margins that we've encountered after `bfc_relative_block_position`.
|
||||||
|
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(),
|
||||||
|
bfc_float_fragments: FloatFragmentList::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.bfc_relative_block_position + self.current_margin.solve();
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds the float fragment to this list.
|
||||||
|
pub(crate) fn push_float_fragment(&mut self, new_float_fragment: ArcRefCell<Fragment>) {
|
||||||
|
self.bfc_float_fragments.root.0 = Some(Arc::new(FloatFragmentNode {
|
||||||
|
fragment: new_float_fragment,
|
||||||
|
next: FloatFragmentLink(self.bfc_float_fragments.root.0.take()),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds the float fragments we've been building up to the given vector.
|
||||||
|
pub(crate) fn add_float_fragments_to_list(&self, fragment_list: &mut Vec<Fragment>) {
|
||||||
|
let start_index = fragment_list.len();
|
||||||
|
let mut link = &self.bfc_float_fragments.root;
|
||||||
|
while let Some(ref node) = link.0 {
|
||||||
|
fragment_list.push(Fragment::HoistedFloat(HoistedFloatFragment {
|
||||||
|
fragment: node.fragment.clone(),
|
||||||
|
}));
|
||||||
|
link = &node.next;
|
||||||
|
}
|
||||||
|
fragment_list[start_index..].reverse();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,16 @@ 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
|
box_.layout(
|
||||||
|
layout_context,
|
||||||
|
ifc.positioning_context,
|
||||||
|
containing_block,
|
||||||
|
ifc.sequential_layout_state.as_mut().map(|c| &mut **c),
|
||||||
|
);
|
||||||
|
ifc.current_nesting_level
|
||||||
|
.fragments_so_far
|
||||||
|
.push(Fragment::Float);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
@ -360,6 +377,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 +395,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 +449,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 +542,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 +607,7 @@ fn layout_atomic(
|
||||||
pbm.padding,
|
pbm.padding,
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
|
Length::zero(),
|
||||||
CollapsedBlockMargins::zero(),
|
CollapsedBlockMargins::zero(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -658,6 +683,7 @@ fn layout_atomic(
|
||||||
pbm.padding,
|
pbm.padding,
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
|
Length::zero(),
|
||||||
CollapsedBlockMargins::zero(),
|
CollapsedBlockMargins::zero(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -861,8 +887,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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,15 +6,13 @@
|
||||||
|
|
||||||
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, 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 +76,31 @@ 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 mut 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
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// FIXME(pcwalton): Relative positioning of ancestors should affect descendant floats.
|
||||||
|
if let Some(ref sequential_layout_state) = sequential_layout_state {
|
||||||
|
sequential_layout_state.add_float_fragments_to_list(&mut flow_layout.fragments);
|
||||||
|
}
|
||||||
|
|
||||||
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 +116,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 +126,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 +134,7 @@ impl BlockContainer {
|
||||||
positioning_context,
|
positioning_context,
|
||||||
containing_block,
|
containing_block,
|
||||||
tree_rank,
|
tree_rank,
|
||||||
|
sequential_layout_state,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,130 +173,110 @@ 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) => {
|
layout_context,
|
||||||
let fragment_block_margins = &fragment.block_margins_collapsed_with_children;
|
positioning_context,
|
||||||
let fragment_block_size = fragment.padding.block_sum() +
|
child_boxes,
|
||||||
fragment.border.block_sum() +
|
containing_block,
|
||||||
fragment.content_rect.size.block;
|
tree_rank,
|
||||||
|
sequential_layout_state,
|
||||||
if placement_state.next_in_flow_margin_collapses_with_parent_start_margin {
|
collapsible_with_parent_start_margin,
|
||||||
assert_eq!(placement_state.current_margin.solve(), Length::zero());
|
),
|
||||||
placement_state
|
None => layout_block_level_children_in_parallel(
|
||||||
.start_margin
|
layout_context,
|
||||||
.adjoin_assign(&fragment_block_margins.start);
|
positioning_context,
|
||||||
if fragment_block_margins.collapsed_through {
|
child_boxes,
|
||||||
placement_state
|
containing_block,
|
||||||
.start_margin
|
tree_rank,
|
||||||
.adjoin_assign(&fragment_block_margins.end);
|
collapsible_with_parent_start_margin,
|
||||||
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 {
|
fn layout_block_level_children_in_parallel(
|
||||||
next_in_flow_margin_collapses_with_parent_start_margin: bool,
|
layout_context: &LayoutContext,
|
||||||
start_margin: CollapsedMargin,
|
positioning_context: &mut PositioningContext,
|
||||||
current_margin: CollapsedMargin,
|
child_boxes: &[ArcRefCell<BlockLevelBox>],
|
||||||
current_block_direction_position: Length,
|
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 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| {
|
let fragments = positioning_context.adjust_static_positions(tree_rank, |positioning_context| {
|
||||||
if float_context.is_some() || !layout_context.use_rayon {
|
let collects_for_nearest_positioned_ancestor =
|
||||||
// Because floats are involved, we do layout for this block formatting context
|
positioning_context.collects_for_nearest_positioned_ancestor();
|
||||||
// in tree order without parallelism. This enables mutable access
|
let mut fragments: Vec<Fragment> = child_boxes
|
||||||
// to a `FloatContext` that tracks every float encountered so far (again in tree order).
|
.par_iter()
|
||||||
child_boxes
|
.enumerate()
|
||||||
.iter()
|
.mapfold_reduce_into(
|
||||||
.enumerate()
|
positioning_context,
|
||||||
.map(|(tree_rank, box_)| {
|
|positioning_context, (tree_rank, box_)| {
|
||||||
let mut fragment = box_.borrow_mut().layout(
|
box_.borrow_mut().layout(
|
||||||
layout_context,
|
layout_context,
|
||||||
positioning_context,
|
positioning_context,
|
||||||
containing_block,
|
containing_block,
|
||||||
tree_rank,
|
tree_rank,
|
||||||
float_context.as_mut().map(|c| &mut **c),
|
/* sequential_layout_state = */ None,
|
||||||
);
|
)
|
||||||
place_block_level_fragment(&mut fragment, &mut placement_state);
|
},
|
||||||
fragment
|
|| PositioningContext::new_for_rayon(collects_for_nearest_positioned_ancestor),
|
||||||
})
|
PositioningContext::append,
|
||||||
.collect()
|
)
|
||||||
} else {
|
.collect();
|
||||||
let collects_for_nearest_positioned_ancestor =
|
for fragment in fragments.iter_mut() {
|
||||||
positioning_context.collects_for_nearest_positioned_ancestor();
|
placement_state.place_fragment(fragment);
|
||||||
let mut fragments = child_boxes
|
|
||||||
.par_iter()
|
|
||||||
.enumerate()
|
|
||||||
.mapfold_reduce_into(
|
|
||||||
positioning_context,
|
|
||||||
|positioning_context, (tree_rank, box_)| {
|
|
||||||
box_.borrow_mut().layout(
|
|
||||||
layout_context,
|
|
||||||
positioning_context,
|
|
||||||
containing_block,
|
|
||||||
tree_rank,
|
|
||||||
/* float_context = */ None,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|| PositioningContext::new_for_rayon(collects_for_nearest_positioned_ancestor),
|
|
||||||
PositioningContext::append,
|
|
||||||
)
|
|
||||||
.collect();
|
|
||||||
for fragment in &mut fragments {
|
|
||||||
place_block_level_fragment(fragment, &mut placement_state)
|
|
||||||
}
|
|
||||||
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);
|
||||||
|
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,11 +364,14 @@ 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_) => {
|
||||||
// FIXME: call layout_maybe_position_relative_fragment here
|
box_.layout(
|
||||||
Fragment::Anonymous(AnonymousFragment::no_op(
|
layout_context,
|
||||||
containing_block.style.writing_mode,
|
positioning_context,
|
||||||
))
|
containing_block,
|
||||||
|
sequential_layout_state,
|
||||||
|
);
|
||||||
|
Fragment::Float
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -425,7 +413,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 +467,76 @@ 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 old_inline_walls;
|
||||||
|
match sequential_layout_state {
|
||||||
|
None => old_inline_walls = 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,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store our old inline walls so we can reset them later.
|
||||||
|
old_inline_walls = Some(sequential_layout_state.floats.walls);
|
||||||
|
sequential_layout_state.floats.walls.left +=
|
||||||
|
pbm.padding.inline_start + pbm.border.inline_start + margin.inline_start;
|
||||||
|
sequential_layout_state.floats.walls.right =
|
||||||
|
sequential_layout_state.floats.walls.left + inline_size;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
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 +573,31 @@ 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 old inline walls.
|
||||||
|
sequential_layout_state.floats.walls = old_inline_walls.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 +605,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 +614,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 +627,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 +641,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 +667,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 +684,81 @@ 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -446,6 +446,8 @@ impl FragmentTree {
|
||||||
Fragment::Text(fragment) => fragment
|
Fragment::Text(fragment) => fragment
|
||||||
.rect
|
.rect
|
||||||
.to_physical(fragment.parent_style.writing_mode, &containing_block),
|
.to_physical(fragment.parent_style.writing_mode, &containing_block),
|
||||||
|
Fragment::HoistedFloat(_) |
|
||||||
|
Fragment::Float |
|
||||||
Fragment::AbsoluteOrFixedPositioned(_) |
|
Fragment::AbsoluteOrFixedPositioned(_) |
|
||||||
Fragment::Image(_) |
|
Fragment::Image(_) |
|
||||||
Fragment::IFrame(_) |
|
Fragment::IFrame(_) |
|
||||||
|
@ -526,6 +528,8 @@ impl FragmentTree {
|
||||||
Fragment::AbsoluteOrFixedPositioned(_) |
|
Fragment::AbsoluteOrFixedPositioned(_) |
|
||||||
Fragment::Image(_) |
|
Fragment::Image(_) |
|
||||||
Fragment::IFrame(_) |
|
Fragment::IFrame(_) |
|
||||||
|
Fragment::Float |
|
||||||
|
Fragment::HoistedFloat(_) |
|
||||||
Fragment::Anonymous(_) => return None,
|
Fragment::Anonymous(_) => return None,
|
||||||
};
|
};
|
||||||
None::<()>
|
None::<()>
|
||||||
|
|
|
@ -26,6 +26,11 @@ use webrender_api::{FontInstanceKey, ImageKey};
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub(crate) enum Fragment {
|
pub(crate) enum Fragment {
|
||||||
Box(BoxFragment),
|
Box(BoxFragment),
|
||||||
|
// The original document position of a float in the document tree.
|
||||||
|
Float,
|
||||||
|
// A float hoisted up from its original position (where a placeholder `Fragment::Float` is) to
|
||||||
|
// its containing block.
|
||||||
|
HoistedFloat(HoistedFloatFragment),
|
||||||
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
|
||||||
|
@ -40,6 +45,13 @@ pub(crate) enum Fragment {
|
||||||
IFrame(IFrameFragment),
|
IFrame(IFrameFragment),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A float hoisted up from its original position (where a placeholder `Fragment::Float` is) to its
|
||||||
|
// containing block.
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct HoistedFloatFragment {
|
||||||
|
pub fragment: ArcRefCell<Fragment>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub(crate) struct BoxFragment {
|
pub(crate) struct BoxFragment {
|
||||||
pub base: BaseFragment,
|
pub base: BaseFragment,
|
||||||
|
@ -57,6 +69,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 +80,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 +92,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,6 +171,8 @@ 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::HoistedFloat(_) |
|
||||||
|
Fragment::Float |
|
||||||
Fragment::AbsoluteOrFixedPositioned(_) => return,
|
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,
|
||||||
|
@ -166,10 +187,12 @@ impl Fragment {
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Fragment::Box(fragment) => &fragment.base,
|
Fragment::Box(fragment) => &fragment.base,
|
||||||
Fragment::Text(fragment) => &fragment.base,
|
Fragment::Text(fragment) => &fragment.base,
|
||||||
|
Fragment::Float => return None,
|
||||||
Fragment::AbsoluteOrFixedPositioned(_) => return None,
|
Fragment::AbsoluteOrFixedPositioned(_) => return None,
|
||||||
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::HoistedFloat(_) => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,6 +203,8 @@ 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::HoistedFloat(fragment) => fragment.print(tree),
|
||||||
|
Fragment::Float => tree.add_item(format!("Float")),
|
||||||
Fragment::AbsoluteOrFixedPositioned(_) => {
|
Fragment::AbsoluteOrFixedPositioned(_) => {
|
||||||
tree.add_item("AbsoluteOrFixedPositioned".to_string());
|
tree.add_item("AbsoluteOrFixedPositioned".to_string());
|
||||||
},
|
},
|
||||||
|
@ -196,7 +221,10 @@ impl Fragment {
|
||||||
) -> PhysicalRect<Length> {
|
) -> PhysicalRect<Length> {
|
||||||
match self {
|
match self {
|
||||||
Fragment::Box(fragment) => fragment.scrollable_overflow_for_parent(&containing_block),
|
Fragment::Box(fragment) => fragment.scrollable_overflow_for_parent(&containing_block),
|
||||||
Fragment::AbsoluteOrFixedPositioned(_) => PhysicalRect::zero(),
|
Fragment::HoistedFloat(fragment) => {
|
||||||
|
(*fragment.fragment.borrow()).scrollable_overflow(&containing_block)
|
||||||
|
},
|
||||||
|
Fragment::Float | 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
|
||||||
.rect
|
.rect
|
||||||
|
@ -266,17 +294,16 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnonymousFragment {
|
impl HoistedFloatFragment {
|
||||||
pub fn no_op(mode: WritingMode) -> Self {
|
#[allow(dead_code)]
|
||||||
Self {
|
pub fn print(&self, tree: &mut PrintTree) {
|
||||||
base: BaseFragment::anonymous(),
|
tree.new_level(format!("HoistedFloatFragment"));
|
||||||
children: vec![],
|
self.fragment.borrow().print(tree);
|
||||||
rect: Rect::zero(),
|
tree.end_level();
|
||||||
mode,
|
|
||||||
scrollable_overflow: PhysicalRect::zero(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnonymousFragment {
|
||||||
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 +352,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 +372,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 +386,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 +409,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,
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -651,6 +651,7 @@ impl HoistedAbsolutelyPositionedBox {
|
||||||
pbm.padding,
|
pbm.padding,
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
|
Length::zero(),
|
||||||
CollapsedBlockMargins::zero(),
|
CollapsedBlockMargins::zero(),
|
||||||
physical_overconstrained,
|
physical_overconstrained,
|
||||||
)
|
)
|
||||||
|
@ -793,7 +794,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!(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -434,6 +434,8 @@ fn process_offset_parent_query_inner(
|
||||||
Fragment::AbsoluteOrFixedPositioned(_) |
|
Fragment::AbsoluteOrFixedPositioned(_) |
|
||||||
Fragment::Image(_) |
|
Fragment::Image(_) |
|
||||||
Fragment::IFrame(_) |
|
Fragment::IFrame(_) |
|
||||||
|
Fragment::Float |
|
||||||
|
Fragment::HoistedFloat(_) |
|
||||||
Fragment::Anonymous(_) => unreachable!(),
|
Fragment::Anonymous(_) => unreachable!(),
|
||||||
};
|
};
|
||||||
let border_box = fragment_relative_rect.translate(containing_block.origin.to_vector());
|
let border_box = fragment_relative_rect.translate(containing_block.origin.to_vector());
|
||||||
|
@ -510,6 +512,8 @@ fn process_offset_parent_query_inner(
|
||||||
Fragment::Text(_) |
|
Fragment::Text(_) |
|
||||||
Fragment::Image(_) |
|
Fragment::Image(_) |
|
||||||
Fragment::IFrame(_) |
|
Fragment::IFrame(_) |
|
||||||
|
Fragment::Float |
|
||||||
|
Fragment::HoistedFloat(_) |
|
||||||
Fragment::Anonymous(_) => None,
|
Fragment::Anonymous(_) => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -561,6 +565,8 @@ fn process_offset_parent_query_inner(
|
||||||
Fragment::Text(_) |
|
Fragment::Text(_) |
|
||||||
Fragment::Image(_) |
|
Fragment::Image(_) |
|
||||||
Fragment::IFrame(_) |
|
Fragment::IFrame(_) |
|
||||||
|
Fragment::Float |
|
||||||
|
Fragment::HoistedFloat(_) |
|
||||||
Fragment::Anonymous(_) => None,
|
Fragment::Anonymous(_) => None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -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::{FloatSide, InlineWalls, 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,9 +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);
|
||||||
|
@ -81,9 +79,7 @@ struct FloatRangeInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 band_count: u32 = Arbitrary::arbitrary(generator);
|
||||||
|
@ -341,19 +337,20 @@ 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,
|
||||||
|
/// Distances from the logical left side of the block formatting context to the logical sides
|
||||||
|
/// of the current containing block.
|
||||||
|
walls: InlineWalls,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arbitrary for FloatInput {
|
impl Arbitrary for FloatInput {
|
||||||
fn arbitrary<G>(generator: &mut G) -> FloatInput
|
fn arbitrary(generator: &mut Gen) -> FloatInput
|
||||||
where
|
|
||||||
G: Gen,
|
|
||||||
{
|
{
|
||||||
let width: u32 = Arbitrary::arbitrary(generator);
|
let width: u32 = Arbitrary::arbitrary(generator);
|
||||||
let height: u32 = Arbitrary::arbitrary(generator);
|
let height: u32 = Arbitrary::arbitrary(generator);
|
||||||
let is_left: bool = Arbitrary::arbitrary(generator);
|
let is_left: bool = Arbitrary::arbitrary(generator);
|
||||||
let ceiling: u32 = Arbitrary::arbitrary(generator);
|
let ceiling: u32 = Arbitrary::arbitrary(generator);
|
||||||
let left_wall: u32 = Arbitrary::arbitrary(generator);
|
let left: u32 = Arbitrary::arbitrary(generator);
|
||||||
let right_wall: u32 = Arbitrary::arbitrary(generator);
|
let right: u32 = Arbitrary::arbitrary(generator);
|
||||||
let clear: u8 = Arbitrary::arbitrary(generator);
|
let clear: u8 = Arbitrary::arbitrary(generator);
|
||||||
FloatInput {
|
FloatInput {
|
||||||
info: PlacementInfo {
|
info: PlacementInfo {
|
||||||
|
@ -367,10 +364,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,
|
||||||
|
walls: InlineWalls {
|
||||||
|
left: Length::new(left as f32),
|
||||||
|
right: Length::new(right as f32),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,12 +388,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.walls.left.px().shrink().next() {
|
||||||
this.info.left_wall = Length::new(left_wall);
|
this.walls.left = Length::new(left);
|
||||||
shrunk = true;
|
shrunk = true;
|
||||||
}
|
}
|
||||||
if let Some(right_wall) = self.info.right_wall.px().shrink().next() {
|
if let Some(right) = self.walls.right.px().shrink().next() {
|
||||||
this.info.right_wall = Length::new(right_wall);
|
this.walls.right = 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 +429,7 @@ struct PlacedFloat {
|
||||||
origin: Vec2<Length>,
|
origin: Vec2<Length>,
|
||||||
info: PlacementInfo,
|
info: PlacementInfo,
|
||||||
ceiling: Length,
|
ceiling: Length,
|
||||||
|
walls: InlineWalls,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for FloatPlacement {
|
impl Drop for FloatPlacement {
|
||||||
|
@ -442,8 +442,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.walls.left,
|
||||||
|
placed_float.walls.right,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
eprintln!("Bands:\n{:?}\n", self.float_context.bands);
|
eprintln!("Bands:\n{:?}\n", self.float_context.bands);
|
||||||
|
@ -466,10 +470,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.walls = float.walls;
|
||||||
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,
|
||||||
|
walls: float.walls,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
FloatPlacement {
|
FloatPlacement {
|
||||||
|
@ -488,9 +494,9 @@ 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.walls.left),
|
||||||
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.walls.right)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -596,12 +602,12 @@ 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.walls.right {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
FloatSide::Right => {
|
FloatSide::Right => {
|
||||||
if placed_float.origin.inline >= placed_float.info.left_wall {
|
if placed_float.origin.inline >= placed_float.walls.left {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -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-child-with-margins-2.html]
|
|
||||||
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
|
|
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