mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Use App units in flow layout (#30894)
* use app_unit in flow layout * fmt * Avoid crash * Drop assert that doesn't hold anymore * update expectation --------- Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
734eb46954
commit
3d520f2668
9 changed files with 313 additions and 225 deletions
|
@ -8,15 +8,16 @@
|
|||
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::{Debug, Formatter, Result as FmtResult};
|
||||
use std::mem;
|
||||
use std::ops::Range;
|
||||
use std::{f32, mem};
|
||||
|
||||
use app_units::{Au, MAX_AU, MIN_AU};
|
||||
use euclid::num::Zero;
|
||||
use serde::Serialize;
|
||||
use servo_arc::Arc;
|
||||
use style::computed_values::float::T as FloatProperty;
|
||||
use style::properties::ComputedValues;
|
||||
use style::values::computed::{CSSPixelLength, Clear, Length};
|
||||
use style::values::computed::{Clear, Length};
|
||||
use style::values::specified::text::TextDecorationLine;
|
||||
|
||||
use crate::context::LayoutContext;
|
||||
|
@ -24,7 +25,7 @@ use crate::dom::NodeExt;
|
|||
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
|
||||
use crate::formatting_contexts::IndependentFormattingContext;
|
||||
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, CollapsedMargin, FloatFragment};
|
||||
use crate::geom::{LogicalRect, LogicalVec2};
|
||||
use crate::geom::{LogicalRect, LogicalSides, LogicalVec2};
|
||||
use crate::positioned::PositioningContext;
|
||||
use crate::style_ext::{ComputedValuesExt, DisplayInside, PaddingBorderMargin};
|
||||
use crate::ContainingBlock;
|
||||
|
@ -47,7 +48,7 @@ pub struct ContainingBlockPositionInfo {
|
|||
/// containing block, excluding uncollapsed block start margins. Note that
|
||||
/// this does not include uncollapsed block start margins because we don't
|
||||
/// know the value of collapsed margins until we lay out children.
|
||||
pub(crate) block_start: Length,
|
||||
pub(crate) block_start: Au,
|
||||
/// Any uncollapsed block start margins that we have collected between the
|
||||
/// block start of the float containing independent block formatting context
|
||||
/// and this containing block, including for this containing block.
|
||||
|
@ -55,17 +56,17 @@ pub struct ContainingBlockPositionInfo {
|
|||
/// The distance from the inline start position of the float containing
|
||||
/// independent formatting context and the inline start of this containing
|
||||
/// block.
|
||||
pub inline_start: Length,
|
||||
pub inline_start: Au,
|
||||
/// The offset from the inline start position of the float containing
|
||||
/// independent formatting context to the inline end of this containing
|
||||
/// block.
|
||||
pub inline_end: Length,
|
||||
pub inline_end: Au,
|
||||
}
|
||||
|
||||
impl ContainingBlockPositionInfo {
|
||||
pub fn new_with_inline_offsets(inline_start: Length, inline_end: Length) -> Self {
|
||||
pub fn new_with_inline_offsets(inline_start: Au, inline_end: Au) -> Self {
|
||||
Self {
|
||||
block_start: Length::zero(),
|
||||
block_start: Au::zero(),
|
||||
block_start_margins_not_collapsed: CollapsedMargin::zero(),
|
||||
inline_start,
|
||||
inline_end,
|
||||
|
@ -84,33 +85,37 @@ pub(crate) struct PlacementAmongFloats<'a> {
|
|||
/// The next band, needed to know the height of the last band in current_bands.
|
||||
next_band: FloatBand,
|
||||
/// The size of the object to place.
|
||||
object_size: LogicalVec2<Length>,
|
||||
object_size: LogicalVec2<Au>,
|
||||
/// The minimum position in the block direction for the placement. Objects should not
|
||||
/// be placed before this point.
|
||||
ceiling: Length,
|
||||
ceiling: Au,
|
||||
/// The inline position where the object would be if there were no floats. The object
|
||||
/// can be placed after it due to floats, but not before it.
|
||||
min_inline_start: Length,
|
||||
min_inline_start: Au,
|
||||
/// The maximum inline position that the object can attain when avoiding floats.
|
||||
max_inline_end: Length,
|
||||
max_inline_end: Au,
|
||||
}
|
||||
|
||||
impl<'a> PlacementAmongFloats<'a> {
|
||||
pub(crate) fn new(
|
||||
float_context: &'a FloatContext,
|
||||
ceiling: Length,
|
||||
object_size: LogicalVec2<Length>,
|
||||
ceiling: Au,
|
||||
object_size: LogicalVec2<Au>,
|
||||
pbm: &PaddingBorderMargin,
|
||||
) -> Self {
|
||||
assert!(!ceiling.px().is_infinite());
|
||||
let mut current_band = float_context.bands.find(ceiling).unwrap();
|
||||
current_band.top = ceiling;
|
||||
let current_bands = VecDeque::from([current_band]);
|
||||
let mut ceiling_band = float_context.bands.find(ceiling).unwrap();
|
||||
let (current_bands, next_band) = if ceiling == MAX_AU {
|
||||
(VecDeque::new(), ceiling_band)
|
||||
} else {
|
||||
ceiling_band.top = ceiling;
|
||||
let current_bands = VecDeque::from([ceiling_band]);
|
||||
let next_band = float_context.bands.find_next(ceiling).unwrap();
|
||||
(current_bands, next_band)
|
||||
};
|
||||
let min_inline_start = float_context.containing_block_info.inline_start +
|
||||
pbm.margin.inline_start.auto_is(Length::zero);
|
||||
pbm.margin.inline_start.auto_is(Length::zero).into();
|
||||
let max_inline_end = (float_context.containing_block_info.inline_end -
|
||||
pbm.margin.inline_end.auto_is(Length::zero))
|
||||
pbm.margin.inline_end.auto_is(Length::zero).into())
|
||||
.max(min_inline_start + object_size.inline);
|
||||
PlacementAmongFloats {
|
||||
float_context,
|
||||
|
@ -126,24 +131,27 @@ impl<'a> PlacementAmongFloats<'a> {
|
|||
/// The top of the bands under consideration. This is initially the ceiling provided
|
||||
/// during creation of this [`PlacementAmongFloats`], but may be larger if the top
|
||||
/// band is discarded.
|
||||
fn top_of_bands(&self) -> Option<Length> {
|
||||
fn top_of_bands(&self) -> Option<Au> {
|
||||
self.current_bands.front().map(|band| band.top)
|
||||
}
|
||||
|
||||
/// The height of the bands under consideration.
|
||||
fn current_bands_height(&self) -> Length {
|
||||
if let Some(top) = self.top_of_bands() {
|
||||
self.next_band.top - top
|
||||
fn current_bands_height(&self) -> Au {
|
||||
if self.next_band.top == MAX_AU {
|
||||
// Treat MAX_AU as infinity.
|
||||
MAX_AU
|
||||
} else {
|
||||
assert!(self.next_band.top.px().is_infinite());
|
||||
self.next_band.top
|
||||
let top = self
|
||||
.top_of_bands()
|
||||
.expect("Should have bands before reaching the end");
|
||||
self.next_band.top - top
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a single band to the bands under consideration and calculate the new
|
||||
/// [`PlacementAmongFloats::next_band`].
|
||||
fn add_one_band(&mut self) {
|
||||
assert!(!self.next_band.top.px().is_infinite());
|
||||
assert!(self.next_band.top != MAX_AU);
|
||||
self.current_bands.push_back(self.next_band);
|
||||
self.next_band = self
|
||||
.float_context
|
||||
|
@ -162,7 +170,7 @@ impl<'a> PlacementAmongFloats<'a> {
|
|||
|
||||
/// Find the start and end of the inline space provided by the current set of bands
|
||||
/// under consideration.
|
||||
fn calculate_inline_start_and_end(&self) -> (Length, Length) {
|
||||
fn calculate_inline_start_and_end(&self) -> (Au, Au) {
|
||||
let mut max_inline_start = self.min_inline_start;
|
||||
let mut min_inline_end = self.max_inline_end;
|
||||
for band in self.current_bands.iter() {
|
||||
|
@ -177,12 +185,12 @@ impl<'a> PlacementAmongFloats<'a> {
|
|||
}
|
||||
|
||||
/// Find the total inline size provided by the current set of bands under consideration.
|
||||
fn calculate_viable_inline_size(&self) -> Length {
|
||||
fn calculate_viable_inline_size(&self) -> Au {
|
||||
let (inline_start, inline_end) = self.calculate_inline_start_and_end();
|
||||
inline_end - inline_start
|
||||
}
|
||||
|
||||
fn try_place_once(&mut self) -> Option<LogicalRect<Length>> {
|
||||
fn try_place_once(&mut self) -> Option<LogicalRect<Au>> {
|
||||
assert!(!self.current_bands.is_empty());
|
||||
self.accumulate_enough_bands_for_block_size();
|
||||
let (inline_start, inline_end) = self.calculate_inline_start_and_end();
|
||||
|
@ -190,15 +198,14 @@ impl<'a> PlacementAmongFloats<'a> {
|
|||
if available_inline_size < self.object_size.inline {
|
||||
return None;
|
||||
}
|
||||
let top = self.top_of_bands().unwrap();
|
||||
Some(LogicalRect {
|
||||
start_corner: LogicalVec2 {
|
||||
inline: inline_start,
|
||||
block: top,
|
||||
block: self.top_of_bands().unwrap(),
|
||||
},
|
||||
size: LogicalVec2 {
|
||||
inline: available_inline_size,
|
||||
block: self.next_band.top - top,
|
||||
block: self.current_bands_height(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -206,7 +213,7 @@ impl<'a> PlacementAmongFloats<'a> {
|
|||
/// Checks if we either have bands or we have gone past all of them.
|
||||
/// This is an invariant that should hold, otherwise we are in a broken state.
|
||||
fn has_bands_or_at_end(&self) -> bool {
|
||||
!self.current_bands.is_empty() || self.next_band.top.px().is_infinite()
|
||||
!self.current_bands.is_empty() || self.next_band.top == MAX_AU
|
||||
}
|
||||
|
||||
fn pop_front_band_ensuring_has_bands_or_at_end(&mut self) {
|
||||
|
@ -217,7 +224,7 @@ impl<'a> PlacementAmongFloats<'a> {
|
|||
}
|
||||
|
||||
/// Run the placement algorithm for this [PlacementAmongFloats].
|
||||
pub(crate) fn place(&mut self) -> LogicalRect<Length> {
|
||||
pub(crate) fn place(&mut self) -> LogicalRect<Au> {
|
||||
debug_assert!(self.has_bands_or_at_end());
|
||||
while !self.current_bands.is_empty() {
|
||||
if let Some(result) = self.try_place_once() {
|
||||
|
@ -239,7 +246,7 @@ impl<'a> PlacementAmongFloats<'a> {
|
|||
},
|
||||
size: LogicalVec2 {
|
||||
inline: self.max_inline_end - self.min_inline_start,
|
||||
block: Length::new(f32::INFINITY),
|
||||
block: MAX_AU,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -252,8 +259,8 @@ impl<'a> PlacementAmongFloats<'a> {
|
|||
/// (with this [PlacementAmongFloats]).
|
||||
pub(crate) fn try_to_expand_for_auto_block_size(
|
||||
&mut self,
|
||||
block_size_after_layout: Length,
|
||||
size_from_placement: &LogicalVec2<Length>,
|
||||
block_size_after_layout: Au,
|
||||
size_from_placement: &LogicalVec2<Au>,
|
||||
) -> bool {
|
||||
debug_assert!(self.has_bands_or_at_end());
|
||||
debug_assert_eq!(size_from_placement.block, self.current_bands_height());
|
||||
|
@ -309,59 +316,59 @@ pub struct FloatContext {
|
|||
pub bands: FloatBandTree,
|
||||
/// The block-direction "ceiling" defined by the placement of other floated content of
|
||||
/// this FloatContext. No new floats can be placed at a lower block start than this value.
|
||||
pub ceiling_from_floats: Length,
|
||||
pub ceiling_from_floats: Au,
|
||||
/// The block-direction "ceiling" defined by the placement of non-floated content that
|
||||
/// precedes floated content in the document. Note that this may actually decrease as
|
||||
/// content is laid out in the case that content overflows its container.
|
||||
pub ceiling_from_non_floats: Length,
|
||||
pub ceiling_from_non_floats: Au,
|
||||
/// Details about the position of the containing block relative to the
|
||||
/// independent block formatting context that contains all of the floats
|
||||
/// this `FloatContext` positions.
|
||||
pub containing_block_info: ContainingBlockPositionInfo,
|
||||
/// The (logically) lowest margin edge of the last left float.
|
||||
pub clear_left_position: Length,
|
||||
pub clear_left_position: Au,
|
||||
/// The (logically) lowest margin edge of the last right float.
|
||||
pub clear_right_position: Length,
|
||||
pub clear_right_position: Au,
|
||||
}
|
||||
|
||||
impl FloatContext {
|
||||
/// Returns a new float context representing a containing block with the given content
|
||||
/// inline-size.
|
||||
pub fn new(max_inline_size: Length) -> Self {
|
||||
pub fn new(max_inline_size: Au) -> Self {
|
||||
let mut bands = FloatBandTree::new();
|
||||
bands = bands.insert(FloatBand {
|
||||
top: Length::new(-f32::INFINITY),
|
||||
top: MIN_AU,
|
||||
left: None,
|
||||
right: None,
|
||||
});
|
||||
bands = bands.insert(FloatBand {
|
||||
top: Length::new(f32::INFINITY),
|
||||
top: MAX_AU,
|
||||
left: None,
|
||||
right: None,
|
||||
});
|
||||
FloatContext {
|
||||
bands,
|
||||
ceiling_from_floats: Length::zero(),
|
||||
ceiling_from_non_floats: Length::zero(),
|
||||
ceiling_from_floats: Au::zero(),
|
||||
ceiling_from_non_floats: Au::zero(),
|
||||
containing_block_info: ContainingBlockPositionInfo::new_with_inline_offsets(
|
||||
Length::zero(),
|
||||
Au::zero(),
|
||||
max_inline_size,
|
||||
),
|
||||
clear_left_position: Length::zero(),
|
||||
clear_right_position: Length::zero(),
|
||||
clear_left_position: Au::zero(),
|
||||
clear_right_position: Au::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
/// (Logically) lowers the ceiling to at least `new_ceiling` units.
|
||||
///
|
||||
/// If the ceiling is already logically lower (i.e. larger) than this, does nothing.
|
||||
pub fn set_ceiling_from_non_floats(&mut self, new_ceiling: Length) {
|
||||
pub fn set_ceiling_from_non_floats(&mut self, new_ceiling: Au) {
|
||||
self.ceiling_from_non_floats = new_ceiling;
|
||||
}
|
||||
|
||||
/// The "ceiling" used for float placement. This is the minimum block position value
|
||||
/// that should be used for placing any new float.
|
||||
fn ceiling(&mut self) -> Length {
|
||||
fn ceiling(&mut self) -> Au {
|
||||
self.ceiling_from_floats.max(self.ceiling_from_non_floats)
|
||||
}
|
||||
|
||||
|
@ -370,11 +377,7 @@ impl FloatContext {
|
|||
///
|
||||
/// This should be used for placing inline elements and block formatting contexts so that they
|
||||
/// don't collide with floats.
|
||||
pub(crate) fn place_object(
|
||||
&self,
|
||||
object: &PlacementInfo,
|
||||
ceiling: Length,
|
||||
) -> LogicalVec2<Length> {
|
||||
pub(crate) fn place_object(&self, object: &PlacementInfo, ceiling: Au) -> LogicalVec2<Au> {
|
||||
let ceiling = match object.clear {
|
||||
Clear::None => ceiling,
|
||||
Clear::Left => ceiling.max(self.clear_left_position),
|
||||
|
@ -388,7 +391,7 @@ impl FloatContext {
|
|||
let mut first_band = self.bands.find(ceiling).unwrap();
|
||||
while !first_band.object_fits(&object, &self.containing_block_info) {
|
||||
let next_band = self.bands.find_next(first_band.top).unwrap();
|
||||
if next_band.top.px().is_infinite() {
|
||||
if next_band.top == MAX_AU {
|
||||
break;
|
||||
}
|
||||
first_band = next_band;
|
||||
|
@ -420,7 +423,7 @@ impl FloatContext {
|
|||
}
|
||||
|
||||
/// Places a new float and adds it to the list. Returns the start corner of its margin box.
|
||||
pub fn add_float(&mut self, new_float: &PlacementInfo) -> LogicalVec2<Length> {
|
||||
pub fn add_float(&mut self, new_float: &PlacementInfo) -> LogicalVec2<Au> {
|
||||
// Place the float.
|
||||
let ceiling = self.ceiling();
|
||||
let new_float_origin = self.place_object(&new_float, ceiling);
|
||||
|
@ -436,8 +439,8 @@ impl FloatContext {
|
|||
// so negative that it's placed completely above the current float ceiling, then
|
||||
// we should position it as if it had zero block size.
|
||||
size: LogicalVec2 {
|
||||
inline: new_float.size.inline.max(CSSPixelLength::zero()),
|
||||
block: new_float.size.block.max(CSSPixelLength::zero()),
|
||||
inline: new_float.size.inline.max(Au::zero()),
|
||||
block: new_float.size.block.max(Au::zero()),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -476,17 +479,25 @@ impl FloatContext {
|
|||
|
||||
// CSS 2.1 § 9.5.1 rule 6: The outer top of a floating box may not be higher than the outer
|
||||
// top of any block or floated box generated by an element earlier in the source document.
|
||||
self.ceiling_from_floats
|
||||
.max_assign(new_float_rect.start_corner.block);
|
||||
max_assign_au(
|
||||
&mut self.ceiling_from_floats,
|
||||
new_float_rect.start_corner.block,
|
||||
);
|
||||
|
||||
new_float_rect.start_corner
|
||||
}
|
||||
}
|
||||
|
||||
fn max_assign_au(current: &mut Au, other: Au) {
|
||||
let max_value = std::cmp::max(current.0, other.0);
|
||||
*current = Au(max_value);
|
||||
}
|
||||
|
||||
/// Information needed to place an object so that it doesn't collide with existing floats.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PlacementInfo {
|
||||
/// The *margin* box size of the object.
|
||||
pub size: LogicalVec2<Length>,
|
||||
pub size: LogicalVec2<Au>,
|
||||
/// Whether the object is (logically) aligned to the left or right.
|
||||
pub side: FloatSide,
|
||||
/// Which side or sides to clear floats on.
|
||||
|
@ -507,17 +518,17 @@ pub enum FloatSide {
|
|||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct FloatBand {
|
||||
/// The logical vertical position of the top of this band.
|
||||
pub top: Length,
|
||||
pub top: Au,
|
||||
/// The distance from the left edge of the block formatting context to the first legal
|
||||
/// (logically) horizontal position where floats may be placed. If `None`, there are no floats
|
||||
/// to the left; distinguishing between the cases of "a zero-width float is present" and "no
|
||||
/// floats at all are present" is necessary to, for example, clear past zero-width floats.
|
||||
pub left: Option<Length>,
|
||||
pub left: Option<Au>,
|
||||
/// The distance from the *left* edge of the block formatting context to the first legal
|
||||
/// (logically) horizontal position where floats may be placed. If `None`, there are no floats
|
||||
/// to the right; distinguishing between the cases of "a zero-width float is present" and "no
|
||||
/// floats at all are present" is necessary to, for example, clear past zero-width floats.
|
||||
pub right: Option<Length>,
|
||||
pub right: Option<Au>,
|
||||
}
|
||||
|
||||
impl FloatSide {
|
||||
|
@ -624,24 +635,19 @@ impl FloatBandTree {
|
|||
}
|
||||
|
||||
/// Returns the first band whose top is less than or equal to the given `block_position`.
|
||||
pub fn find(&self, block_position: Length) -> Option<FloatBand> {
|
||||
pub fn find(&self, block_position: Au) -> Option<FloatBand> {
|
||||
self.root.find(block_position)
|
||||
}
|
||||
|
||||
/// Returns the first band whose top is strictly greater than to the given `block_position`.
|
||||
pub fn find_next(&self, block_position: Length) -> Option<FloatBand> {
|
||||
pub fn find_next(&self, block_position: Au) -> Option<FloatBand> {
|
||||
self.root.find_next(block_position)
|
||||
}
|
||||
|
||||
/// Sets the side values of all bands within the given half-open range to be at least
|
||||
/// `new_value`.
|
||||
#[must_use]
|
||||
pub fn set_range(
|
||||
&self,
|
||||
range: &Range<Length>,
|
||||
side: FloatSide,
|
||||
new_value: Length,
|
||||
) -> FloatBandTree {
|
||||
pub fn set_range(&self, range: &Range<Au>, side: FloatSide, new_value: Au) -> FloatBandTree {
|
||||
FloatBandTree {
|
||||
root: FloatBandLink(
|
||||
self.root
|
||||
|
@ -674,22 +680,21 @@ impl FloatBandNode {
|
|||
|
||||
/// Sets the side values of all bands within the given half-open range to be at least
|
||||
/// `new_value`.
|
||||
fn set_range(
|
||||
&self,
|
||||
range: &Range<Length>,
|
||||
side: FloatSide,
|
||||
new_value: Length,
|
||||
) -> Arc<FloatBandNode> {
|
||||
fn set_range(&self, range: &Range<Au>, side: FloatSide, new_value: Au) -> Arc<FloatBandNode> {
|
||||
let mut new_band = self.band.clone();
|
||||
if self.band.top >= range.start && self.band.top < range.end {
|
||||
match side {
|
||||
FloatSide::Left => match new_band.left {
|
||||
None => new_band.left = Some(new_value),
|
||||
Some(ref mut old_value) => *old_value = old_value.max(new_value),
|
||||
FloatSide::Left => {
|
||||
new_band.left = match new_band.left {
|
||||
Some(old_value) => Some(std::cmp::max(old_value, new_value)),
|
||||
None => Some(new_value),
|
||||
};
|
||||
},
|
||||
FloatSide::Right => match new_band.right {
|
||||
None => new_band.right = Some(new_value),
|
||||
Some(ref mut old_value) => *old_value = old_value.min(new_value),
|
||||
FloatSide::Right => {
|
||||
new_band.right = match new_band.right {
|
||||
Some(old_value) => Some(std::cmp::min(old_value, new_value)),
|
||||
None => Some(new_value),
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -721,7 +726,7 @@ impl FloatBandNode {
|
|||
|
||||
impl FloatBandLink {
|
||||
/// Returns the first band whose top is less than or equal to the given `block_position`.
|
||||
fn find(&self, block_position: Length) -> Option<FloatBand> {
|
||||
fn find(&self, block_position: Au) -> Option<FloatBand> {
|
||||
let this = match self.0 {
|
||||
None => return None,
|
||||
Some(ref node) => node,
|
||||
|
@ -741,7 +746,7 @@ impl FloatBandLink {
|
|||
}
|
||||
|
||||
/// Returns the first band whose top is strictly greater than the given `block_position`.
|
||||
fn find_next(&self, block_position: Length) -> Option<FloatBand> {
|
||||
fn find_next(&self, block_position: Au) -> Option<FloatBand> {
|
||||
let this = match self.0 {
|
||||
None => return None,
|
||||
Some(ref node) => node,
|
||||
|
@ -886,7 +891,7 @@ impl FloatBox {
|
|||
// 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 margin = pbm.margin.auto_is(Length::zero);
|
||||
let pbm_sums = &(&pbm.padding + &pbm.border) + &margin;
|
||||
|
||||
let (content_size, children);
|
||||
|
@ -993,18 +998,18 @@ pub(crate) struct SequentialLayoutState {
|
|||
/// This is often, but not always, the same as the float ceiling. The float ceiling can be lower
|
||||
/// than this value because this value is calculated based on in-flow boxes only, while
|
||||
/// out-of-flow floats can affect the ceiling as well (see CSS 2.1 § 9.5.1 rule 6).
|
||||
pub(crate) bfc_relative_block_position: Length,
|
||||
pub(crate) bfc_relative_block_position: Au,
|
||||
/// Any collapsible margins that we've encountered after `bfc_relative_block_position`.
|
||||
pub(crate) current_margin: CollapsedMargin,
|
||||
}
|
||||
|
||||
impl SequentialLayoutState {
|
||||
/// Creates a new empty `SequentialLayoutState`.
|
||||
pub(crate) fn new(max_inline_size: Length) -> SequentialLayoutState {
|
||||
pub(crate) fn new(max_inline_size: Au) -> SequentialLayoutState {
|
||||
SequentialLayoutState {
|
||||
floats: FloatContext::new(max_inline_size),
|
||||
current_margin: CollapsedMargin::zero(),
|
||||
bfc_relative_block_position: Length::zero(),
|
||||
bfc_relative_block_position: Au::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1014,7 +1019,7 @@ impl SequentialLayoutState {
|
|||
/// [`SequentialLayoutState`] after processing its overflowing content.
|
||||
///
|
||||
/// Floats may not be placed higher than the current block position.
|
||||
pub(crate) fn advance_block_position(&mut self, block_distance: Length) {
|
||||
pub(crate) fn advance_block_position(&mut self, block_distance: Au) {
|
||||
self.bfc_relative_block_position += block_distance;
|
||||
self.floats
|
||||
.set_ceiling_from_non_floats(self.bfc_relative_block_position);
|
||||
|
@ -1032,8 +1037,8 @@ impl SequentialLayoutState {
|
|||
|
||||
/// Return the current block position in the float containing block formatting
|
||||
/// context and any uncollapsed block margins.
|
||||
pub(crate) fn current_block_position_including_margins(&self) -> Length {
|
||||
self.bfc_relative_block_position + self.current_margin.solve()
|
||||
pub(crate) fn current_block_position_including_margins(&self) -> Au {
|
||||
self.bfc_relative_block_position + self.current_margin.solve().into()
|
||||
}
|
||||
|
||||
/// Collapses margins, moving the block position down by the collapsed value of `current_margin`
|
||||
|
@ -1042,29 +1047,29 @@ impl SequentialLayoutState {
|
|||
/// 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.advance_block_position(self.current_margin.solve().into());
|
||||
self.current_margin = CollapsedMargin::zero();
|
||||
}
|
||||
|
||||
/// Computes the position of the block-start border edge of an element
|
||||
/// with the provided `block_start_margin`, assuming no clearance.
|
||||
pub(crate) fn position_without_clearance(
|
||||
&self,
|
||||
block_start_margin: &CollapsedMargin,
|
||||
) -> Length {
|
||||
pub(crate) fn position_without_clearance(&self, block_start_margin: &CollapsedMargin) -> Au {
|
||||
// Adjoin `current_margin` and `block_start_margin` since there is no clearance.
|
||||
self.bfc_relative_block_position + self.current_margin.adjoin(&block_start_margin).solve()
|
||||
self.bfc_relative_block_position +
|
||||
self.current_margin
|
||||
.adjoin(&block_start_margin)
|
||||
.solve()
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Computes the position of the block-start border edge of an element
|
||||
/// with the provided `block_start_margin`, assuming a clearance of 0px.
|
||||
pub(crate) fn position_with_zero_clearance(
|
||||
&self,
|
||||
block_start_margin: &CollapsedMargin,
|
||||
) -> Length {
|
||||
pub(crate) fn position_with_zero_clearance(&self, block_start_margin: &CollapsedMargin) -> Au {
|
||||
// Clearance prevents `current_margin` and `block_start_margin` from being
|
||||
// adjoining, so we need to solve them separately and then sum.
|
||||
self.bfc_relative_block_position + self.current_margin.solve() + block_start_margin.solve()
|
||||
self.bfc_relative_block_position +
|
||||
self.current_margin.solve().into() +
|
||||
block_start_margin.solve().into()
|
||||
}
|
||||
|
||||
/// Returns the block-end outer edge of the lowest float that is to be cleared (if any)
|
||||
|
@ -1073,7 +1078,7 @@ impl SequentialLayoutState {
|
|||
&self,
|
||||
clear: Clear,
|
||||
block_start_margin: &CollapsedMargin,
|
||||
) -> Option<Length> {
|
||||
) -> Option<Au> {
|
||||
if clear == Clear::None {
|
||||
return None;
|
||||
}
|
||||
|
@ -1112,7 +1117,7 @@ impl SequentialLayoutState {
|
|||
&self,
|
||||
clear: Clear,
|
||||
block_start_margin: &CollapsedMargin,
|
||||
) -> Option<Length> {
|
||||
) -> Option<Au> {
|
||||
return self
|
||||
.calculate_clear_position(clear, &block_start_margin)
|
||||
.map(|offset| offset - self.position_with_zero_clearance(&block_start_margin));
|
||||
|
@ -1131,8 +1136,8 @@ impl SequentialLayoutState {
|
|||
clear: Clear,
|
||||
block_start_margin: &CollapsedMargin,
|
||||
pbm: &PaddingBorderMargin,
|
||||
object_size: LogicalVec2<Length>,
|
||||
) -> (Option<Length>, LogicalRect<Length>) {
|
||||
object_size: LogicalVec2<Au>,
|
||||
) -> (Option<Au>, LogicalRect<Au>) {
|
||||
// First compute the clear position required by the 'clear' property.
|
||||
// The code below may then add extra clearance when the element can't fit
|
||||
// next to floats not covered by 'clear'.
|
||||
|
@ -1157,12 +1162,13 @@ impl SequentialLayoutState {
|
|||
}
|
||||
|
||||
/// Get the offset of the current containing block and any uncollapsed margins.
|
||||
pub(crate) fn current_containing_block_offset(&self) -> CSSPixelLength {
|
||||
pub(crate) fn current_containing_block_offset(&self) -> Au {
|
||||
self.floats.containing_block_info.block_start +
|
||||
self.floats
|
||||
.containing_block_info
|
||||
.block_start_margins_not_collapsed
|
||||
.solve()
|
||||
.into()
|
||||
}
|
||||
|
||||
/// This function places a Fragment that has been created for a FloatBox.
|
||||
|
@ -1170,30 +1176,33 @@ impl SequentialLayoutState {
|
|||
&mut self,
|
||||
box_fragment: &mut BoxFragment,
|
||||
margins_collapsing_with_parent_containing_block: CollapsedMargin,
|
||||
block_offset_from_containing_block_top: Length,
|
||||
block_offset_from_containing_block_top: Au,
|
||||
) {
|
||||
let block_start_of_containing_block_in_bfc = self.floats.containing_block_info.block_start +
|
||||
self.floats
|
||||
.containing_block_info
|
||||
.block_start_margins_not_collapsed
|
||||
.adjoin(&margins_collapsing_with_parent_containing_block)
|
||||
.solve();
|
||||
.solve()
|
||||
.into();
|
||||
|
||||
self.floats.set_ceiling_from_non_floats(
|
||||
block_start_of_containing_block_in_bfc + block_offset_from_containing_block_top,
|
||||
);
|
||||
|
||||
let pbm_sums = &(&box_fragment.padding + &box_fragment.border) + &box_fragment.margin;
|
||||
let content_rect: LogicalRect<Au> = box_fragment.content_rect.clone().into();
|
||||
let pbm_sums_all: LogicalSides<Au> = pbm_sums.map(|length| (*length).into());
|
||||
let margin_box_start_corner = self.floats.add_float(&PlacementInfo {
|
||||
size: &box_fragment.content_rect.size + &pbm_sums.sum(),
|
||||
size: &content_rect.size + &pbm_sums_all.sum(),
|
||||
side: FloatSide::from_style(&box_fragment.style).expect("Float box wasn't floated!"),
|
||||
clear: box_fragment.style.get_box().clear,
|
||||
});
|
||||
|
||||
// This is the position of the float in the float-containing block formatting context. We add the
|
||||
// existing start corner here because we may have already gotten some relative positioning offset.
|
||||
let new_position_in_bfc = &(&margin_box_start_corner + &pbm_sums.start_offset()) +
|
||||
&box_fragment.content_rect.start_corner;
|
||||
let new_position_in_bfc =
|
||||
&(&margin_box_start_corner + &pbm_sums_all.start_offset()) + &content_rect.start_corner;
|
||||
|
||||
// This is the position of the float relative to the containing block start.
|
||||
let new_position_in_containing_block = LogicalVec2 {
|
||||
|
@ -1201,6 +1210,6 @@ impl SequentialLayoutState {
|
|||
block: new_position_in_bfc.block - block_start_of_containing_block_in_bfc,
|
||||
};
|
||||
|
||||
box_fragment.content_rect.start_corner = new_position_in_containing_block;
|
||||
box_fragment.content_rect.start_corner = new_position_in_containing_block.into();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,10 +160,10 @@ impl LineUnderConstruction {
|
|||
}
|
||||
}
|
||||
|
||||
fn line_block_start_considering_placement_among_floats(&self) -> Length {
|
||||
fn line_block_start_considering_placement_among_floats(&self) -> Au {
|
||||
match self.placement_among_floats.get() {
|
||||
Some(placement_among_floats) => placement_among_floats.start_corner.block,
|
||||
None => self.start_position.block,
|
||||
Some(placement_among_floats) => placement_among_floats.start_corner.block.into(),
|
||||
None => self.start_position.block.into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -730,12 +730,12 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
|||
LineBlockSizes::zero()
|
||||
};
|
||||
|
||||
let block_end_position = block_start_position + effective_block_advance.resolve();
|
||||
let block_end_position = block_start_position + effective_block_advance.resolve().into();
|
||||
if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
|
||||
// This amount includes both the block size of the line and any extra space
|
||||
// added to move the line down in order to avoid overlapping floats.
|
||||
let increment = block_end_position - self.current_line.start_position.block;
|
||||
sequential_layout_state.advance_block_position(increment);
|
||||
let increment = block_end_position - self.current_line.start_position.block.into();
|
||||
sequential_layout_state.advance_block_position(increment.into());
|
||||
}
|
||||
|
||||
let mut line_items = std::mem::take(&mut self.current_line.line_items);
|
||||
|
@ -746,7 +746,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
|||
// Set up the new line now that we no longer need the old one.
|
||||
self.current_line = LineUnderConstruction::new(LogicalVec2 {
|
||||
inline: Length::zero(),
|
||||
block: block_end_position,
|
||||
block: block_end_position.into(),
|
||||
});
|
||||
|
||||
let baseline_offset = effective_block_advance.find_baseline_offset();
|
||||
|
@ -759,7 +759,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
|||
positioning_context: &mut self.positioning_context,
|
||||
justification_adjustment,
|
||||
line_metrics: &LineMetrics {
|
||||
block_offset: block_start_position,
|
||||
block_offset: block_start_position.into(),
|
||||
block_size: effective_block_advance.resolve(),
|
||||
baseline_block_offset: baseline_offset,
|
||||
},
|
||||
|
@ -782,14 +782,14 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
|||
return;
|
||||
}
|
||||
|
||||
self.last_baseline_offset = Some(baseline_offset + block_start_position);
|
||||
self.last_baseline_offset = Some(baseline_offset + block_start_position.into());
|
||||
let line_rect = LogicalRect {
|
||||
// The inline part of this start offset was taken into account when determining
|
||||
// the inline start of the line in `calculate_inline_start_for_current_line` so
|
||||
// we do not need to include it in the `start_corner` of the line's main Fragment.
|
||||
start_corner: LogicalVec2 {
|
||||
inline: Length::zero(),
|
||||
block: block_start_position,
|
||||
block: block_start_position.into(),
|
||||
},
|
||||
size: LogicalVec2 {
|
||||
inline: self.containing_block.inline_size,
|
||||
|
@ -1000,7 +1000,9 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
|||
.floats
|
||||
.containing_block_info
|
||||
.inline_start,
|
||||
block: sequential_layout_state.current_containing_block_offset(),
|
||||
block: sequential_layout_state
|
||||
.current_containing_block_offset()
|
||||
.into(),
|
||||
};
|
||||
|
||||
let ceiling = self
|
||||
|
@ -1008,14 +1010,17 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
|||
.line_block_start_considering_placement_among_floats();
|
||||
let mut placement = PlacementAmongFloats::new(
|
||||
&sequential_layout_state.floats,
|
||||
ceiling + ifc_offset_in_float_container.block,
|
||||
potential_line_size.clone(),
|
||||
ceiling + ifc_offset_in_float_container.block.into(),
|
||||
LogicalVec2 {
|
||||
inline: potential_line_size.inline.into(),
|
||||
block: potential_line_size.block.into(),
|
||||
},
|
||||
&PaddingBorderMargin::zero(),
|
||||
);
|
||||
|
||||
let mut placement_rect = placement.place();
|
||||
placement_rect.start_corner = &placement_rect.start_corner - &ifc_offset_in_float_container;
|
||||
placement_rect
|
||||
placement_rect.into()
|
||||
}
|
||||
|
||||
/// Returns true if a new potential line size for the current line would require a line
|
||||
|
@ -1083,6 +1088,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
|||
if new_placement.start_corner.block !=
|
||||
self.current_line
|
||||
.line_block_start_considering_placement_among_floats()
|
||||
.into()
|
||||
{
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
//! Flow layout, also known as block-and-inline layout.
|
||||
|
||||
use app_units::Au;
|
||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||
use serde::Serialize;
|
||||
use servo_arc::Arc;
|
||||
|
@ -213,7 +214,9 @@ impl BlockFormattingContext {
|
|||
containing_block: &ContainingBlock,
|
||||
) -> IndependentLayout {
|
||||
let mut sequential_layout_state = if self.contains_floats || !layout_context.use_rayon {
|
||||
Some(SequentialLayoutState::new(containing_block.inline_size))
|
||||
Some(SequentialLayoutState::new(
|
||||
containing_block.inline_size.into(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -242,7 +245,7 @@ impl BlockFormattingContext {
|
|||
fragments: flow_layout.fragments,
|
||||
content_block_size: (flow_layout.content_block_size +
|
||||
flow_layout.collapsible_margins_in_children.end.solve() +
|
||||
clearance.unwrap_or_else(Length::zero))
|
||||
clearance.unwrap_or_else(Au::zero).into())
|
||||
.into(),
|
||||
last_inflow_baseline_offset: flow_layout.last_inflow_baseline_offset.map(|t| t.into()),
|
||||
}
|
||||
|
@ -692,9 +695,10 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context(
|
|||
// 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.padding.block_start +
|
||||
pbm.border.block_start +
|
||||
clearance.unwrap_or_else(Length::zero),
|
||||
clearance.unwrap_or_else(Au::zero).into())
|
||||
.into(),
|
||||
);
|
||||
|
||||
// We are about to lay out children. Update the offset between the block formatting
|
||||
|
@ -706,14 +710,14 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context(
|
|||
.floats
|
||||
.containing_block_info
|
||||
.inline_start +
|
||||
pbm.padding.inline_start +
|
||||
pbm.border.inline_start +
|
||||
margin.inline_start;
|
||||
pbm.padding.inline_start.into() +
|
||||
pbm.border.inline_start.into() +
|
||||
margin.inline_start.into();
|
||||
let new_cb_offsets = ContainingBlockPositionInfo {
|
||||
block_start: sequential_layout_state.bfc_relative_block_position,
|
||||
block_start_margins_not_collapsed: sequential_layout_state.current_margin,
|
||||
inline_start,
|
||||
inline_end: inline_start + containing_block_for_children.inline_size,
|
||||
inline_end: inline_start + containing_block_for_children.inline_size.into(),
|
||||
};
|
||||
parent_containing_block_position_info = Some(
|
||||
sequential_layout_state.replace_containing_block_position_info(new_cb_offsets),
|
||||
|
@ -781,7 +785,7 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context(
|
|||
// the block direction. In that case, the ceiling for floats is effectively raised
|
||||
// as long as no floats in the overflowing content lowered it.
|
||||
sequential_layout_state.advance_block_position(
|
||||
(block_size - content_block_size) + pbm.padding.block_end + pbm.border.block_end,
|
||||
(block_size - content_block_size + pbm.padding.block_end + pbm.border.block_end).into(),
|
||||
);
|
||||
|
||||
if !end_margin_can_collapse_with_children {
|
||||
|
@ -792,9 +796,10 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context(
|
|||
|
||||
let content_rect = LogicalRect {
|
||||
start_corner: LogicalVec2 {
|
||||
block: pbm.padding.block_start +
|
||||
block: (pbm.padding.block_start +
|
||||
pbm.border.block_start +
|
||||
clearance.unwrap_or_else(Length::zero),
|
||||
clearance.unwrap_or_else(Au::zero).into())
|
||||
.into(),
|
||||
inline: pbm.padding.inline_start + pbm.border.inline_start + margin.inline_start,
|
||||
},
|
||||
size: LogicalVec2 {
|
||||
|
@ -811,7 +816,7 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context(
|
|||
pbm.padding,
|
||||
pbm.border,
|
||||
margin,
|
||||
clearance,
|
||||
clearance.map(|t| t.into()),
|
||||
flow_layout.last_inflow_baseline_offset,
|
||||
block_margins_collapsed_with_children,
|
||||
)
|
||||
|
@ -987,7 +992,7 @@ impl NonReplacedFormattingContext {
|
|||
let mut placement = PlacementAmongFloats::new(
|
||||
&sequential_layout_state.floats,
|
||||
ceiling,
|
||||
minimum_size_of_block,
|
||||
minimum_size_of_block.into(),
|
||||
&pbm,
|
||||
);
|
||||
let mut placement_rect;
|
||||
|
@ -995,8 +1000,9 @@ impl NonReplacedFormattingContext {
|
|||
loop {
|
||||
// First try to place the block using the minimum size as the object size.
|
||||
placement_rect = placement.place();
|
||||
let proposed_inline_size = (placement_rect.size.inline -
|
||||
pbm.padding_border_sums.inline)
|
||||
let proposed_inline_size = Length::from(
|
||||
placement_rect.size.inline - pbm.padding_border_sums.inline.into(),
|
||||
)
|
||||
.clamp_between_extremums(min_box_size.inline, max_box_size.inline);
|
||||
|
||||
// Now lay out the block using the inline size we calculated from the placement.
|
||||
|
@ -1029,7 +1035,7 @@ impl NonReplacedFormattingContext {
|
|||
// size of auto. Try to fit it into our precalculated placement among the
|
||||
// floats. If it fits, then we can stop trying layout candidates.
|
||||
if placement.try_to_expand_for_auto_block_size(
|
||||
content_size.block + pbm.padding_border_sums.block,
|
||||
(content_size.block + pbm.padding_border_sums.block).into(),
|
||||
&placement_rect.size,
|
||||
) {
|
||||
break;
|
||||
|
@ -1046,9 +1052,10 @@ impl NonReplacedFormattingContext {
|
|||
// prevent margin collapse.
|
||||
clearance = if clear_position.is_some() || placement_rect.start_corner.block > ceiling {
|
||||
Some(
|
||||
placement_rect.start_corner.block -
|
||||
(placement_rect.start_corner.block -
|
||||
sequential_layout_state
|
||||
.position_with_zero_clearance(&collapsed_margin_block_start),
|
||||
.position_with_zero_clearance(&collapsed_margin_block_start))
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
|
@ -1059,7 +1066,7 @@ impl NonReplacedFormattingContext {
|
|||
&containing_block,
|
||||
&pbm,
|
||||
content_size.inline + pbm.padding_border_sums.inline,
|
||||
placement_rect,
|
||||
placement_rect.into(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1080,9 +1087,10 @@ impl NonReplacedFormattingContext {
|
|||
// Margins can never collapse into independent formatting contexts.
|
||||
sequential_layout_state.collapse_margins();
|
||||
sequential_layout_state.advance_block_position(
|
||||
pbm.padding_border_sums.block +
|
||||
(pbm.padding_border_sums.block +
|
||||
content_size.block +
|
||||
clearance.unwrap_or_else(Length::zero),
|
||||
clearance.unwrap_or_else(Length::zero))
|
||||
.into(),
|
||||
);
|
||||
sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
|
||||
|
||||
|
@ -1163,7 +1171,7 @@ fn layout_in_flow_replaced_block_level<'a>(
|
|||
// Margins can never collapse into replaced elements.
|
||||
sequential_layout_state.collapse_margins();
|
||||
sequential_layout_state
|
||||
.advance_block_position(size.block + clearance.unwrap_or_else(Length::zero));
|
||||
.advance_block_position((size.block + clearance.unwrap_or_else(Length::zero)).into());
|
||||
sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin_block_end));
|
||||
} else {
|
||||
clearance = None;
|
||||
|
@ -1299,16 +1307,16 @@ fn solve_clearance_and_inline_margins_avoiding_floats(
|
|||
style.get_box().clear,
|
||||
&block_start_margin,
|
||||
&pbm,
|
||||
size.clone(),
|
||||
size.clone().into(),
|
||||
);
|
||||
let inline_margins = solve_inline_margins_avoiding_floats(
|
||||
&sequential_layout_state,
|
||||
&containing_block,
|
||||
&pbm,
|
||||
size.inline,
|
||||
placement_rect,
|
||||
placement_rect.into(),
|
||||
);
|
||||
(clearance, inline_margins)
|
||||
(clearance.map(|t| t.into()), inline_margins)
|
||||
}
|
||||
|
||||
/// Resolves the margins of an in-flow block-level box in the inline axis
|
||||
|
@ -1325,7 +1333,8 @@ fn solve_inline_margins_avoiding_floats(
|
|||
sequential_layout_state
|
||||
.floats
|
||||
.containing_block_info
|
||||
.inline_start;
|
||||
.inline_start
|
||||
.into();
|
||||
assert!(placement_rect.size.inline >= inline_size);
|
||||
let free_space = placement_rect.size.inline - inline_size;
|
||||
let margin_inline_start = match (pbm.margin.inline_start, pbm.margin.inline_end) {
|
||||
|
@ -1493,7 +1502,7 @@ impl PlacementState {
|
|||
sequential_layout_state.place_float_fragment(
|
||||
box_fragment,
|
||||
self.start_margin,
|
||||
block_offset_from_containing_block_top,
|
||||
block_offset_from_containing_block_top.into(),
|
||||
);
|
||||
},
|
||||
Fragment::Anonymous(_) => {},
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::convert::From;
|
||||
use std::fmt;
|
||||
use std::ops::{Add, AddAssign, Sub};
|
||||
|
||||
|
@ -10,7 +11,7 @@ use serde::Serialize;
|
|||
use style::logical_geometry::{
|
||||
BlockFlowDirection, InlineBaseDirection, PhysicalCorner, WritingMode,
|
||||
};
|
||||
use style::values::computed::{Length, LengthPercentage};
|
||||
use style::values::computed::{CSSPixelLength, Length, LengthPercentage};
|
||||
use style::values::generics::length::GenericLengthPercentageOrAuto as AutoOr;
|
||||
use style::Zero;
|
||||
use style_traits::CSSPixel;
|
||||
|
@ -415,3 +416,51 @@ impl<T> LogicalRect<T> {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LogicalVec2<CSSPixelLength>> for LogicalVec2<Au> {
|
||||
fn from(value: LogicalVec2<CSSPixelLength>) -> Self {
|
||||
LogicalVec2 {
|
||||
inline: value.inline.into(),
|
||||
block: value.block.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LogicalVec2<Au>> for LogicalVec2<CSSPixelLength> {
|
||||
fn from(value: LogicalVec2<Au>) -> Self {
|
||||
LogicalVec2 {
|
||||
inline: value.inline.into(),
|
||||
block: value.block.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LogicalRect<Au>> for LogicalRect<CSSPixelLength> {
|
||||
fn from(value: LogicalRect<Au>) -> Self {
|
||||
LogicalRect {
|
||||
start_corner: LogicalVec2 {
|
||||
inline: value.start_corner.inline.into(),
|
||||
block: value.start_corner.block.into(),
|
||||
},
|
||||
size: LogicalVec2 {
|
||||
inline: value.size.inline.into(),
|
||||
block: value.size.block.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LogicalRect<CSSPixelLength>> for LogicalRect<Au> {
|
||||
fn from(value: LogicalRect<CSSPixelLength>) -> Self {
|
||||
LogicalRect {
|
||||
start_corner: LogicalVec2 {
|
||||
inline: value.start_corner.inline.into(),
|
||||
block: value.start_corner.block.into(),
|
||||
},
|
||||
size: LogicalVec2 {
|
||||
inline: value.size.inline.into(),
|
||||
block: value.size.block.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
|
||||
//! Property-based randomized testing for the core float layout algorithm.
|
||||
|
||||
use std::f32::INFINITY;
|
||||
use std::ops::Range;
|
||||
use std::panic::{self, PanicInfo};
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
use std::{f32, thread, u32};
|
||||
use std::{thread, u32};
|
||||
|
||||
use app_units::Au;
|
||||
use euclid::num::Zero;
|
||||
use layout_2020::flow::float::{
|
||||
ContainingBlockPositionInfo, FloatBand, FloatBandNode, FloatBandTree, FloatContext, FloatSide,
|
||||
|
@ -17,7 +19,7 @@ use layout_2020::flow::float::{
|
|||
use layout_2020::geom::{LogicalRect, LogicalVec2};
|
||||
use lazy_static::lazy_static;
|
||||
use quickcheck::{Arbitrary, Gen};
|
||||
use style::values::computed::{Clear, Length};
|
||||
use style::values::computed::Clear;
|
||||
|
||||
lazy_static! {
|
||||
static ref PANIC_HOOK_MUTEX: Mutex<()> = Mutex::new(());
|
||||
|
@ -57,13 +59,14 @@ struct FloatBandWrapper(FloatBand);
|
|||
|
||||
impl Arbitrary for FloatBandWrapper {
|
||||
fn arbitrary(generator: &mut Gen) -> FloatBandWrapper {
|
||||
let top: u32 = Arbitrary::arbitrary(generator);
|
||||
let left: Option<u32> = Arbitrary::arbitrary(generator);
|
||||
let right: Option<u32> = Arbitrary::arbitrary(generator);
|
||||
let top: u32 = u32::arbitrary(generator);
|
||||
let left: Option<u32> = Some(u32::arbitrary(generator));
|
||||
let right: Option<u32> = Some(u32::arbitrary(generator));
|
||||
|
||||
FloatBandWrapper(FloatBand {
|
||||
top: Length::new(top as f32),
|
||||
left: left.map(|value| Length::new(value as f32)),
|
||||
right: right.map(|value| Length::new(value as f32)),
|
||||
top: Au::from_f32_px(top as f32),
|
||||
left: left.map(|value| Au::from_f32_px(value as f32)),
|
||||
right: right.map(|value| Au::from_f32_px(value as f32)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +151,7 @@ fn check_tree_balance(tree: FloatBandTree) {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_tree_find(tree: &FloatBandTree, block_position: Length, sorted_bands: &[FloatBand]) {
|
||||
fn check_tree_find(tree: &FloatBandTree, block_position: Au, sorted_bands: &[FloatBand]) {
|
||||
let found_band = tree
|
||||
.find(block_position)
|
||||
.expect("Couldn't find the band in the tree!");
|
||||
|
@ -163,7 +166,7 @@ fn check_tree_find(tree: &FloatBandTree, block_position: Length, sorted_bands: &
|
|||
assert_eq!(found_band.right, reference_band.right);
|
||||
}
|
||||
|
||||
fn check_tree_find_next(tree: &FloatBandTree, block_position: Length, sorted_bands: &[FloatBand]) {
|
||||
fn check_tree_find_next(tree: &FloatBandTree, block_position: Au, sorted_bands: &[FloatBand]) {
|
||||
let found_band = tree
|
||||
.find_next(block_position)
|
||||
.expect("Couldn't find the band in the tree!");
|
||||
|
@ -179,9 +182,9 @@ fn check_tree_find_next(tree: &FloatBandTree, block_position: Length, sorted_ban
|
|||
|
||||
fn check_node_range_setting(
|
||||
node: &FloatBandNode,
|
||||
block_range: &Range<Length>,
|
||||
block_range: &Range<Au>,
|
||||
side: FloatSide,
|
||||
value: Length,
|
||||
value: Au,
|
||||
) {
|
||||
if node.band.top >= block_range.start && node.band.top < block_range.end {
|
||||
match side {
|
||||
|
@ -200,9 +203,9 @@ fn check_node_range_setting(
|
|||
|
||||
fn check_tree_range_setting(
|
||||
tree: &FloatBandTree,
|
||||
block_range: &Range<Length>,
|
||||
block_range: &Range<Au>,
|
||||
side: FloatSide,
|
||||
value: Length,
|
||||
value: Au,
|
||||
) {
|
||||
if let Some(ref root) = tree.root.0 {
|
||||
check_node_range_setting(root, block_range, side, value)
|
||||
|
@ -242,17 +245,17 @@ fn test_tree_balance() {
|
|||
// Tests that the `find()` method works.
|
||||
#[test]
|
||||
fn test_tree_find() {
|
||||
let f: fn(Vec<FloatBandWrapper>, Vec<u32>) = check;
|
||||
let f: fn(Vec<FloatBandWrapper>, Vec<u16>) = check;
|
||||
quickcheck::quickcheck(f);
|
||||
fn check(bands: Vec<FloatBandWrapper>, lookups: Vec<u32>) {
|
||||
fn check(bands: Vec<FloatBandWrapper>, lookups: Vec<u16>) {
|
||||
let mut bands: Vec<FloatBand> = bands.into_iter().map(|band| band.0).collect();
|
||||
bands.push(FloatBand {
|
||||
top: Length::zero(),
|
||||
top: Au::zero(),
|
||||
left: None,
|
||||
right: None,
|
||||
});
|
||||
bands.push(FloatBand {
|
||||
top: Length::new(f32::INFINITY),
|
||||
top: Au::from_f32_px(INFINITY),
|
||||
left: None,
|
||||
right: None,
|
||||
});
|
||||
|
@ -262,7 +265,7 @@ fn test_tree_find() {
|
|||
}
|
||||
bands.sort_by(|a, b| a.top.partial_cmp(&b.top).unwrap());
|
||||
for lookup in lookups {
|
||||
check_tree_find(&tree, Length::new(lookup as f32), &bands);
|
||||
check_tree_find(&tree, Au::from_f32_px(lookup as f32), &bands);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -270,17 +273,17 @@ fn test_tree_find() {
|
|||
// Tests that the `find_next()` method works.
|
||||
#[test]
|
||||
fn test_tree_find_next() {
|
||||
let f: fn(Vec<FloatBandWrapper>, Vec<u32>) = check;
|
||||
let f: fn(Vec<FloatBandWrapper>, Vec<u16>) = check;
|
||||
quickcheck::quickcheck(f);
|
||||
fn check(bands: Vec<FloatBandWrapper>, lookups: Vec<u32>) {
|
||||
fn check(bands: Vec<FloatBandWrapper>, lookups: Vec<u16>) {
|
||||
let mut bands: Vec<FloatBand> = bands.into_iter().map(|band| band.0).collect();
|
||||
bands.push(FloatBand {
|
||||
top: Length::zero(),
|
||||
top: Au::zero(),
|
||||
left: None,
|
||||
right: None,
|
||||
});
|
||||
bands.push(FloatBand {
|
||||
top: Length::new(f32::INFINITY),
|
||||
top: Au::from_f32_px(INFINITY),
|
||||
left: None,
|
||||
right: None,
|
||||
});
|
||||
|
@ -291,7 +294,7 @@ fn test_tree_find_next() {
|
|||
tree = tree.insert((*band).clone());
|
||||
}
|
||||
for lookup in lookups {
|
||||
check_tree_find_next(&tree, Length::new(lookup as f32), &bands);
|
||||
check_tree_find_next(&tree, Au::from_f32_px(lookup as f32), &bands);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -307,15 +310,15 @@ fn test_tree_range_setting() {
|
|||
tree = tree.insert((*band).clone());
|
||||
}
|
||||
|
||||
let mut tops: Vec<Length> = bands.iter().map(|band| band.0.top).collect();
|
||||
tops.push(Length::new(f32::INFINITY));
|
||||
tops.sort_by(|a, b| a.px().partial_cmp(&b.px()).unwrap());
|
||||
let mut tops: Vec<Au> = bands.iter().map(|band| band.0.top).collect();
|
||||
tops.push(Au::from_f32_px(INFINITY));
|
||||
tops.sort_by(|a, b| a.to_px().partial_cmp(&b.to_px()).unwrap());
|
||||
|
||||
for range in ranges {
|
||||
let start = range.start_index.min(tops.len() as u32 - 1);
|
||||
let end = (range.start_index as u64 + range.length as u64).min(tops.len() as u64 - 1);
|
||||
let block_range = tops[start as usize]..tops[end as usize];
|
||||
let length = Length::new(range.length as f32);
|
||||
let length = Au::from_px(range.length as i32);
|
||||
let new_tree = tree.set_range(&block_range, range.side, length);
|
||||
check_tree_range_setting(&new_tree, &block_range, range.side, length);
|
||||
}
|
||||
|
@ -330,7 +333,7 @@ struct FloatInput {
|
|||
info: PlacementInfo,
|
||||
// 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.
|
||||
ceiling: u32,
|
||||
ceiling: Au,
|
||||
/// Containing block positioning information, which is used to track the current offsets
|
||||
/// from the float containing block formatting context to the current containing block.
|
||||
containing_block_info: ContainingBlockPositionInfo,
|
||||
|
@ -353,8 +356,8 @@ impl Arbitrary for FloatInput {
|
|||
FloatInput {
|
||||
info: PlacementInfo {
|
||||
size: LogicalVec2 {
|
||||
inline: Length::new(width as f32),
|
||||
block: Length::new(height as f32),
|
||||
inline: Au::from_f32_px(width as f32),
|
||||
block: Au::from_f32_px(height as f32),
|
||||
},
|
||||
side: if is_left {
|
||||
FloatSide::Left
|
||||
|
@ -363,10 +366,10 @@ impl Arbitrary for FloatInput {
|
|||
},
|
||||
clear: new_clear(clear),
|
||||
},
|
||||
ceiling,
|
||||
ceiling: Au::from_f32_px(ceiling as f32),
|
||||
containing_block_info: ContainingBlockPositionInfo::new_with_inline_offsets(
|
||||
Length::new(left as f32),
|
||||
Length::new(left as f32 + containing_block_width as f32),
|
||||
Au::from_f32_px(left as f32),
|
||||
Au::from_f32_px(left as f32 + containing_block_width as f32),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -374,28 +377,40 @@ impl Arbitrary for FloatInput {
|
|||
fn shrink(&self) -> Box<dyn Iterator<Item = FloatInput>> {
|
||||
let mut this = (*self).clone();
|
||||
let mut shrunk = false;
|
||||
if let Some(inline_size) = self.info.size.inline.px().shrink().next() {
|
||||
this.info.size.inline = Length::new(inline_size);
|
||||
if let Some(inline_size) = self.info.size.inline.to_px().shrink().next() {
|
||||
this.info.size.inline = Au::from_px(inline_size);
|
||||
shrunk = true;
|
||||
}
|
||||
if let Some(block_size) = self.info.size.block.px().shrink().next() {
|
||||
this.info.size.block = Length::new(block_size);
|
||||
if let Some(block_size) = self.info.size.block.to_px().shrink().next() {
|
||||
this.info.size.block = Au::from_px(block_size);
|
||||
shrunk = true;
|
||||
}
|
||||
if let Some(clear) = (self.info.clear as u8).shrink().next() {
|
||||
this.info.clear = new_clear(clear);
|
||||
shrunk = true;
|
||||
}
|
||||
if let Some(left) = self.containing_block_info.inline_start.px().shrink().next() {
|
||||
this.containing_block_info.inline_start = Length::new(left);
|
||||
if let Some(left) = self
|
||||
.containing_block_info
|
||||
.inline_start
|
||||
.to_px()
|
||||
.shrink()
|
||||
.next()
|
||||
{
|
||||
this.containing_block_info.inline_start = Au::from_px(left);
|
||||
shrunk = true;
|
||||
}
|
||||
if let Some(right) = self.containing_block_info.inline_end.px().shrink().next() {
|
||||
this.containing_block_info.inline_end = Length::new(right);
|
||||
if let Some(right) = self
|
||||
.containing_block_info
|
||||
.inline_end
|
||||
.to_px()
|
||||
.shrink()
|
||||
.next()
|
||||
{
|
||||
this.containing_block_info.inline_end = Au::from_px(right);
|
||||
shrunk = true;
|
||||
}
|
||||
if let Some(ceiling) = self.ceiling.shrink().next() {
|
||||
this.ceiling = ceiling;
|
||||
if let Some(ceiling) = self.ceiling.to_px().shrink().next() {
|
||||
this.ceiling = Au::from_px(ceiling);
|
||||
shrunk = true;
|
||||
}
|
||||
if shrunk {
|
||||
|
@ -424,9 +439,9 @@ struct FloatPlacement {
|
|||
// Information about the placement of a float.
|
||||
#[derive(Clone)]
|
||||
struct PlacedFloat {
|
||||
origin: LogicalVec2<Length>,
|
||||
origin: LogicalVec2<Au>,
|
||||
info: PlacementInfo,
|
||||
ceiling: Length,
|
||||
ceiling: Au,
|
||||
containing_block_info: ContainingBlockPositionInfo,
|
||||
}
|
||||
|
||||
|
@ -453,7 +468,7 @@ impl Drop for FloatPlacement {
|
|||
}
|
||||
|
||||
impl PlacedFloat {
|
||||
fn rect(&self) -> LogicalRect<Length> {
|
||||
fn rect(&self) -> LogicalRect<Au> {
|
||||
LogicalRect {
|
||||
start_corner: self.origin.clone(),
|
||||
size: self.info.size.clone(),
|
||||
|
@ -463,10 +478,10 @@ impl PlacedFloat {
|
|||
|
||||
impl FloatPlacement {
|
||||
fn place(floats: Vec<FloatInput>) -> FloatPlacement {
|
||||
let mut float_context = FloatContext::new(Length::new(f32::INFINITY));
|
||||
let mut float_context = FloatContext::new(Au::from_f32_px(INFINITY));
|
||||
let mut placed_floats = vec![];
|
||||
for float in floats {
|
||||
let ceiling = Length::new(float.ceiling as f32);
|
||||
let ceiling = float.ceiling;
|
||||
float_context.set_ceiling_from_non_floats(ceiling);
|
||||
float_context.containing_block_info = float.containing_block_info;
|
||||
placed_floats.push(PlacedFloat {
|
||||
|
@ -547,9 +562,9 @@ fn check_floats_rule_3(placement: &FloatPlacement) {
|
|||
// Where the top of `b` should probably be 32px per Rule 3, but unless this distinction
|
||||
// is made the top of `b` could legally be 0px.
|
||||
if this_float.origin.block >= other_float.rect().max_block_position() ||
|
||||
(this_float.info.size.block == Length::zero() &&
|
||||
(this_float.info.size.block == Au::zero() &&
|
||||
this_float.rect().max_block_position() < other_float.origin.block) ||
|
||||
(this_float.info.size.block > Length::zero() &&
|
||||
(this_float.info.size.block > Au::zero() &&
|
||||
this_float.rect().max_block_position() <= other_float.origin.block)
|
||||
{
|
||||
continue;
|
||||
|
@ -574,14 +589,14 @@ fn check_floats_rule_3(placement: &FloatPlacement) {
|
|||
// is defined by the rules in the section on margin collapsing.
|
||||
fn check_floats_rule_4(placement: &FloatPlacement) {
|
||||
for placed_float in &placement.placed_floats {
|
||||
assert!(placed_float.origin.block >= Length::zero());
|
||||
assert!(placed_float.origin.block >= Au::zero());
|
||||
}
|
||||
}
|
||||
|
||||
// 5. The outer top of a floating box may not be higher than the outer top of any block or floated
|
||||
// box generated by an element earlier in the source document.
|
||||
fn check_floats_rule_5(placement: &FloatPlacement) {
|
||||
let mut block_position = Length::zero();
|
||||
let mut block_position = Au::zero();
|
||||
for placed_float in &placement.placed_floats {
|
||||
assert!(placed_float.origin.block >= block_position);
|
||||
block_position = placed_float.origin.block;
|
||||
|
@ -644,7 +659,8 @@ fn check_floats_rule_8(floats_and_perturbations: Vec<(FloatInput, u32)>) {
|
|||
|
||||
let mut placement = placement.clone();
|
||||
placement.placed_floats[float_index].origin.block =
|
||||
placement.placed_floats[float_index].origin.block - Length::new(perturbation as f32);
|
||||
placement.placed_floats[float_index].origin.block -
|
||||
Au::from_f32_px(perturbation as f32);
|
||||
|
||||
let result = {
|
||||
let mutex_guard = PANIC_HOOK_MUTEX.lock().unwrap();
|
||||
|
@ -673,7 +689,7 @@ fn check_floats_rule_9(floats_and_perturbations: Vec<(FloatInput, u32)>) {
|
|||
let mut placement = placement.clone();
|
||||
{
|
||||
let placed_float = &mut placement.placed_floats[float_index];
|
||||
let perturbation = Length::new(perturbation as f32);
|
||||
let perturbation = Au::from_f32_px(perturbation as f32);
|
||||
match placed_float.info.side {
|
||||
FloatSide::Left => {
|
||||
placed_float.origin.inline = placed_float.origin.inline - perturbation
|
||||
|
@ -699,7 +715,7 @@ fn check_floats_rule_9(floats_and_perturbations: Vec<(FloatInput, u32)>) {
|
|||
// left-floating boxes (in the case of 'clear: left'), or all earlier right-floating boxes (in
|
||||
// the case of 'clear: right'), or both ('clear: both').
|
||||
fn check_floats_rule_10(placement: &FloatPlacement) {
|
||||
let mut block_position = Length::zero();
|
||||
let mut block_position = Au::zero();
|
||||
for placed_float in &placement.placed_floats {
|
||||
assert!(placed_float.origin.block >= block_position);
|
||||
block_position = placed_float.origin.block;
|
||||
|
@ -721,9 +737,9 @@ fn check_floats_rule_10(placement: &FloatPlacement) {
|
|||
// Where the top of `b` should probably be 32px per Rule 3, but unless this distinction
|
||||
// is made the top of `b` could legally be 0px.
|
||||
if this_float.origin.block >= other_float.rect().max_block_position() ||
|
||||
(this_float.info.size.block == Length::zero() &&
|
||||
(this_float.info.size.block == Au::zero() &&
|
||||
this_float.rect().max_block_position() < other_float.origin.block) ||
|
||||
(this_float.info.size.block > Length::zero() &&
|
||||
(this_float.info.size.block > Au::zero() &&
|
||||
this_float.rect().max_block_position() <= other_float.origin.block)
|
||||
{
|
||||
continue;
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[c414-flt-fit-004.xht]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[font-weight-applies-to-005.xht]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[flexbox_item-clear.html]
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[marquee-min-intrinsic-size.html]
|
||||
expected: FAIL
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue