From a2c2b294d544c022cdf4c4a871dc8342835c3438 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Dec 2019 14:39:38 +0100 Subject: [PATCH 01/19] Move `clamp_*` functions to methods of `Length` --- components/layout_2020/flow/mod.rs | 32 ++++++++-------------- components/style/values/computed/length.rs | 26 ++++++++++++++++-- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 7138ffe3eb0..f877e702dfe 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -411,7 +411,7 @@ fn layout_in_flow_non_replaced_block_level<'a>( // https://drafts.csswg.org/css2/visudet.html#min-max-heights let mut block_size = box_size.block; if let LengthOrAuto::LengthPercentage(ref mut block_size) = block_size { - *block_size = clamp_between_extremums(*block_size, min_box_size.block, max_box_size.block); + *block_size = block_size.clamp_between_extremums(min_box_size.block, max_box_size.block); } let containing_block_for_children = ContainingBlock { @@ -475,11 +475,9 @@ fn layout_in_flow_non_replaced_block_level<'a>( .collapsed_through; let relative_adjustement = relative_adjustement(style, inline_size, block_size); let block_size = block_size.auto_is(|| { - clamp_between_extremums( - flow_layout.content_block_size, - min_box_size.block, - max_box_size.block, - ) + flow_layout + .content_block_size + .clamp_between_extremums(min_box_size.block, max_box_size.block) }); let content_rect = Rect { start_corner: Vec2 { @@ -536,10 +534,10 @@ fn layout_in_flow_replaced_block_level<'a>( percent_resolved_box_size(style.min_box_size(), containing_block).auto_is(Length::zero); let max_box_size = percent_resolved_max_box_size(style.max_box_size(), containing_block); - let clamp = |inline_size, block_size| { + let clamp = |inline_size: Length, block_size: Length| { ( - clamp_between_extremums(inline_size, min_box_size.inline, max_box_size.inline), - clamp_between_extremums(block_size, min_box_size.block, max_box_size.block), + inline_size.clamp_between_extremums(min_box_size.inline, max_box_size.inline), + block_size.clamp_between_extremums(min_box_size.block, max_box_size.block), ) }; // https://drafts.csswg.org/css2/visudet.html#min-max-widths @@ -590,7 +588,7 @@ fn layout_in_flow_replaced_block_level<'a>( // Row 3. (Violation::Below(min_inline_size), Violation::None) => { let block_size = - clamp_below_max(min_inline_size / intrinsic_ratio, max_box_size.block); + (min_inline_size / intrinsic_ratio).clamp_below_max(max_box_size.block); (min_inline_size, block_size) }, // Row 4. @@ -601,7 +599,7 @@ fn layout_in_flow_replaced_block_level<'a>( // Row 5. (Violation::None, Violation::Below(min_block_size)) => { let inline_size = - clamp_below_max(min_block_size * intrinsic_ratio, max_box_size.inline); + (min_block_size * intrinsic_ratio).clamp_below_max(max_box_size.inline); (inline_size, min_block_size) }, // Rows 6-7. @@ -627,12 +625,12 @@ fn layout_in_flow_replaced_block_level<'a>( { // Row 8. let inline_size = - clamp_below_max(min_block_size * intrinsic_ratio, max_box_size.inline); + (min_block_size * intrinsic_ratio).clamp_below_max(max_box_size.inline); (inline_size, min_block_size) } else { // Row 9. let block_size = - clamp_below_max(min_inline_size / intrinsic_ratio, max_box_size.block); + (min_inline_size / intrinsic_ratio).clamp_below_max(max_box_size.block); (min_inline_size, block_size) } }, @@ -704,14 +702,6 @@ fn solve_inline_margins_for_in_flow_block_level( } } -fn clamp_between_extremums(size: Length, min_size: Length, max_size: Option) -> Length { - clamp_below_max(size, max_size).max(min_size) -} - -fn clamp_below_max(size: Length, max_size: Option) -> Length { - max_size.map_or(size, |max_size| size.min(max_size)) -} - fn percent_resolved_box_size( box_size: Vec2, containing_block: &ContainingBlock, diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 06631a354df..2423d447a6a 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -657,14 +657,14 @@ impl CSSPixelLength { /// Return the containing pixel value. #[inline] - pub fn px(&self) -> CSSFloat { + pub fn px(self) -> CSSFloat { self.0 } /// Return the length with app_unit i32 type. #[inline] - pub fn to_i32_au(&self) -> i32 { - Au::from(*self).0 + pub fn to_i32_au(self) -> i32 { + Au::from(self).0 } /// Return the absolute value of this length. @@ -692,9 +692,29 @@ impl CSSPixelLength { } /// Sets `self` to the maximum between `self` and `other`. + #[inline] pub fn max_assign(&mut self, other: Self) { *self = self.max(other); } + + /// Clamp the value to a lower bound and an optional upper bound. + /// + /// Can be used for example with `min-width` and `max-width`. + #[inline] + pub fn clamp_between_extremums(self, min_size: Self, max_size: Option) -> Self { + self.clamp_below_max(max_size).max(min_size) + } + + /// Clamp the value to an optional upper bound. + /// + /// Can be used for example with `max-width`. + #[inline] + pub fn clamp_below_max(self, max_size: Option) -> Self { + match max_size { + None => self, + Some(max_size) => self.min(max_size), + } + } } impl Zero for CSSPixelLength { From ce7e84be72554ae8d5423fe6fd199bd4009134c8 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Dec 2019 15:08:25 +0100 Subject: [PATCH 02/19] Replace `percent_resolved_*` functions with methods --- components/layout_2020/flow/mod.rs | 61 ++++++---------------- components/layout_2020/geom.rs | 35 +++++++++++++ components/style/values/generics/length.rs | 9 ++++ 3 files changed, 61 insertions(+), 44 deletions(-) diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index f877e702dfe..7321b602d52 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -21,8 +21,7 @@ use rayon_croissant::ParallelIteratorExt; use servo_arc::Arc; use style::computed_values::position::T as Position; use style::properties::ComputedValues; -use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto}; -use style::values::generics::length::MaxSize; +use style::values::computed::{Length, LengthOrAuto}; use style::Zero; mod construct; @@ -365,10 +364,14 @@ fn layout_in_flow_non_replaced_block_level<'a>( let pb = &padding + &border; let pb_inline_sum = pb.inline_sum(); - let box_size = percent_resolved_box_size(style.box_size(), containing_block); - let max_box_size = percent_resolved_max_box_size(style.max_box_size(), containing_block); - let min_box_size = - percent_resolved_box_size(style.min_box_size(), containing_block).auto_is(Length::zero); + let box_size = style.box_size().percentages_relative_to(containing_block); + let max_box_size = style + .max_box_size() + .percentages_relative_to(containing_block); + let min_box_size = style + .min_box_size() + .percentages_relative_to(containing_block) + .auto_is(Length::zero); // https://drafts.csswg.org/css2/visudet.html#min-max-widths let solve_inline_margins = |inline_size| { @@ -529,10 +532,14 @@ fn layout_in_flow_replaced_block_level<'a>( // FIXME(nox): This can divide by zero. let intrinsic_ratio = intrinsic_size.inline.px() / intrinsic_size.block.px(); - let box_size = percent_resolved_box_size(style.box_size(), containing_block); - let min_box_size = - percent_resolved_box_size(style.min_box_size(), containing_block).auto_is(Length::zero); - let max_box_size = percent_resolved_max_box_size(style.max_box_size(), containing_block); + let box_size = style.box_size().percentages_relative_to(containing_block); + let min_box_size = style + .min_box_size() + .percentages_relative_to(containing_block) + .auto_is(Length::zero); + let max_box_size = style + .max_box_size() + .percentages_relative_to(containing_block); let clamp = |inline_size: Length, block_size: Length| { ( @@ -701,37 +708,3 @@ fn solve_inline_margins_for_in_flow_block_level( (LengthOrAuto::LengthPercentage(start), _) => (start, inline_margins - start), } } - -fn percent_resolved_box_size( - box_size: Vec2, - containing_block: &ContainingBlock, -) -> Vec2 { - Vec2 { - inline: box_size - .inline - .percentage_relative_to(containing_block.inline_size), - block: box_size - .block - .maybe_percentage_relative_to(containing_block.block_size.non_auto()), - } -} - -fn percent_resolved_max_box_size( - max_box_size: Vec2>, - containing_block: &ContainingBlock, -) -> Vec2> { - Vec2 { - inline: match max_box_size.inline { - MaxSize::LengthPercentage(max_inline_size) => { - Some(max_inline_size.percentage_relative_to(containing_block.inline_size)) - }, - MaxSize::None => None, - }, - block: match max_box_size.block { - MaxSize::LengthPercentage(max_block_size) => { - max_block_size.maybe_percentage_relative_to(containing_block.block_size.non_auto()) - }, - MaxSize::None => None, - }, - } -} diff --git a/components/layout_2020/geom.rs b/components/layout_2020/geom.rs index 2eafb78051e..562fad5c4f6 100644 --- a/components/layout_2020/geom.rs +++ b/components/layout_2020/geom.rs @@ -2,11 +2,13 @@ * 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 crate::ContainingBlock; use std::fmt; use std::ops::{Add, AddAssign, Sub}; use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection}; use style::logical_geometry::{PhysicalCorner, WritingMode}; use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto}; +use style::values::generics::length::MaxSize; use style::Zero; use style_traits::CSSPixel; @@ -151,6 +153,39 @@ impl flow_relative::Vec2 { } } +impl flow_relative::Vec2 { + pub fn percentages_relative_to( + &self, + containing_block: &ContainingBlock, + ) -> flow_relative::Vec2 { + flow_relative::Vec2 { + inline: self + .inline + .percentage_relative_to(containing_block.inline_size), + block: self + .block + .maybe_percentage_relative_to(containing_block.block_size.non_auto()), + } + } +} + +impl flow_relative::Vec2> { + pub fn percentages_relative_to( + &self, + containing_block: &ContainingBlock, + ) -> flow_relative::Vec2> { + flow_relative::Vec2 { + inline: self + .inline + .to_option() + .map(|lp| lp.percentage_relative_to(containing_block.inline_size)), + block: self.block.to_option().and_then(|olp| { + olp.maybe_percentage_relative_to(containing_block.block_size.non_auto()) + }), + } + } +} + impl flow_relative::Rect { pub fn zero() -> Self { Self { diff --git a/components/style/values/generics/length.rs b/components/style/values/generics/length.rs index 4183f40a942..b2f34058283 100644 --- a/components/style/values/generics/length.rs +++ b/components/style/values/generics/length.rs @@ -207,6 +207,15 @@ impl MaxSize { pub fn none() -> Self { MaxSize::None } + + /// Convert + #[cfg(not(feature = "gecko"))] + pub fn to_option(self) -> Option { + match self { + Self::LengthPercentage(lp) => Some(lp), + Self::None => None, + } + } } /// A generic `` | `` value for the `-moz-tab-size` property. From 999dd72895319ea8343d8799aec358b717fcbaa1 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Dec 2019 15:25:55 +0100 Subject: [PATCH 03/19] Account for min/max-width in outer intrinsic sizing --- components/layout_2020/sizing.rs | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index 75c13b1dcb5..e3c74d3733f 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -107,17 +107,37 @@ impl BoxContentSizes { &self, style: &ComputedValues, ) -> (ContentSizes, Percentage) { - // FIXME: account for 'min-width', 'max-width', 'box-sizing' - + // FIXME: account for 'box-sizing' let inline_size = style.box_size().inline; + let min_inline_size = style + .min_box_size() + .inline + .percentage_relative_to(Length::zero()) + .auto_is(Length::zero); + let max_inline_size = style + .max_box_size() + .inline + .to_option() + .and_then(|lp| lp.as_length()); + let clamp = |l: Length| l.clamp_between_extremums(min_inline_size, max_inline_size); + // Percentages for 'width' are treated as 'auto' let inline_size = inline_size.map(|lp| lp.as_length()); // The (inner) min/max-content are only used for 'auto' let mut outer = match inline_size.non_auto().flatten() { - None => self.expect_inline().clone(), - Some(length) => ContentSizes { - min_content: length, - max_content: length, + None => { + let inner = self.expect_inline().clone(); + ContentSizes { + min_content: clamp(inner.min_content), + max_content: clamp(inner.max_content), + } + }, + Some(length) => { + let length = clamp(length); + ContentSizes { + min_content: length, + max_content: length, + } }, }; From bf9698826008d4d3a4cb3854d71597e889587754 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Dec 2019 15:44:42 +0100 Subject: [PATCH 04/19] Add min/max-width/height support for `inline-block` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (… and other non-replaced atomic inline-level boxes.) --- components/layout_2020/flow/inline.rs | 38 ++++++++++++++++--- components/layout_2020/formatting_contexts.rs | 2 + 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 0cacfb794a7..96c933f4596 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -482,10 +482,29 @@ fn layout_atomic<'box_tree>( }, Err(non_replaced) => { let box_size = atomic.style.box_size(); - let inline_size = box_size.inline.percentage_relative_to(cbis).auto_is(|| { - let available_size = cbis - pbm.inline_sum(); - atomic.content_sizes.shrink_to_fit(available_size) - }); + let max_box_size = atomic + .style + .max_box_size() + .percentages_relative_to(ifc.containing_block); + let min_box_size = atomic + .style + .min_box_size() + .percentages_relative_to(ifc.containing_block) + .auto_is(Length::zero); + + // https://drafts.csswg.org/css2/visudet.html#inlineblock-width + let tentative_inline_size = + box_size.inline.percentage_relative_to(cbis).auto_is(|| { + let available_size = cbis - pbm.inline_sum(); + atomic.content_sizes.shrink_to_fit(available_size) + }); + + // https://drafts.csswg.org/css2/visudet.html#min-max-widths + // In this case “applying the rules above again” with a non-auto inline-size + // always results in that size. + let inline_size = tentative_inline_size + .clamp_between_extremums(min_box_size.inline, max_box_size.inline); + let block_size = box_size .block .maybe_percentage_relative_to(ifc.containing_block.block_size.non_auto()); @@ -508,7 +527,16 @@ fn layout_atomic<'box_tree>( dummy_tree_rank, ifc.absolutely_positioned_fragments, ); - let block_size = block_size.auto_is(|| independent_layout.content_block_size); + + // https://drafts.csswg.org/css2/visudet.html#block-root-margin + let tentative_block_size = block_size.auto_is(|| independent_layout.content_block_size); + + // https://drafts.csswg.org/css2/visudet.html#min-max-heights + // In this case “applying the rules above again” with a non-auto block-size + // always results in that size. + let block_size = tentative_block_size + .clamp_between_extremums(min_box_size.block, max_box_size.block); + let content_rect = Rect { start_corner, size: Vec2 { diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index d7477182437..f741e44affd 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -29,6 +29,8 @@ pub(crate) struct IndependentFormattingContext { pub(crate) struct IndependentLayout { pub fragments: Vec, + + /// https://drafts.csswg.org/css2/visudet.html#root-height pub content_block_size: Length, } From c40583b64480acb4b4b7ec2ac8837a040827e006 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Dec 2019 16:22:31 +0100 Subject: [PATCH 05/19] Move replaced box used size computation to a method of `ReplacedContents` --- components/layout_2020/flow/mod.rs | 138 +-------------------------- components/layout_2020/replaced.rs | 148 ++++++++++++++++++++++++++++- 2 files changed, 152 insertions(+), 134 deletions(-) diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 7321b602d52..0e0c08cc168 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -521,144 +521,20 @@ fn layout_in_flow_replaced_block_level<'a>( style: &Arc, replaced: &ReplacedContent, ) -> BoxFragment { + let size = replaced.used_size(containing_block, style); + let cbis = containing_block.inline_size; let padding = style.padding().percentages_relative_to(cbis); let border = style.border_width(); let computed_margin = style.margin().percentages_relative_to(cbis); let pb = &padding + &border; - let mode = style.writing_mode; - // FIXME(nox): We shouldn't pretend we always have a fully known intrinsic size. - let intrinsic_size = replaced.intrinsic_size.size_to_flow_relative(mode); - // FIXME(nox): This can divide by zero. - let intrinsic_ratio = intrinsic_size.inline.px() / intrinsic_size.block.px(); - - let box_size = style.box_size().percentages_relative_to(containing_block); - let min_box_size = style - .min_box_size() - .percentages_relative_to(containing_block) - .auto_is(Length::zero); - let max_box_size = style - .max_box_size() - .percentages_relative_to(containing_block); - - let clamp = |inline_size: Length, block_size: Length| { - ( - inline_size.clamp_between_extremums(min_box_size.inline, max_box_size.inline), - block_size.clamp_between_extremums(min_box_size.block, max_box_size.block), - ) - }; - // https://drafts.csswg.org/css2/visudet.html#min-max-widths - // https://drafts.csswg.org/css2/visudet.html#min-max-heights - let (inline_size, block_size) = match (box_size.inline, box_size.block) { - (LengthOrAuto::LengthPercentage(inline), LengthOrAuto::LengthPercentage(block)) => { - clamp(inline, block) - }, - (LengthOrAuto::LengthPercentage(inline), LengthOrAuto::Auto) => { - clamp(inline, inline / intrinsic_ratio) - }, - (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(block)) => { - clamp(block * intrinsic_ratio, block) - }, - (LengthOrAuto::Auto, LengthOrAuto::Auto) => { - enum Violation { - None, - Below(Length), - Above(Length), - } - let violation = |size, min_size, mut max_size: Option| { - if let Some(max) = max_size.as_mut() { - max.max_assign(min_size); - } - if size < min_size { - return Violation::Below(min_size); - } - match max_size { - Some(max_size) if size > max_size => Violation::Above(max_size), - _ => Violation::None, - } - }; - match ( - violation( - intrinsic_size.inline, - min_box_size.inline, - max_box_size.inline, - ), - violation(intrinsic_size.block, min_box_size.block, max_box_size.block), - ) { - // Row 1. - (Violation::None, Violation::None) => (intrinsic_size.inline, intrinsic_size.block), - // Row 2. - (Violation::Above(max_inline_size), Violation::None) => { - let block_size = (max_inline_size / intrinsic_ratio).max(min_box_size.block); - (max_inline_size, block_size) - }, - // Row 3. - (Violation::Below(min_inline_size), Violation::None) => { - let block_size = - (min_inline_size / intrinsic_ratio).clamp_below_max(max_box_size.block); - (min_inline_size, block_size) - }, - // Row 4. - (Violation::None, Violation::Above(max_block_size)) => { - let inline_size = (max_block_size * intrinsic_ratio).max(min_box_size.inline); - (inline_size, max_block_size) - }, - // Row 5. - (Violation::None, Violation::Below(min_block_size)) => { - let inline_size = - (min_block_size * intrinsic_ratio).clamp_below_max(max_box_size.inline); - (inline_size, min_block_size) - }, - // Rows 6-7. - (Violation::Above(max_inline_size), Violation::Above(max_block_size)) => { - if max_inline_size.px() / intrinsic_size.inline.px() <= - max_block_size.px() / intrinsic_size.block.px() - { - // Row 6. - let block_size = - (max_inline_size / intrinsic_ratio).max(min_box_size.block); - (max_inline_size, block_size) - } else { - // Row 7. - let inline_size = - (max_block_size * intrinsic_ratio).max(min_box_size.inline); - (inline_size, max_block_size) - } - }, - // Rows 8-9. - (Violation::Below(min_inline_size), Violation::Below(min_block_size)) => { - if min_inline_size.px() / intrinsic_size.inline.px() <= - min_block_size.px() / intrinsic_size.block.px() - { - // Row 8. - let inline_size = - (min_block_size * intrinsic_ratio).clamp_below_max(max_box_size.inline); - (inline_size, min_block_size) - } else { - // Row 9. - let block_size = - (min_inline_size / intrinsic_ratio).clamp_below_max(max_box_size.block); - (min_inline_size, block_size) - } - }, - // Row 10. - (Violation::Below(min_inline_size), Violation::Above(max_block_size)) => { - (min_inline_size, max_block_size) - }, - // Row 11. - (Violation::Above(max_inline_size), Violation::Below(min_block_size)) => { - (max_inline_size, min_block_size) - }, - } - }, - }; let (margin_inline_start, margin_inline_end) = solve_inline_margins_for_in_flow_block_level( containing_block, pb.inline_sum(), computed_margin.inline_start, computed_margin.inline_end, - inline_size, + size.inline, ); let margin = Sides { inline_start: margin_inline_start, @@ -666,15 +542,11 @@ fn layout_in_flow_replaced_block_level<'a>( block_start: computed_margin.block_start.auto_is(Length::zero), block_end: computed_margin.block_end.auto_is(Length::zero), }; - let size = Vec2 { - block: block_size, - inline: inline_size, - }; let fragments = replaced.make_fragments(style, size.clone()); let relative_adjustement = relative_adjustement( style, - inline_size, - LengthOrAuto::LengthPercentage(block_size), + size.inline, + LengthOrAuto::LengthPercentage(size.block), ); let content_rect = Rect { start_corner: Vec2 { diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 8a28cbf763c..b59df5e9b68 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -5,11 +5,14 @@ use crate::dom_traversal::NodeExt; use crate::fragments::{Fragment, ImageFragment}; use crate::geom::{flow_relative, physical}; +use crate::style_ext::ComputedValuesExt; +use crate::ContainingBlock; use net_traits::image::base::Image; use servo_arc::Arc as ServoArc; use std::sync::Arc; use style::properties::ComputedValues; -use style::values::computed::Length; +use style::values::computed::{Length, LengthOrAuto}; +use style::Zero; #[derive(Debug)] pub(crate) struct ReplacedContent { @@ -56,4 +59,147 @@ impl ReplacedContent { .collect(), } } + + // https://drafts.csswg.org/css2/visudet.html#inline-replaced-width + // https://drafts.csswg.org/css2/visudet.html#inline-replaced-height + pub fn used_size( + &self, + containing_block: &ContainingBlock, + style: &ComputedValues, + ) -> flow_relative::Vec2 { + let mode = style.writing_mode; + // FIXME(nox): We shouldn't pretend we always have a fully known intrinsic size. + let intrinsic_size = self.intrinsic_size.size_to_flow_relative(mode); + // FIXME(nox): This can divide by zero. + let intrinsic_ratio = intrinsic_size.inline.px() / intrinsic_size.block.px(); + + let box_size = style.box_size().percentages_relative_to(containing_block); + let min_box_size = style + .min_box_size() + .percentages_relative_to(containing_block) + .auto_is(Length::zero); + let max_box_size = style + .max_box_size() + .percentages_relative_to(containing_block); + + let clamp = |inline_size: Length, block_size: Length| { + ( + inline_size.clamp_between_extremums(min_box_size.inline, max_box_size.inline), + block_size.clamp_between_extremums(min_box_size.block, max_box_size.block), + ) + }; + // https://drafts.csswg.org/css2/visudet.html#min-max-widths + // https://drafts.csswg.org/css2/visudet.html#min-max-heights + let (inline_size, block_size) = match (box_size.inline, box_size.block) { + (LengthOrAuto::LengthPercentage(inline), LengthOrAuto::LengthPercentage(block)) => { + clamp(inline, block) + }, + (LengthOrAuto::LengthPercentage(inline), LengthOrAuto::Auto) => { + clamp(inline, inline / intrinsic_ratio) + }, + (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(block)) => { + clamp(block * intrinsic_ratio, block) + }, + (LengthOrAuto::Auto, LengthOrAuto::Auto) => { + enum Violation { + None, + Below(Length), + Above(Length), + } + let violation = |size, min_size, mut max_size: Option| { + if let Some(max) = max_size.as_mut() { + max.max_assign(min_size); + } + if size < min_size { + return Violation::Below(min_size); + } + match max_size { + Some(max_size) if size > max_size => Violation::Above(max_size), + _ => Violation::None, + } + }; + match ( + violation( + intrinsic_size.inline, + min_box_size.inline, + max_box_size.inline, + ), + violation(intrinsic_size.block, min_box_size.block, max_box_size.block), + ) { + // Row 1. + (Violation::None, Violation::None) => { + (intrinsic_size.inline, intrinsic_size.block) + }, + // Row 2. + (Violation::Above(max_inline_size), Violation::None) => { + let block_size = + (max_inline_size / intrinsic_ratio).max(min_box_size.block); + (max_inline_size, block_size) + }, + // Row 3. + (Violation::Below(min_inline_size), Violation::None) => { + let block_size = + (min_inline_size / intrinsic_ratio).clamp_below_max(max_box_size.block); + (min_inline_size, block_size) + }, + // Row 4. + (Violation::None, Violation::Above(max_block_size)) => { + let inline_size = + (max_block_size * intrinsic_ratio).max(min_box_size.inline); + (inline_size, max_block_size) + }, + // Row 5. + (Violation::None, Violation::Below(min_block_size)) => { + let inline_size = + (min_block_size * intrinsic_ratio).clamp_below_max(max_box_size.inline); + (inline_size, min_block_size) + }, + // Rows 6-7. + (Violation::Above(max_inline_size), Violation::Above(max_block_size)) => { + if max_inline_size.px() / intrinsic_size.inline.px() <= + max_block_size.px() / intrinsic_size.block.px() + { + // Row 6. + let block_size = + (max_inline_size / intrinsic_ratio).max(min_box_size.block); + (max_inline_size, block_size) + } else { + // Row 7. + let inline_size = + (max_block_size * intrinsic_ratio).max(min_box_size.inline); + (inline_size, max_block_size) + } + }, + // Rows 8-9. + (Violation::Below(min_inline_size), Violation::Below(min_block_size)) => { + if min_inline_size.px() / intrinsic_size.inline.px() <= + min_block_size.px() / intrinsic_size.block.px() + { + // Row 8. + let inline_size = (min_block_size * intrinsic_ratio) + .clamp_below_max(max_box_size.inline); + (inline_size, min_block_size) + } else { + // Row 9. + let block_size = (min_inline_size / intrinsic_ratio) + .clamp_below_max(max_box_size.block); + (min_inline_size, block_size) + } + }, + // Row 10. + (Violation::Below(min_inline_size), Violation::Above(max_block_size)) => { + (min_inline_size, max_block_size) + }, + // Row 11. + (Violation::Above(max_inline_size), Violation::Below(min_block_size)) => { + (max_inline_size, min_block_size) + }, + } + }, + }; + flow_relative::Vec2 { + inline: inline_size, + block: block_size, + } + } } From b73eb49a580cdf3eaf0cc2153524e49165ed0fc5 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Dec 2019 16:24:09 +0100 Subject: [PATCH 06/19] Add sizing of inline replaced boxes --- components/layout_2020/flow/inline.rs | 4 +--- .../css/CSS2/box-display/containing-block-008.xht.ini | 2 ++ .../css/CSS2/box-display/containing-block-009.xht.ini | 2 ++ .../css/CSS2/box-display/containing-block-010.xht.ini | 2 ++ .../css/CSS2/box-display/containing-block-027.xht.ini | 2 ++ .../css/CSS2/box-display/containing-block-028.xht.ini | 2 ++ .../css/CSS2/box-display/display-change-001.xht.ini | 2 -- 7 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-008.xht.ini create mode 100644 tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-009.xht.ini create mode 100644 tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-010.xht.ini create mode 100644 tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-027.xht.ini create mode 100644 tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-028.xht.ini delete mode 100644 tests/wpt/metadata-layout-2020/css/CSS2/box-display/display-change-001.xht.ini diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 96c933f4596..61c99fc0b7b 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -465,9 +465,7 @@ fn layout_atomic<'box_tree>( let fragment = match atomic.as_replaced() { Ok(replaced) => { - // FIXME: implement https://drafts.csswg.org/css2/visudet.html#inline-replaced-width - // and https://drafts.csswg.org/css2/visudet.html#inline-replaced-height - let size = Vec2::zero(); + let size = replaced.used_size(ifc.containing_block, &atomic.style); let fragments = replaced.make_fragments(&atomic.style, size.clone()); let content_rect = Rect { start_corner, size }; BoxFragment { diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-008.xht.ini b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-008.xht.ini new file mode 100644 index 00000000000..dd630be0f54 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-008.xht.ini @@ -0,0 +1,2 @@ +[containing-block-008.xht] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-009.xht.ini b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-009.xht.ini new file mode 100644 index 00000000000..4d8ca58286c --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-009.xht.ini @@ -0,0 +1,2 @@ +[containing-block-009.xht] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-010.xht.ini b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-010.xht.ini new file mode 100644 index 00000000000..af40eef0a5d --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-010.xht.ini @@ -0,0 +1,2 @@ +[containing-block-010.xht] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-027.xht.ini b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-027.xht.ini new file mode 100644 index 00000000000..3d51008fd8a --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-027.xht.ini @@ -0,0 +1,2 @@ +[containing-block-027.xht] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-028.xht.ini b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-028.xht.ini new file mode 100644 index 00000000000..51d02b1875c --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-028.xht.ini @@ -0,0 +1,2 @@ +[containing-block-028.xht] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/display-change-001.xht.ini b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/display-change-001.xht.ini deleted file mode 100644 index 6b39c7c47cb..00000000000 --- a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/display-change-001.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[display-change-001.xht] - expected: FAIL From 8996be3c5efa4ef93fd0af332a05bbcbb4905810 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Dec 2019 21:41:26 +0100 Subject: [PATCH 07/19] =?UTF-8?q?Don=E2=80=99t=20assume=20replaced=20eleme?= =?UTF-8?q?nts=20have=20an=20intrinsic=20size?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/layout_2020/dom_traversal.rs | 18 +- components/layout_2020/replaced.rs | 238 +++++++++++++++++------- 2 files changed, 180 insertions(+), 76 deletions(-) diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs index 056103d7205..56ee72d67ca 100644 --- a/components/layout_2020/dom_traversal.rs +++ b/components/layout_2020/dom_traversal.rs @@ -17,7 +17,6 @@ use std::sync::Arc; use style::dom::TNode; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; -use style::values::computed::Length; #[derive(Clone, Copy)] pub enum WhichPseudoElement { @@ -299,7 +298,10 @@ impl Drop for BoxSlot<'_> { pub(crate) trait NodeExt<'dom>: 'dom + Copy + LayoutNode + Send + Sync { fn is_element(self) -> bool; fn as_text(self) -> Option; - fn as_image(self) -> Option<(Option>, Vec2)>; + + /// Returns the image if it’s loaded, and its size in image pixels + /// adjusted for `image_density`. + fn as_image(self) -> Option<(Option>, Vec2)>; fn first_child(self) -> Option; fn next_sibling(self) -> Option; fn parent_node(self) -> Option; @@ -328,7 +330,7 @@ where } } - fn as_image(self) -> Option<(Option>, Vec2)> { + fn as_image(self) -> Option<(Option>, Vec2)> { let node = self.to_threadsafe(); let (resource, metadata) = node.image_data()?; let (width, height) = resource @@ -336,14 +338,14 @@ where .map(|image| (image.width, image.height)) .or_else(|| metadata.map(|metadata| (metadata.width, metadata.height))) .unwrap_or((0, 0)); - let (mut width, mut height) = (width as f32, height as f32); + let (mut width, mut height) = (width as f64, height as f64); if let Some(density) = node.image_density().filter(|density| *density != 1.) { - width = (width as f64 / density) as f32; - height = (height as f64 / density) as f32; + width = width / density; + height = height / density; } let size = Vec2 { - x: Length::new(width), - y: Length::new(height), + x: width, + y: height, }; Some((resource, size)) } diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index b59df5e9b68..00242f61736 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -4,7 +4,8 @@ use crate::dom_traversal::NodeExt; use crate::fragments::{Fragment, ImageFragment}; -use crate::geom::{flow_relative, physical}; +use crate::geom::flow_relative::{Rect, Vec2}; +use crate::geom::physical; use crate::style_ext::ComputedValuesExt; use crate::ContainingBlock; use net_traits::image::base::Image; @@ -12,12 +13,27 @@ use servo_arc::Arc as ServoArc; use std::sync::Arc; use style::properties::ComputedValues; use style::values::computed::{Length, LengthOrAuto}; +use style::values::CSSFloat; use style::Zero; #[derive(Debug)] pub(crate) struct ReplacedContent { pub kind: ReplacedContentKind, - pub intrinsic_size: physical::Vec2, + + /// * Raster images always have an instrinsic width and height, with 1 image pixel = 1px. + /// The intrinsic ratio should be based on dividing those. + /// See https://github.com/w3c/csswg-drafts/issues/4572 for the case where either is zero. + /// PNG specifically disallows this but I (SimonSapin) am not sure about other formats. + /// + /// * Form controls have both intrinsic width and height **but no intrinsic ratio**. + /// See https://github.com/w3c/csswg-drafts/issues/1044 and + /// https://drafts.csswg.org/css-images/#intrinsic-dimensions “In general, […]” + /// + /// * For SVG, see https://svgwg.org/svg2-draft/coords.html#SizingSVGInCSS + /// and again https://github.com/w3c/csswg-drafts/issues/4572. + intrinsic_width: Option, + intrinsic_height: Option, + intrinsic_ratio: Option, } #[derive(Debug)] @@ -27,10 +43,21 @@ pub(crate) enum ReplacedContentKind { impl ReplacedContent { pub fn for_element<'dom>(element: impl NodeExt<'dom>) -> Option { - if let Some((image, intrinsic_size)) = element.as_image() { + if let Some((image, intrinsic_size_in_dots)) = element.as_image() { + // FIXME: should 'image-resolution' (when implemented) be used *instead* of + // `script::dom::htmlimageelement::ImageRequest::current_pixel_density`? + + // https://drafts.csswg.org/css-images-4/#the-image-resolution + let dppx = 1.0; + + let width = (intrinsic_size_in_dots.x as CSSFloat) / dppx; + let height = (intrinsic_size_in_dots.y as CSSFloat) / dppx; return Some(Self { kind: ReplacedContentKind::Image(image), - intrinsic_size, + intrinsic_width: Some(Length::new(width)), + intrinsic_height: Some(Length::new(height)), + // FIXME https://github.com/w3c/csswg-drafts/issues/4572 + intrinsic_ratio: Some(width / height), }); } None @@ -39,7 +66,7 @@ impl ReplacedContent { pub fn make_fragments<'a>( &'a self, style: &ServoArc, - size: flow_relative::Vec2, + size: Vec2, ) -> Vec { match &self.kind { ReplacedContentKind::Image(image) => image @@ -48,8 +75,8 @@ impl ReplacedContent { .map(|image_key| { Fragment::Image(ImageFragment { style: style.clone(), - rect: flow_relative::Rect { - start_corner: flow_relative::Vec2::zero(), + rect: Rect { + start_corner: Vec2::zero(), size, }, image_key, @@ -66,12 +93,21 @@ impl ReplacedContent { &self, containing_block: &ContainingBlock, style: &ComputedValues, - ) -> flow_relative::Vec2 { + ) -> Vec2 { let mode = style.writing_mode; - // FIXME(nox): We shouldn't pretend we always have a fully known intrinsic size. - let intrinsic_size = self.intrinsic_size.size_to_flow_relative(mode); - // FIXME(nox): This can divide by zero. - let intrinsic_ratio = intrinsic_size.inline.px() / intrinsic_size.block.px(); + let intrinsic_size = physical::Vec2 { + x: self.intrinsic_width, + y: self.intrinsic_height, + }; + let intrinsic_size = intrinsic_size.size_to_flow_relative(mode); + let intrinsic_ratio = self.intrinsic_ratio.map(|width_over_height| { + // inline-size over block-size + if style.writing_mode.is_vertical() { + 1. / width_over_height + } else { + width_over_height + } + }); let box_size = style.box_size().percentages_relative_to(containing_block); let min_box_size = style @@ -82,25 +118,94 @@ impl ReplacedContent { .max_box_size() .percentages_relative_to(containing_block); - let clamp = |inline_size: Length, block_size: Length| { - ( - inline_size.clamp_between_extremums(min_box_size.inline, max_box_size.inline), - block_size.clamp_between_extremums(min_box_size.block, max_box_size.block), - ) + let default_object_size = || { + // FIXME: + // “If 300px is too wide to fit the device, UAs should use the width of + // the largest rectangle that has a 2:1 ratio and fits the device instead.” + // “height of the largest rectangle that has a 2:1 ratio, has a height not greater + // than 150px, and has a width not greater than the device width.” + physical::Vec2 { + x: Length::new(300.), + y: Length::new(150.), + } + .size_to_flow_relative(mode) + }; + let clamp = |inline_size: Length, block_size: Length| Vec2 { + inline: inline_size.clamp_between_extremums(min_box_size.inline, max_box_size.inline), + block: block_size.clamp_between_extremums(min_box_size.block, max_box_size.block), }; // https://drafts.csswg.org/css2/visudet.html#min-max-widths // https://drafts.csswg.org/css2/visudet.html#min-max-heights - let (inline_size, block_size) = match (box_size.inline, box_size.block) { + match (box_size.inline, box_size.block) { (LengthOrAuto::LengthPercentage(inline), LengthOrAuto::LengthPercentage(block)) => { clamp(inline, block) }, (LengthOrAuto::LengthPercentage(inline), LengthOrAuto::Auto) => { - clamp(inline, inline / intrinsic_ratio) + let block = if let Some(i_over_b) = intrinsic_ratio { + inline / i_over_b + } else if let Some(block) = intrinsic_size.block { + block + } else { + default_object_size().block + }; + clamp(inline, block) }, (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(block)) => { - clamp(block * intrinsic_ratio, block) + let inline = if let Some(i_over_b) = intrinsic_ratio { + block * i_over_b + } else if let Some(inline) = intrinsic_size.inline { + inline + } else { + default_object_size().inline + }; + clamp(inline, block) }, (LengthOrAuto::Auto, LengthOrAuto::Auto) => { + let inline_size = + match (intrinsic_size.inline, intrinsic_size.block, intrinsic_ratio) { + (Some(inline), _, _) => inline, + (None, Some(block), Some(i_over_b)) => { + // “used height” in CSS 2 is always gonna be the intrinsic one, + // since it is available. + block * i_over_b + }, + // FIXME + // + // “If 'height' and 'width' both have computed values of 'auto' + // and the element has an intrinsic ratio but no intrinsic height or width, + // […]” + // + // In this `match` expression this would be an additional arm here: + // + // ``` + // (Vec2 { inline: None, block: None }, Some(_)) => {…} + // ``` + // + // “[…] then the used value of 'width' is undefined in CSS 2. + // However, it is suggested that, if the containing block's width + // does not itself depend on the replaced element's width, + // then the used value of 'width' is calculated from the constraint + // equation used for block-level, non-replaced elements in normal flow.” + _ => default_object_size().inline, + }; + let block_size = if let Some(block) = intrinsic_size.block { + block + } else if let Some(i_over_b) = intrinsic_ratio { + // “used width” in CSS 2 is what we just computed above + inline_size / i_over_b + } else { + default_object_size().block + }; + + let i_over_b = if let Some(i_over_b) = intrinsic_ratio { + i_over_b + } else { + return clamp(inline_size, block_size); + }; + + // https://drafts.csswg.org/css2/visudet.html#min-max-widths + // “However, for replaced elements with an intrinsic ratio and both + // 'width' and 'height' specified as 'auto', the algorithm is as follows” enum Violation { None, Below(Length), @@ -119,87 +224,84 @@ impl ReplacedContent { } }; match ( - violation( - intrinsic_size.inline, - min_box_size.inline, - max_box_size.inline, - ), - violation(intrinsic_size.block, min_box_size.block, max_box_size.block), + violation(inline_size, min_box_size.inline, max_box_size.inline), + violation(block_size, min_box_size.block, max_box_size.block), ) { // Row 1. - (Violation::None, Violation::None) => { - (intrinsic_size.inline, intrinsic_size.block) + (Violation::None, Violation::None) => Vec2 { + inline: inline_size, + block: block_size, }, // Row 2. - (Violation::Above(max_inline_size), Violation::None) => { - let block_size = - (max_inline_size / intrinsic_ratio).max(min_box_size.block); - (max_inline_size, block_size) + (Violation::Above(max_inline_size), Violation::None) => Vec2 { + inline: max_inline_size, + block: (max_inline_size / i_over_b).max(min_box_size.block), }, // Row 3. - (Violation::Below(min_inline_size), Violation::None) => { - let block_size = - (min_inline_size / intrinsic_ratio).clamp_below_max(max_box_size.block); - (min_inline_size, block_size) + (Violation::Below(min_inline_size), Violation::None) => Vec2 { + inline: min_inline_size, + block: (min_inline_size / i_over_b).clamp_below_max(max_box_size.block), }, // Row 4. - (Violation::None, Violation::Above(max_block_size)) => { - let inline_size = - (max_block_size * intrinsic_ratio).max(min_box_size.inline); - (inline_size, max_block_size) + (Violation::None, Violation::Above(max_block_size)) => Vec2 { + inline: (max_block_size * i_over_b).max(min_box_size.inline), + block: max_block_size, }, // Row 5. - (Violation::None, Violation::Below(min_block_size)) => { - let inline_size = - (min_block_size * intrinsic_ratio).clamp_below_max(max_box_size.inline); - (inline_size, min_block_size) + (Violation::None, Violation::Below(min_block_size)) => Vec2 { + inline: (min_block_size * i_over_b).clamp_below_max(max_box_size.inline), + block: min_block_size, }, // Rows 6-7. (Violation::Above(max_inline_size), Violation::Above(max_block_size)) => { - if max_inline_size.px() / intrinsic_size.inline.px() <= - max_block_size.px() / intrinsic_size.block.px() + if max_inline_size.px() / inline_size.px() <= + max_block_size.px() / block_size.px() { // Row 6. - let block_size = - (max_inline_size / intrinsic_ratio).max(min_box_size.block); - (max_inline_size, block_size) + Vec2 { + inline: max_inline_size, + block: (max_inline_size / i_over_b).max(min_box_size.block), + } } else { // Row 7. - let inline_size = - (max_block_size * intrinsic_ratio).max(min_box_size.inline); - (inline_size, max_block_size) + Vec2 { + inline: (max_block_size * i_over_b).max(min_box_size.inline), + block: max_block_size, + } } }, // Rows 8-9. (Violation::Below(min_inline_size), Violation::Below(min_block_size)) => { - if min_inline_size.px() / intrinsic_size.inline.px() <= - min_block_size.px() / intrinsic_size.block.px() + if min_inline_size.px() / inline_size.px() <= + min_block_size.px() / block_size.px() { // Row 8. - let inline_size = (min_block_size * intrinsic_ratio) - .clamp_below_max(max_box_size.inline); - (inline_size, min_block_size) + Vec2 { + inline: (min_block_size * i_over_b) + .clamp_below_max(max_box_size.inline), + block: min_block_size, + } } else { // Row 9. - let block_size = (min_inline_size / intrinsic_ratio) - .clamp_below_max(max_box_size.block); - (min_inline_size, block_size) + Vec2 { + inline: min_inline_size, + block: (min_inline_size / i_over_b) + .clamp_below_max(max_box_size.block), + } } }, // Row 10. - (Violation::Below(min_inline_size), Violation::Above(max_block_size)) => { - (min_inline_size, max_block_size) + (Violation::Below(min_inline_size), Violation::Above(max_block_size)) => Vec2 { + inline: min_inline_size, + block: max_block_size, }, // Row 11. - (Violation::Above(max_inline_size), Violation::Below(min_block_size)) => { - (max_inline_size, min_block_size) + (Violation::Above(max_inline_size), Violation::Below(min_block_size)) => Vec2 { + inline: max_inline_size, + block: min_block_size, }, } }, - }; - flow_relative::Vec2 { - inline: inline_size, - block: block_size, } } } From 80b2b5fb5e60f10ddac23429bf8c4353b6317133 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Dec 2019 22:04:41 +0100 Subject: [PATCH 08/19] Fix min/max-content of replaced boxes --- components/layout_2020/formatting_contexts.rs | 27 +++++----- components/layout_2020/replaced.rs | 51 ++++++++++++++----- 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index f741e44affd..eb90d62c926 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -59,31 +59,30 @@ impl IndependentFormattingContext { contents: Contents>, content_sizes: ContentSizesRequest, ) -> Self { - use self::IndependentFormattingContextContents as Contents; - let (contents, content_sizes) = match contents.try_into() { + match contents.try_into() { Ok(non_replaced) => match display_inside { DisplayInside::Flow | DisplayInside::FlowRoot => { - let (bfc, box_content_sizes) = BlockFormattingContext::construct( + let (bfc, content_sizes) = BlockFormattingContext::construct( context, &style, non_replaced, content_sizes, ); - (Contents::Flow(bfc), box_content_sizes) + Self { + style, + content_sizes, + contents: IndependentFormattingContextContents::Flow(bfc), + } }, }, Err(replaced) => { - // The `content_sizes` field is not used by layout code: - ( - Contents::Replaced(replaced), - BoxContentSizes::NoneWereRequested, - ) + let content_sizes = content_sizes.compute(|| replaced.inline_content_sizes(&style)); + Self { + style, + content_sizes, + contents: IndependentFormattingContextContents::Replaced(replaced), + } }, - }; - Self { - style, - contents, - content_sizes, } } diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 00242f61736..16a9b86fa8e 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -6,6 +6,7 @@ use crate::dom_traversal::NodeExt; use crate::fragments::{Fragment, ImageFragment}; use crate::geom::flow_relative::{Rect, Vec2}; use crate::geom::physical; +use crate::sizing::ContentSizes; use crate::style_ext::ComputedValuesExt; use crate::ContainingBlock; use net_traits::image::base::Image; @@ -63,6 +64,41 @@ impl ReplacedContent { None } + fn flow_relative_intrinsic_size(&self, style: &ComputedValues) -> Vec2> { + let intrinsic_size = physical::Vec2 { + x: self.intrinsic_width, + y: self.intrinsic_height, + }; + intrinsic_size.size_to_flow_relative(style.writing_mode) + } + + fn inline_size_over_block_size_intrinsic_ratio( + &self, + style: &ComputedValues, + ) -> Option { + self.intrinsic_ratio.map(|width_over_height| { + if style.writing_mode.is_vertical() { + 1. / width_over_height + } else { + width_over_height + } + }) + } + + pub fn inline_content_sizes(&self, style: &ComputedValues) -> ContentSizes { + // FIXME: min/max-content of replaced elements is not defined in + // https://dbaron.org/css/intrinsic/ + // This seems sensible? + let inline = self + .flow_relative_intrinsic_size(style) + .inline + .unwrap_or(Length::zero()); + ContentSizes { + min_content: inline, + max_content: inline, + } + } + pub fn make_fragments<'a>( &'a self, style: &ServoArc, @@ -95,19 +131,8 @@ impl ReplacedContent { style: &ComputedValues, ) -> Vec2 { let mode = style.writing_mode; - let intrinsic_size = physical::Vec2 { - x: self.intrinsic_width, - y: self.intrinsic_height, - }; - let intrinsic_size = intrinsic_size.size_to_flow_relative(mode); - let intrinsic_ratio = self.intrinsic_ratio.map(|width_over_height| { - // inline-size over block-size - if style.writing_mode.is_vertical() { - 1. / width_over_height - } else { - width_over_height - } - }); + let intrinsic_size = self.flow_relative_intrinsic_size(style); + let intrinsic_ratio = self.inline_size_over_block_size_intrinsic_ratio(style); let box_size = style.box_size().percentages_relative_to(containing_block); let min_box_size = style From f43dc3afcbe3bf1f5e3d8b5c4d063854bb1af7f6 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Dec 2019 23:32:15 +0100 Subject: [PATCH 09/19] Remove inline/block_size from AbsolutelyPositionedFragment --- components/layout_2020/positioned.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 7aed850a5ca..93a04f834c2 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -31,10 +31,7 @@ pub(crate) struct AbsolutelyPositionedFragment<'box_> { pub(crate) tree_rank: usize, pub(crate) inline_start: AbsoluteBoxOffsets, - inline_size: LengthPercentageOrAuto, - pub(crate) block_start: AbsoluteBoxOffsets, - block_size: LengthPercentageOrAuto, } #[derive(Clone, Copy, Debug)] @@ -79,10 +76,6 @@ impl AbsolutelyPositionedBox { ) -> AbsolutelyPositionedFragment { let style = &self.contents.style; let box_offsets = style.box_offsets(); - let box_size = style.box_size(); - - let inline_size = box_size.inline; - let block_size = box_size.block; fn absolute_box_offsets( initial_static_start: Length, @@ -114,9 +107,7 @@ impl AbsolutelyPositionedBox { absolutely_positioned_box: self, tree_rank, inline_start, - inline_size, block_start, - block_size, } } } @@ -162,6 +153,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { let cbis = containing_block.size.inline; let cbbs = containing_block.size.block; + let box_size = style.box_size(); let padding = style.padding().percentages_relative_to(cbis); let border = style.border_width(); let computed_margin = style.margin().percentages_relative_to(cbis); @@ -260,7 +252,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { } }, self.inline_start, - self.inline_size, + box_size.inline, ); let (block_anchor, block_size, margin_block_start, margin_block_end) = solve_axis( @@ -270,7 +262,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { computed_margin.block_end, |margins| (margins / 2., margins / 2.), self.block_start, - self.block_size, + box_size.block, ); let margin = Sides { From e86222d6bb073269fe20b427f797ed1ab9c972f8 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Dec 2019 23:38:21 +0100 Subject: [PATCH 10/19] =?UTF-8?q?Remove=20AbsoluteBoxOffsets=E2=80=99s=20t?= =?UTF-8?q?ype=20parameter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/layout_2020/positioned.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 93a04f834c2..026cca1d03b 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -30,16 +30,25 @@ pub(crate) struct AbsolutelyPositionedFragment<'box_> { /// static positions when going up the tree. pub(crate) tree_rank: usize, - pub(crate) inline_start: AbsoluteBoxOffsets, - pub(crate) block_start: AbsoluteBoxOffsets, + inline_start: AbsoluteBoxOffsets, + block_start: AbsoluteBoxOffsets, } #[derive(Clone, Copy, Debug)] -pub(crate) enum AbsoluteBoxOffsets { - StaticStart { start: Length }, - Start { start: NonStatic }, - End { end: NonStatic }, - Both { start: NonStatic, end: NonStatic }, +pub(crate) enum AbsoluteBoxOffsets { + StaticStart { + start: Length, + }, + Start { + start: LengthPercentage, + }, + End { + end: LengthPercentage, + }, + Both { + start: LengthPercentage, + end: LengthPercentage, + }, } impl AbsolutelyPositionedBox { @@ -81,7 +90,7 @@ impl AbsolutelyPositionedBox { initial_static_start: Length, start: LengthPercentageOrAuto, end: LengthPercentageOrAuto, - ) -> AbsoluteBoxOffsets { + ) -> AbsoluteBoxOffsets { match (start.non_auto(), end.non_auto()) { (None, None) => AbsoluteBoxOffsets::StaticStart { start: initial_static_start, @@ -170,7 +179,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { computed_margin_start: LengthOrAuto, computed_margin_end: LengthOrAuto, solve_margins: impl FnOnce(Length) -> (Length, Length), - box_offsets: AbsoluteBoxOffsets, + box_offsets: AbsoluteBoxOffsets, size: LengthPercentageOrAuto, ) -> (Anchor, LengthOrAuto, Length, Length) { let size = size.percentage_relative_to(containing_size); From 14ddf39215a0ff4bed7d3a5d3de258fa12461159 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Dec 2019 23:39:48 +0100 Subject: [PATCH 11/19] Rename ReplacedContent::used_size to used_size_as_if_inline_element --- components/layout_2020/flow/inline.rs | 2 +- components/layout_2020/flow/mod.rs | 2 +- components/layout_2020/replaced.rs | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 61c99fc0b7b..c4b760275d9 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -465,7 +465,7 @@ fn layout_atomic<'box_tree>( let fragment = match atomic.as_replaced() { Ok(replaced) => { - let size = replaced.used_size(ifc.containing_block, &atomic.style); + let size = replaced.used_size_as_if_inline_element(ifc.containing_block, &atomic.style); let fragments = replaced.make_fragments(&atomic.style, size.clone()); let content_rect = Rect { start_corner, size }; BoxFragment { diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 0e0c08cc168..ed578c825ef 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -521,7 +521,7 @@ fn layout_in_flow_replaced_block_level<'a>( style: &Arc, replaced: &ReplacedContent, ) -> BoxFragment { - let size = replaced.used_size(containing_block, style); + let size = replaced.used_size_as_if_inline_element(containing_block, style); let cbis = containing_block.inline_size; let padding = style.padding().percentages_relative_to(cbis); diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 16a9b86fa8e..72071dbf684 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -123,9 +123,12 @@ impl ReplacedContent { } } - // https://drafts.csswg.org/css2/visudet.html#inline-replaced-width - // https://drafts.csswg.org/css2/visudet.html#inline-replaced-height - pub fn used_size( + /// https://drafts.csswg.org/css2/visudet.html#inline-replaced-width + /// https://drafts.csswg.org/css2/visudet.html#inline-replaced-height + /// + /// Also used in other cases, for example + /// https://drafts.csswg.org/css2/visudet.html#block-replaced-width + pub fn used_size_as_if_inline_element( &self, containing_block: &ContainingBlock, style: &ComputedValues, From f09c14aa6b4d95a2010953bfb763900da4bf12d4 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Dec 2019 23:40:36 +0100 Subject: [PATCH 12/19] impl From<&'_ DefiniteContainingBlock> for ContainingBlock --- components/layout_2020/flow/root.rs | 24 +++++++++--------------- components/layout_2020/lib.rs | 10 ++++++++++ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index f1c301771f9..2415d18e0ca 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -16,12 +16,12 @@ use crate::positioned::AbsolutelyPositionedBox; use crate::replaced::ReplacedContent; use crate::sizing::ContentSizesRequest; use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside}; -use crate::{ContainingBlock, DefiniteContainingBlock}; +use crate::DefiniteContainingBlock; use rayon::iter::{IntoParallelRefIterator, ParallelExtend, ParallelIterator}; use script_layout_interface::wrapper_traits::LayoutNode; use servo_arc::Arc; use style::properties::ComputedValues; -use style::values::computed::{Length, LengthOrAuto}; +use style::values::computed::Length; use style::Zero; use style_traits::CSSPixel; @@ -99,31 +99,25 @@ impl BoxTreeRoot { viewport: geom::Size, ) -> FragmentTreeRoot { let style = ComputedValues::initial_values(); - let initial_containing_block_size = Vec2 { - inline: Length::new(viewport.width), - block: Length::new(viewport.height), - }; - - let initial_containing_block = ContainingBlock { - inline_size: initial_containing_block_size.inline, - block_size: LengthOrAuto::LengthPercentage(initial_containing_block_size.block), + let initial_containing_block = DefiniteContainingBlock { + size: Vec2 { + inline: Length::new(viewport.width), + block: Length::new(viewport.height), + }, // FIXME: use the document’s mode: // https://drafts.csswg.org/css-writing-modes/#principal-flow style, }; + let dummy_tree_rank = 0; let mut absolutely_positioned_fragments = vec![]; let mut independent_layout = self.0.layout( layout_context, - &initial_containing_block, + &(&initial_containing_block).into(), dummy_tree_rank, &mut absolutely_positioned_fragments, ); - let initial_containing_block = DefiniteContainingBlock { - size: initial_containing_block_size, - style, - }; independent_layout.fragments.par_extend( absolutely_positioned_fragments .par_iter() diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs index f0c56afa6f1..8e923f870d7 100644 --- a/components/layout_2020/lib.rs +++ b/components/layout_2020/lib.rs @@ -44,6 +44,16 @@ struct DefiniteContainingBlock<'a> { style: &'a ComputedValues, } +impl<'a> From<&'_ DefiniteContainingBlock<'a>> for ContainingBlock<'a> { + fn from(definite: &DefiniteContainingBlock<'a>) -> Self { + ContainingBlock { + inline_size: definite.size.inline, + block_size: LengthOrAuto::LengthPercentage(definite.size.block), + style: definite.style, + } + } +} + /// https://drafts.csswg.org/css2/visuren.html#relative-positioning fn relative_adjustement( style: &ComputedValues, From 1fcdde99cb8c6b6c10acb54f63ae4fa934c08773 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Dec 2019 23:55:19 +0100 Subject: [PATCH 13/19] Move two AbsoluteBoxOffsets fields into a Vec2 --- components/layout_2020/positioned.rs | 40 +++++++++++++--------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 026cca1d03b..d8e047e5015 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -30,8 +30,7 @@ pub(crate) struct AbsolutelyPositionedFragment<'box_> { /// static positions when going up the tree. pub(crate) tree_rank: usize, - inline_start: AbsoluteBoxOffsets, - block_start: AbsoluteBoxOffsets, + box_offsets: Vec2, } #[derive(Clone, Copy, Debug)] @@ -83,9 +82,6 @@ impl AbsolutelyPositionedBox { initial_start_corner: Vec2, tree_rank: usize, ) -> AbsolutelyPositionedFragment { - let style = &self.contents.style; - let box_offsets = style.box_offsets(); - fn absolute_box_offsets( initial_static_start: Length, start: LengthPercentageOrAuto, @@ -101,22 +97,22 @@ impl AbsolutelyPositionedBox { } } - let inline_start = absolute_box_offsets( - initial_start_corner.inline, - box_offsets.inline_start, - box_offsets.inline_end, - ); - let block_start = absolute_box_offsets( - initial_start_corner.block, - box_offsets.block_start, - box_offsets.block_end, - ); - + let box_offsets = self.contents.style.box_offsets(); AbsolutelyPositionedFragment { absolutely_positioned_box: self, tree_rank, - inline_start, - block_start, + box_offsets: Vec2 { + inline: absolute_box_offsets( + initial_start_corner.inline, + box_offsets.inline_start, + box_offsets.inline_end, + ), + block: absolute_box_offsets( + initial_start_corner.block, + box_offsets.block_start, + box_offsets.block_end, + ), + }, } } } @@ -260,7 +256,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { (Length::zero(), margins) } }, - self.inline_start, + self.box_offsets.inline, box_size.inline, ); @@ -270,7 +266,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { computed_margin.block_start, computed_margin.block_end, |margins| (margins / 2., margins / 2.), - self.block_start, + self.box_offsets.block, box_size.block, ); @@ -400,11 +396,11 @@ pub(crate) fn adjust_static_positions( abspos_fragment.tree_rank = tree_rank_in_parent; - if let AbsoluteBoxOffsets::StaticStart { start } = &mut abspos_fragment.inline_start { + if let AbsoluteBoxOffsets::StaticStart { start } = &mut abspos_fragment.box_offsets.inline { *start += child_fragment_rect.start_corner.inline; } - if let AbsoluteBoxOffsets::StaticStart { start } = &mut abspos_fragment.block_start { + if let AbsoluteBoxOffsets::StaticStart { start } = &mut abspos_fragment.box_offsets.block { *start += child_fragment_rect.start_corner.block; } } From 1fa20e93d038c1c905bfd6ca6af6b96c6138849d Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 8 Dec 2019 00:54:26 +0100 Subject: [PATCH 14/19] Implement replaced abspos --- components/layout_2020/positioned.rs | 125 +++++++++++++++------------ 1 file changed, 71 insertions(+), 54 deletions(-) diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index d8e047e5015..12ebf24c62e 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -158,7 +158,30 @@ impl<'a> AbsolutelyPositionedFragment<'a> { let cbis = containing_block.size.inline; let cbbs = containing_block.size.block; - let box_size = style.box_size(); + let size; + let replaced_used_size; + match self.absolutely_positioned_box.contents.as_replaced() { + Ok(replaced) => { + // https://drafts.csswg.org/css2/visudet.html#abs-replaced-width + // https://drafts.csswg.org/css2/visudet.html#abs-replaced-height + let u = replaced.used_size_as_if_inline_element(&containing_block.into(), style); + size = Vec2 { + inline: LengthOrAuto::LengthPercentage(u.inline), + block: LengthOrAuto::LengthPercentage(u.block), + }; + replaced_used_size = Some(u); + } + Err(_non_replaced) => { + let box_size = style.box_size(); + size = Vec2 { + inline: box_size.inline.percentage_relative_to(cbis), + block: box_size.block.percentage_relative_to(cbbs), + }; + replaced_used_size = None; + } + } + + let padding = style.padding().percentages_relative_to(cbis); let border = style.border_width(); let computed_margin = style.margin().percentages_relative_to(cbis); @@ -169,6 +192,17 @@ impl<'a> AbsolutelyPositionedFragment<'a> { End(Length), } + /// This unifies both: + /// + /// * https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width + /// * https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height + /// + /// … and: + /// + /// * https://drafts.csswg.org/css2/visudet.html#abs-replaced-width + /// * https://drafts.csswg.org/css2/visudet.html#abs-replaced-height + /// + /// In the replaced case, `size` is never `Auto`. fn solve_axis( containing_size: Length, padding_border_sum: Length, @@ -176,9 +210,8 @@ impl<'a> AbsolutelyPositionedFragment<'a> { computed_margin_end: LengthOrAuto, solve_margins: impl FnOnce(Length) -> (Length, Length), box_offsets: AbsoluteBoxOffsets, - size: LengthPercentageOrAuto, + size: LengthOrAuto, ) -> (Anchor, LengthOrAuto, Length, Length) { - let size = size.percentage_relative_to(containing_size); match box_offsets { AbsoluteBoxOffsets::StaticStart { start } => ( Anchor::Start(start), @@ -257,7 +290,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { } }, self.box_offsets.inline, - box_size.inline, + size.inline, ); let (block_anchor, block_size, margin_block_start, margin_block_end) = solve_axis( @@ -267,7 +300,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { computed_margin.block_end, |margins| (margins / 2., margins / 2.), self.box_offsets.block, - box_size.block, + size.block, ); let margin = Sides { @@ -277,47 +310,30 @@ impl<'a> AbsolutelyPositionedFragment<'a> { block_end: margin_block_end, }; - let inline_size = inline_size.auto_is(|| { - let available_size = match inline_anchor { - Anchor::Start(start) => cbis - start - pb.inline_sum() - margin.inline_sum(), - Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(), - }; - - if self - .absolutely_positioned_box - .contents - .as_replaced() - .is_ok() - { - // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width - available_size - } else { - self.absolutely_positioned_box - .contents - .content_sizes - .shrink_to_fit(available_size) - } - }); - let mut absolutely_positioned_fragments = Vec::new(); - let mut independent_layout = match self.absolutely_positioned_box.contents.as_replaced() { + let (size, mut fragments) = match self.absolutely_positioned_box.contents.as_replaced() { Ok(replaced) => { - // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width - // and https://drafts.csswg.org/css2/visudet.html#abs-replaced-height - let block_size = block_size.auto_is(Length::zero); - let fragments = replaced.make_fragments( - &self.absolutely_positioned_box.contents.style, - Vec2 { - inline: inline_size, - block: block_size, - }, - ); - crate::formatting_contexts::IndependentLayout { - fragments, - content_block_size: block_size, - } + // https://drafts.csswg.org/css2/visudet.html#abs-replaced-width + // https://drafts.csswg.org/css2/visudet.html#abs-replaced-height + let style = &self.absolutely_positioned_box.contents.style; + let size = replaced_used_size.unwrap(); + let fragments = replaced.make_fragments(style, size.clone()); + (size, fragments) }, Err(non_replaced) => { + // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width + // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height + let inline_size = inline_size.auto_is(|| { + let available_size = match inline_anchor { + Anchor::Start(start) => cbis - start - pb.inline_sum() - margin.inline_sum(), + Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(), + }; + self.absolutely_positioned_box + .contents + .content_sizes + .shrink_to_fit(available_size) + }); + let containing_block_for_children = ContainingBlock { inline_size, block_size, @@ -330,24 +346,28 @@ impl<'a> AbsolutelyPositionedFragment<'a> { "Mixed writing modes are not supported yet" ); let dummy_tree_rank = 0; - non_replaced.layout( + let independent_layout = non_replaced.layout( layout_context, &containing_block_for_children, dummy_tree_rank, &mut absolutely_positioned_fragments, - ) + ); + + let size = Vec2 { + inline: inline_size, + block: block_size.auto_is(|| independent_layout.content_block_size), + }; + (size, independent_layout.fragments) }, }; let inline_start = match inline_anchor { Anchor::Start(start) => start + pb.inline_start + margin.inline_start, - Anchor::End(end) => cbbs - end - pb.inline_end - margin.inline_end - inline_size, + Anchor::End(end) => cbbs - end - pb.inline_end - margin.inline_end - size.inline, }; - - let block_size = block_size.auto_is(|| independent_layout.content_block_size); let block_start = match block_anchor { Anchor::Start(start) => start + pb.block_start + margin.block_start, - Anchor::End(end) => cbbs - end - pb.block_end - margin.block_end - block_size, + Anchor::End(end) => cbbs - end - pb.block_end - margin.block_end - size.block, }; let content_rect = Rect { @@ -355,16 +375,13 @@ impl<'a> AbsolutelyPositionedFragment<'a> { inline: inline_start, block: block_start, }, - size: Vec2 { - inline: inline_size, - block: block_size, - }, + size, }; AbsolutelyPositionedFragment::in_positioned_containing_block( layout_context, &absolutely_positioned_fragments, - &mut independent_layout.fragments, + &mut fragments, &content_rect.size, &padding, style, @@ -372,7 +389,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { Fragment::Box(BoxFragment { style: style.clone(), - children: independent_layout.fragments, + children: fragments, content_rect, padding, border, From c07c9805010f3b62c571485ca579b3d23623ea69 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 8 Dec 2019 01:02:27 +0100 Subject: [PATCH 15/19] Delayed initialization over mutation --- components/layout_2020/positioned.rs | 29 +++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 12ebf24c62e..c327b23924b 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -235,11 +235,12 @@ impl<'a> AbsolutelyPositionedFragment<'a> { let start = start.percentage_relative_to(containing_size); let end = end.percentage_relative_to(containing_size); - let mut margin_start = computed_margin_start.auto_is(Length::zero); - let mut margin_end = computed_margin_end.auto_is(Length::zero); - - let size = if let LengthOrAuto::LengthPercentage(size) = size { - let margins = containing_size - start - end - padding_border_sum - size; + let margin_start; + let margin_end; + let used_size; + if let LengthOrAuto::LengthPercentage(s) = size { + used_size = s; + let margins = containing_size - start - end - padding_border_sum - s; match (computed_margin_start, computed_margin_end) { (LengthOrAuto::Auto, LengthOrAuto::Auto) => { let (s, e) = solve_margins(margins); @@ -248,19 +249,25 @@ impl<'a> AbsolutelyPositionedFragment<'a> { }, (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => { margin_start = margins - end; + margin_end = end; }, (LengthOrAuto::LengthPercentage(start), LengthOrAuto::Auto) => { + margin_start = start; margin_end = margins - start; }, ( - LengthOrAuto::LengthPercentage(_), - LengthOrAuto::LengthPercentage(_), - ) => {}, + LengthOrAuto::LengthPercentage(start), + LengthOrAuto::LengthPercentage(end), + ) => { + margin_start = start; + margin_end = end; + }, } - size } else { + margin_start = computed_margin_start.auto_is(Length::zero); + margin_end = computed_margin_end.auto_is(Length::zero); // FIXME(nox): What happens if that is negative? - containing_size - + used_size = containing_size - start - end - padding_border_sum - @@ -269,7 +276,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { }; ( Anchor::Start(start), - LengthOrAuto::LengthPercentage(size), + LengthOrAuto::LengthPercentage(used_size), margin_start, margin_end, ) From 29067225a46998927dabb95f0b5e16eebd241687 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 8 Dec 2019 01:04:18 +0100 Subject: [PATCH 16/19] Conditionsals over closures --- components/layout_2020/positioned.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index c327b23924b..afbebcd565d 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -208,7 +208,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { padding_border_sum: Length, computed_margin_start: LengthOrAuto, computed_margin_end: LengthOrAuto, - solve_margins: impl FnOnce(Length) -> (Length, Length), + avoid_negative_margin_start: bool, box_offsets: AbsoluteBoxOffsets, size: LengthOrAuto, ) -> (Anchor, LengthOrAuto, Length, Length) { @@ -243,9 +243,13 @@ impl<'a> AbsolutelyPositionedFragment<'a> { let margins = containing_size - start - end - padding_border_sum - s; match (computed_margin_start, computed_margin_end) { (LengthOrAuto::Auto, LengthOrAuto::Auto) => { - let (s, e) = solve_margins(margins); - margin_start = s; - margin_end = e; + if avoid_negative_margin_start && margins < Length::zero() { + margin_start = Length::zero(); + margin_end = margins; + } else { + margin_start = margins / 2.; + margin_end = margins / 2.; + } }, (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => { margin_start = margins - end; @@ -289,13 +293,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { pb.inline_sum(), computed_margin.inline_start, computed_margin.inline_end, - |margins| { - if margins.px() >= 0. { - (margins / 2., margins / 2.) - } else { - (Length::zero(), margins) - } - }, + /* avoid_negative_margin_start */ true, self.box_offsets.inline, size.inline, ); @@ -305,7 +303,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { pb.block_sum(), computed_margin.block_start, computed_margin.block_end, - |margins| (margins / 2., margins / 2.), + /* avoid_negative_margin_start */ false, self.box_offsets.block, size.block, ); From be8df1d114781030cd62b72fc6687db7b419727f Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 8 Dec 2019 01:06:29 +0100 Subject: [PATCH 17/19] Move `solve_axis` function to module level --- components/layout_2020/positioned.rs | 207 +++++++++++++-------------- 1 file changed, 102 insertions(+), 105 deletions(-) diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index afbebcd565d..0dca844fea9 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -170,7 +170,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { block: LengthOrAuto::LengthPercentage(u.block), }; replaced_used_size = Some(u); - } + }, Err(_non_replaced) => { let box_size = style.box_size(); size = Vec2 { @@ -178,116 +178,14 @@ impl<'a> AbsolutelyPositionedFragment<'a> { block: box_size.block.percentage_relative_to(cbbs), }; replaced_used_size = None; - } + }, } - let padding = style.padding().percentages_relative_to(cbis); let border = style.border_width(); let computed_margin = style.margin().percentages_relative_to(cbis); let pb = &padding + &border; - enum Anchor { - Start(Length), - End(Length), - } - - /// This unifies both: - /// - /// * https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width - /// * https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height - /// - /// … and: - /// - /// * https://drafts.csswg.org/css2/visudet.html#abs-replaced-width - /// * https://drafts.csswg.org/css2/visudet.html#abs-replaced-height - /// - /// In the replaced case, `size` is never `Auto`. - fn solve_axis( - containing_size: Length, - padding_border_sum: Length, - computed_margin_start: LengthOrAuto, - computed_margin_end: LengthOrAuto, - avoid_negative_margin_start: bool, - box_offsets: AbsoluteBoxOffsets, - size: LengthOrAuto, - ) -> (Anchor, LengthOrAuto, Length, Length) { - match box_offsets { - AbsoluteBoxOffsets::StaticStart { start } => ( - Anchor::Start(start), - size, - computed_margin_start.auto_is(Length::zero), - computed_margin_end.auto_is(Length::zero), - ), - AbsoluteBoxOffsets::Start { start } => ( - Anchor::Start(start.percentage_relative_to(containing_size)), - size, - computed_margin_start.auto_is(Length::zero), - computed_margin_end.auto_is(Length::zero), - ), - AbsoluteBoxOffsets::End { end } => ( - Anchor::End(end.percentage_relative_to(containing_size)), - size, - computed_margin_start.auto_is(Length::zero), - computed_margin_end.auto_is(Length::zero), - ), - AbsoluteBoxOffsets::Both { start, end } => { - let start = start.percentage_relative_to(containing_size); - let end = end.percentage_relative_to(containing_size); - - let margin_start; - let margin_end; - let used_size; - if let LengthOrAuto::LengthPercentage(s) = size { - used_size = s; - let margins = containing_size - start - end - padding_border_sum - s; - match (computed_margin_start, computed_margin_end) { - (LengthOrAuto::Auto, LengthOrAuto::Auto) => { - if avoid_negative_margin_start && margins < Length::zero() { - margin_start = Length::zero(); - margin_end = margins; - } else { - margin_start = margins / 2.; - margin_end = margins / 2.; - } - }, - (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => { - margin_start = margins - end; - margin_end = end; - }, - (LengthOrAuto::LengthPercentage(start), LengthOrAuto::Auto) => { - margin_start = start; - margin_end = margins - start; - }, - ( - LengthOrAuto::LengthPercentage(start), - LengthOrAuto::LengthPercentage(end), - ) => { - margin_start = start; - margin_end = end; - }, - } - } else { - margin_start = computed_margin_start.auto_is(Length::zero); - margin_end = computed_margin_end.auto_is(Length::zero); - // FIXME(nox): What happens if that is negative? - used_size = containing_size - - start - - end - - padding_border_sum - - margin_start - - margin_end - }; - ( - Anchor::Start(start), - LengthOrAuto::LengthPercentage(used_size), - margin_start, - margin_end, - ) - }, - } - } - let (inline_anchor, inline_size, margin_inline_start, margin_inline_end) = solve_axis( cbis, pb.inline_sum(), @@ -330,7 +228,9 @@ impl<'a> AbsolutelyPositionedFragment<'a> { // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height let inline_size = inline_size.auto_is(|| { let available_size = match inline_anchor { - Anchor::Start(start) => cbis - start - pb.inline_sum() - margin.inline_sum(), + Anchor::Start(start) => { + cbis - start - pb.inline_sum() - margin.inline_sum() + }, Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(), }; self.absolutely_positioned_box @@ -404,6 +304,103 @@ impl<'a> AbsolutelyPositionedFragment<'a> { } } +enum Anchor { + Start(Length), + End(Length), +} + +/// This unifies some of the parts in common in: +/// +/// * https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width +/// * https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height +/// +/// … and: +/// +/// * https://drafts.csswg.org/css2/visudet.html#abs-replaced-width +/// * https://drafts.csswg.org/css2/visudet.html#abs-replaced-height +/// +/// In the replaced case, `size` is never `Auto`. +fn solve_axis( + containing_size: Length, + padding_border_sum: Length, + computed_margin_start: LengthOrAuto, + computed_margin_end: LengthOrAuto, + avoid_negative_margin_start: bool, + box_offsets: AbsoluteBoxOffsets, + size: LengthOrAuto, +) -> (Anchor, LengthOrAuto, Length, Length) { + match box_offsets { + AbsoluteBoxOffsets::StaticStart { start } => ( + Anchor::Start(start), + size, + computed_margin_start.auto_is(Length::zero), + computed_margin_end.auto_is(Length::zero), + ), + AbsoluteBoxOffsets::Start { start } => ( + Anchor::Start(start.percentage_relative_to(containing_size)), + size, + computed_margin_start.auto_is(Length::zero), + computed_margin_end.auto_is(Length::zero), + ), + AbsoluteBoxOffsets::End { end } => ( + Anchor::End(end.percentage_relative_to(containing_size)), + size, + computed_margin_start.auto_is(Length::zero), + computed_margin_end.auto_is(Length::zero), + ), + AbsoluteBoxOffsets::Both { start, end } => { + let start = start.percentage_relative_to(containing_size); + let end = end.percentage_relative_to(containing_size); + + let margin_start; + let margin_end; + let used_size; + if let LengthOrAuto::LengthPercentage(s) = size { + used_size = s; + let margins = containing_size - start - end - padding_border_sum - s; + match (computed_margin_start, computed_margin_end) { + (LengthOrAuto::Auto, LengthOrAuto::Auto) => { + if avoid_negative_margin_start && margins < Length::zero() { + margin_start = Length::zero(); + margin_end = margins; + } else { + margin_start = margins / 2.; + margin_end = margins / 2.; + } + }, + (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => { + margin_start = margins - end; + margin_end = end; + }, + (LengthOrAuto::LengthPercentage(start), LengthOrAuto::Auto) => { + margin_start = start; + margin_end = margins - start; + }, + ( + LengthOrAuto::LengthPercentage(start), + LengthOrAuto::LengthPercentage(end), + ) => { + margin_start = start; + margin_end = end; + }, + } + } else { + margin_start = computed_margin_start.auto_is(Length::zero); + margin_end = computed_margin_end.auto_is(Length::zero); + // FIXME(nox): What happens if that is negative? + used_size = + containing_size - start - end - padding_border_sum - margin_start - margin_end + }; + ( + Anchor::Start(start), + LengthOrAuto::LengthPercentage(used_size), + margin_start, + margin_end, + ) + }, + } +} + pub(crate) fn adjust_static_positions( absolutely_positioned_fragments: &mut [AbsolutelyPositionedFragment], child_fragments: &mut [Fragment], From a17db217a18e8646c4e8c64b81533469ecbd2580 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 8 Dec 2019 01:12:10 +0100 Subject: [PATCH 18/19] Struct with named fields over large tuple --- components/layout_2020/positioned.rs | 73 ++++++++++++++++------------ 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 0dca844fea9..8b5f078f520 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -186,7 +186,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { let computed_margin = style.margin().percentages_relative_to(cbis); let pb = &padding + &border; - let (inline_anchor, inline_size, margin_inline_start, margin_inline_end) = solve_axis( + let inline_axis = solve_axis( cbis, pb.inline_sum(), computed_margin.inline_start, @@ -196,7 +196,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { size.inline, ); - let (block_anchor, block_size, margin_block_start, margin_block_end) = solve_axis( + let block_axis = solve_axis( cbis, pb.block_sum(), computed_margin.block_start, @@ -207,10 +207,10 @@ impl<'a> AbsolutelyPositionedFragment<'a> { ); let margin = Sides { - inline_start: margin_inline_start, - inline_end: margin_inline_end, - block_start: margin_block_start, - block_end: margin_block_end, + inline_start: inline_axis.margin_start, + inline_end: inline_axis.margin_end, + block_start: block_axis.margin_start, + block_end: block_axis.margin_end, }; let mut absolutely_positioned_fragments = Vec::new(); @@ -226,8 +226,8 @@ impl<'a> AbsolutelyPositionedFragment<'a> { Err(non_replaced) => { // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height - let inline_size = inline_size.auto_is(|| { - let available_size = match inline_anchor { + let inline_size = inline_axis.size.auto_is(|| { + let available_size = match inline_axis.anchor { Anchor::Start(start) => { cbis - start - pb.inline_sum() - margin.inline_sum() }, @@ -241,7 +241,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { let containing_block_for_children = ContainingBlock { inline_size, - block_size, + block_size: block_axis.size, style, }; // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows @@ -260,17 +260,19 @@ impl<'a> AbsolutelyPositionedFragment<'a> { let size = Vec2 { inline: inline_size, - block: block_size.auto_is(|| independent_layout.content_block_size), + block: block_axis + .size + .auto_is(|| independent_layout.content_block_size), }; (size, independent_layout.fragments) }, }; - let inline_start = match inline_anchor { + let inline_start = match inline_axis.anchor { Anchor::Start(start) => start + pb.inline_start + margin.inline_start, Anchor::End(end) => cbbs - end - pb.inline_end - margin.inline_end - size.inline, }; - let block_start = match block_anchor { + let block_start = match block_axis.anchor { Anchor::Start(start) => start + pb.block_start + margin.block_start, Anchor::End(end) => cbbs - end - pb.block_end - margin.block_end - size.block, }; @@ -309,6 +311,13 @@ enum Anchor { End(Length), } +struct AxisResult { + anchor: Anchor, + size: LengthOrAuto, + margin_start: Length, + margin_end: Length, +} + /// This unifies some of the parts in common in: /// /// * https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width @@ -328,26 +337,26 @@ fn solve_axis( avoid_negative_margin_start: bool, box_offsets: AbsoluteBoxOffsets, size: LengthOrAuto, -) -> (Anchor, LengthOrAuto, Length, Length) { +) -> AxisResult { match box_offsets { - AbsoluteBoxOffsets::StaticStart { start } => ( - Anchor::Start(start), + AbsoluteBoxOffsets::StaticStart { start } => AxisResult { + anchor: Anchor::Start(start), size, - computed_margin_start.auto_is(Length::zero), - computed_margin_end.auto_is(Length::zero), - ), - AbsoluteBoxOffsets::Start { start } => ( - Anchor::Start(start.percentage_relative_to(containing_size)), + margin_start: computed_margin_start.auto_is(Length::zero), + margin_end: computed_margin_end.auto_is(Length::zero), + }, + AbsoluteBoxOffsets::Start { start } => AxisResult { + anchor: Anchor::Start(start.percentage_relative_to(containing_size)), size, - computed_margin_start.auto_is(Length::zero), - computed_margin_end.auto_is(Length::zero), - ), - AbsoluteBoxOffsets::End { end } => ( - Anchor::End(end.percentage_relative_to(containing_size)), + margin_start: computed_margin_start.auto_is(Length::zero), + margin_end: computed_margin_end.auto_is(Length::zero), + }, + AbsoluteBoxOffsets::End { end } => AxisResult { + anchor: Anchor::End(end.percentage_relative_to(containing_size)), size, - computed_margin_start.auto_is(Length::zero), - computed_margin_end.auto_is(Length::zero), - ), + margin_start: computed_margin_start.auto_is(Length::zero), + margin_end: computed_margin_end.auto_is(Length::zero), + }, AbsoluteBoxOffsets::Both { start, end } => { let start = start.percentage_relative_to(containing_size); let end = end.percentage_relative_to(containing_size); @@ -391,12 +400,12 @@ fn solve_axis( used_size = containing_size - start - end - padding_border_sum - margin_start - margin_end }; - ( - Anchor::Start(start), - LengthOrAuto::LengthPercentage(used_size), + AxisResult { + anchor: Anchor::Start(start), + size: LengthOrAuto::LengthPercentage(used_size), margin_start, margin_end, - ) + } }, } } From 53ce7140059c07019489fc0eae522e9f76fcb08b Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 8 Dec 2019 11:41:29 +0100 Subject: [PATCH 19/19] =?UTF-8?q?Fix=20a=20=E2=80=9CAccessing=20content=20?= =?UTF-8?q?size=20that=20was=20not=20requested=E2=80=9D=20panic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Percentage `width` are treated as `auto` for the purpose of min/max-content computation, so they also need to be considered when testing “wether width is auto” --- components/layout_2020/flow/construct.rs | 6 +++--- components/layout_2020/flow/float.rs | 2 +- components/layout_2020/positioned.rs | 2 +- components/layout_2020/style_ext.rs | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index c02dcd08fd7..896293831bc 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -393,7 +393,7 @@ where style.clone(), display_inside, contents, - ContentSizesRequest::inline_if(style.inline_size_is_auto()), + ContentSizesRequest::inline_if(!style.inline_size_is_length()), ), )) }; @@ -590,7 +590,7 @@ where &style, ContentSizesRequest::inline_if( max_assign_in_flow_outer_content_sizes_to.is_some() && - style.inline_size_is_auto(), + !style.inline_size_is_length(), ), ); if let Some(to) = max_assign_in_flow_outer_content_sizes_to { @@ -607,7 +607,7 @@ where } => { let content_sizes = ContentSizesRequest::inline_if( max_assign_in_flow_outer_content_sizes_to.is_some() && - style.inline_size_is_auto(), + !style.inline_size_is_length(), ); let contents = IndependentFormattingContext::construct( context, diff --git a/components/layout_2020/flow/float.rs b/components/layout_2020/flow/float.rs index 2acc2095004..df9cbbaf514 100644 --- a/components/layout_2020/flow/float.rs +++ b/components/layout_2020/flow/float.rs @@ -33,7 +33,7 @@ impl FloatBox { display_inside: DisplayInside, contents: Contents>, ) -> Self { - let content_sizes = ContentSizesRequest::inline_if(style.inline_size_is_auto()); + let content_sizes = ContentSizesRequest::inline_if(!style.inline_size_is_length()); Self { contents: IndependentFormattingContext::construct( context, diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 8b5f078f520..9459f3c3f84 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -60,7 +60,7 @@ impl AbsolutelyPositionedBox { // "Shrink-to-fit" in https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width let content_sizes = ContentSizesRequest::inline_if( // If inline-size is non-auto, that value is used without shrink-to-fit - style.inline_size_is_auto() && + !style.inline_size_is_length() && // If it is, then the only case where shrink-to-fit is *not* used is // if both offsets are non-auto, leaving inline-size as the only variable // in the constraint equation. diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs index 59c87fd633c..67d8bc78d7e 100644 --- a/components/layout_2020/style_ext.rs +++ b/components/layout_2020/style_ext.rs @@ -40,7 +40,7 @@ pub(crate) enum DisplayInside { } pub(crate) trait ComputedValuesExt { - fn inline_size_is_auto(&self) -> bool; + fn inline_size_is_length(&self) -> bool; fn inline_box_offsets_are_both_non_auto(&self) -> bool; fn box_offsets(&self) -> flow_relative::Sides; fn box_size(&self) -> flow_relative::Vec2; @@ -52,14 +52,14 @@ pub(crate) trait ComputedValuesExt { } impl ComputedValuesExt for ComputedValues { - fn inline_size_is_auto(&self) -> bool { + fn inline_size_is_length(&self) -> bool { let position = self.get_position(); let size = if self.writing_mode.is_horizontal() { position.width } else { position.height }; - size == Size::Auto + matches!(size, Size::LengthPercentage(lp) if lp.0.as_length().is_some()) } fn inline_box_offsets_are_both_non_auto(&self) -> bool {