diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index ef52fba6750..0b741e0c90c 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -529,10 +529,8 @@ fn layout_atomic( atomic: &IndependentFormattingContext, ) { let pbm = atomic.style.padding_border_margin(&ifc.containing_block); - let padding = pbm.padding; - let border = pbm.border; let margin = pbm.margin.auto_is(Length::zero); - let pbm_sums = &(&padding + &border) + &margin; + let pbm_sums = &(&pbm.padding + &pbm.border) + &margin; ifc.inline_position += pbm_sums.inline_start; let mut start_corner = Vec2 { block: pbm_sums.block_start, @@ -544,7 +542,8 @@ fn layout_atomic( let fragment = match atomic.as_replaced() { Ok(replaced) => { - let size = replaced.used_size_as_if_inline_element(ifc.containing_block, &atomic.style); + let size = + replaced.used_size_as_if_inline_element(ifc.containing_block, &atomic.style, &pbm); let fragments = replaced.make_fragments(&atomic.style, size.clone()); let content_rect = Rect { start_corner, size }; BoxFragment::new( @@ -552,31 +551,27 @@ fn layout_atomic( atomic.style.clone(), fragments, content_rect, - padding, - border, + pbm.padding, + pbm.border, margin, CollapsedBlockMargins::zero(), ) }, Err(non_replaced) => { - let box_size = atomic.style.box_size(); + let box_size = atomic.style.content_box_size(&ifc.containing_block, &pbm); let max_box_size = atomic .style - .max_box_size() - .percentages_relative_to(ifc.containing_block); + .content_max_box_size(&ifc.containing_block, &pbm); let min_box_size = atomic .style - .min_box_size() - .percentages_relative_to(ifc.containing_block) + .content_min_box_size(&ifc.containing_block, &pbm) .auto_is(Length::zero); // https://drafts.csswg.org/css2/visudet.html#inlineblock-width - let cbis = ifc.containing_block.inline_size; - let tentative_inline_size = - box_size.inline.percentage_relative_to(cbis).auto_is(|| { - let available_size = cbis - pbm_sums.inline_sum(); - atomic.content_sizes.shrink_to_fit(available_size) - }); + let tentative_inline_size = box_size.inline.auto_is(|| { + let available_size = ifc.containing_block.inline_size - pbm_sums.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 @@ -584,12 +579,9 @@ fn layout_atomic( 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()); let containing_block_for_children = ContainingBlock { inline_size, - block_size, + block_size: box_size.block, style: &atomic.style, }; assert_eq!( @@ -608,7 +600,9 @@ fn layout_atomic( ); // https://drafts.csswg.org/css2/visudet.html#block-root-margin - let tentative_block_size = block_size.auto_is(|| independent_layout.content_block_size); + let tentative_block_size = box_size + .block + .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 @@ -628,8 +622,8 @@ fn layout_atomic( atomic.style.clone(), independent_layout.fragments, content_rect, - padding, - border, + pbm.padding, + pbm.border, margin, CollapsedBlockMargins::zero(), ) diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index bd39a55cc53..33bba78de96 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -351,14 +351,10 @@ fn layout_in_flow_non_replaced_block_level( float_context: Option<&mut FloatContext>, ) -> BoxFragment { let pbm = style.padding_border_margin(containing_block); - - 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 box_size = style.content_box_size(containing_block, &pbm); + let max_box_size = style.content_max_box_size(containing_block, &pbm); let min_box_size = style - .min_box_size() - .percentages_relative_to(containing_block) + .content_min_box_size(containing_block, &pbm) .auto_is(Length::zero); // https://drafts.csswg.org/css2/visudet.html#min-max-widths @@ -508,7 +504,7 @@ fn layout_in_flow_replaced_block_level<'a>( replaced: &ReplacedContent, ) -> BoxFragment { let pbm = style.padding_border_margin(containing_block); - let size = replaced.used_size_as_if_inline_element(containing_block, style); + let size = replaced.used_size_as_if_inline_element(containing_block, style, &pbm); let (margin_inline_start, margin_inline_end) = solve_inline_margins_for_in_flow_block_level(containing_block, &pbm, size.inline); diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 993cf40786f..78b346700f5 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -416,19 +416,16 @@ impl HoistedAbsolutelyPositionedBox { 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); + let used_size = + replaced.used_size_as_if_inline_element(&containing_block.into(), style, &pbm); size = Vec2 { - inline: LengthOrAuto::LengthPercentage(u.inline), - block: LengthOrAuto::LengthPercentage(u.block), + inline: LengthOrAuto::LengthPercentage(used_size.inline), + block: LengthOrAuto::LengthPercentage(used_size.block), }; - replaced_used_size = Some(u); + replaced_used_size = Some(used_size); }, 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), - }; + size = style.content_box_size(&containing_block.into(), &pbm); replaced_used_size = None; }, } diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 1d7557b0641..334ff44a665 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -8,7 +8,7 @@ use crate::fragments::{DebugId, Fragment, ImageFragment}; use crate::geom::flow_relative::{Rect, Vec2}; use crate::geom::PhysicalSize; use crate::sizing::ContentSizes; -use crate::style_ext::ComputedValuesExt; +use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin}; use crate::ContainingBlock; use canvas_traits::canvas::{CanvasId, CanvasMsg, FromLayoutMsg}; use ipc_channel::ipc::{self, IpcSender}; @@ -240,19 +240,17 @@ impl ReplacedContent { &self, containing_block: &ContainingBlock, style: &ComputedValues, + pbm: &PaddingBorderMargin, ) -> Vec2 { let mode = style.writing_mode; 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 box_size = style.content_box_size(containing_block, &pbm); + let max_box_size = style.content_max_box_size(containing_block, &pbm); let min_box_size = style - .min_box_size() - .percentages_relative_to(containing_block) + .content_min_box_size(containing_block, &pbm) .auto_is(Length::zero); - let max_box_size = style - .max_box_size() - .percentages_relative_to(containing_block); let default_object_size = || { // FIXME: diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index b92360bf42f..ae742763ea9 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -5,6 +5,7 @@ //! https://drafts.csswg.org/css-sizing/ use crate::style_ext::ComputedValuesExt; +use style::properties::longhands::box_sizing::computed_value::T as BoxSizing; use style::properties::ComputedValues; use style::values::computed::{Length, LengthPercentage, Percentage}; use style::values::generics::length::MaxSize; @@ -63,6 +64,13 @@ impl ContentSizes { } } + fn map(&self, f: impl Fn(Length) -> Length) -> Self { + Self { + min_content: f(self.min_content), + max_content: f(self.max_content), + } + } + pub fn max_assign(&mut self, other: &Self) { self.min_content.max_assign(other.min_content); self.max_content.max_assign(other.max_content); @@ -108,61 +116,68 @@ impl BoxContentSizes { &self, style: &ComputedValues, ) -> (ContentSizes, Percentage) { - // FIXME: account for 'box-sizing' - let inline_size = style.box_size().inline; + let padding = style.padding(); + let border = style.border_width(); + let margin = style.margin(); + + let mut pbm_percentages = Percentage::zero(); + let mut decompose = |x: LengthPercentage| { + pbm_percentages += x.to_percentage().unwrap_or_else(Zero::zero); + x.to_length().unwrap_or_else(Zero::zero) + }; + let pb_lengths = + border.inline_sum() + decompose(padding.inline_start) + decompose(padding.inline_end); + let mut m_lengths = Length::zero(); + if let Some(m) = margin.inline_start.non_auto() { + m_lengths += decompose(m) + } + if let Some(m) = margin.inline_end.non_auto() { + m_lengths += decompose(m) + } + + let box_sizing = style.get_position().box_sizing; + let inline_size = style + .box_size() + .inline + .non_auto() + // Percentages for 'width' are treated as 'auto' + .and_then(|lp| lp.to_length()); let min_inline_size = style .min_box_size() .inline + // Percentages for 'min-width' are treated as zero .percentage_relative_to(Length::zero()) + // FIXME: 'auto' is not zero in Flexbox .auto_is(Length::zero); let max_inline_size = match style.max_box_size().inline { MaxSize::None => None, + // Percentages for 'max-width' are treated as 'none' MaxSize::LengthPercentage(ref lp) => lp.to_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.to_length()); - // The (inner) min/max-content are only used for 'auto' - let mut outer = match inline_size.non_auto().flatten() { - None => { - let inner = self.expect_inline().clone(); + let border_box_sizes = match inline_size { + Some(non_auto) => { + let clamped = clamp(non_auto); + let border_box_size = match box_sizing { + BoxSizing::ContentBox => clamped + pb_lengths, + BoxSizing::BorderBox => clamped, + }; ContentSizes { - min_content: clamp(inner.min_content), - max_content: clamp(inner.max_content), + min_content: border_box_size, + max_content: border_box_size, } }, - Some(length) => { - let length = clamp(length); - ContentSizes { - min_content: length, - max_content: length, + None => self.expect_inline().map(|content_box_size| { + match box_sizing { + // Clamp to 'min-width' and 'max-width', which are sizing the… + BoxSizing::ContentBox => clamp(content_box_size) + pb_lengths, + BoxSizing::BorderBox => clamp(content_box_size + pb_lengths), } - }, + }), }; - let mut pbm_lengths = Length::zero(); - let mut pbm_percentages = Percentage::zero(); - let padding = style.padding(); - let border = style.border_width(); - let margin = style.margin(); - pbm_lengths += border.inline_sum(); - let mut add = |x: LengthPercentage| { - if let Some(l) = x.to_length() { - pbm_lengths += l; - } - if let Some(p) = x.to_percentage() { - pbm_percentages += p; - } - }; - add(padding.inline_start); - add(padding.inline_end); - margin.inline_start.non_auto().map(&mut add); - margin.inline_end.non_auto().map(&mut add); - - outer.min_content += pbm_lengths; - outer.max_content += pbm_lengths; - + let outer = border_box_sizes.map(|s| s + m_lengths); (outer, pbm_percentages) } diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs index 60399f33f35..41ccde51de4 100644 --- a/components/layout_2020/style_ext.rs +++ b/components/layout_2020/style_ext.rs @@ -7,12 +7,14 @@ use crate::ContainingBlock; use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode; use style::computed_values::position::T as ComputedPosition; use style::computed_values::transform_style::T as ComputedTransformStyle; +use style::properties::longhands::box_sizing::computed_value::T as BoxSizing; use style::properties::ComputedValues; use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto}; use style::values::computed::{NonNegativeLengthPercentage, Size}; use style::values::generics::box_::Perspective; use style::values::generics::length::MaxSize; use style::values::specified::box_ as stylo; +use style::Zero; #[derive(Clone, Copy, Eq, PartialEq)] pub(crate) enum Display { @@ -61,6 +63,21 @@ pub(crate) trait ComputedValuesExt { fn box_size(&self) -> flow_relative::Vec2; fn min_box_size(&self) -> flow_relative::Vec2; fn max_box_size(&self) -> flow_relative::Vec2>; + fn content_box_size( + &self, + containing_block: &ContainingBlock, + pbm: &PaddingBorderMargin, + ) -> flow_relative::Vec2; + fn content_min_box_size( + &self, + containing_block: &ContainingBlock, + pbm: &PaddingBorderMargin, + ) -> flow_relative::Vec2; + fn content_max_box_size( + &self, + containing_block: &ContainingBlock, + pbm: &PaddingBorderMargin, + ) -> flow_relative::Vec2>; fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin; fn padding(&self) -> flow_relative::Sides; fn border_width(&self) -> flow_relative::Sides; @@ -93,7 +110,6 @@ impl ComputedValuesExt for ComputedValues { !a.is_auto() && !b.is_auto() } - #[inline] fn box_offsets(&self) -> flow_relative::Sides { let position = self.get_position(); flow_relative::Sides::from_physical( @@ -107,7 +123,6 @@ impl ComputedValuesExt for ComputedValues { ) } - #[inline] fn box_size(&self) -> flow_relative::Vec2 { let position = self.get_position(); flow_relative::Vec2::from_physical_size( @@ -119,7 +134,6 @@ impl ComputedValuesExt for ComputedValues { ) } - #[inline] fn min_box_size(&self) -> flow_relative::Vec2 { let position = self.get_position(); flow_relative::Vec2::from_physical_size( @@ -131,7 +145,6 @@ impl ComputedValuesExt for ComputedValues { ) } - #[inline] fn max_box_size(&self) -> flow_relative::Vec2> { let unwrap = |max_size: MaxSize| match max_size { MaxSize::LengthPercentage(length) => MaxSize::LengthPercentage(length.0), @@ -147,6 +160,70 @@ impl ComputedValuesExt for ComputedValues { ) } + fn content_box_size( + &self, + containing_block: &ContainingBlock, + pbm: &PaddingBorderMargin, + ) -> flow_relative::Vec2 { + let box_size = self.box_size().percentages_relative_to(containing_block); + match self.get_position().box_sizing { + BoxSizing::ContentBox => box_size, + BoxSizing::BorderBox => flow_relative::Vec2 { + // These may be negative, but will later be clamped by `min-width`/`min-height` + // which is clamped to zero. + inline: box_size.inline.map(|i| i - pbm.padding_border_sums.inline), + block: box_size.block.map(|b| b - pbm.padding_border_sums.block), + }, + } + } + + fn content_min_box_size( + &self, + containing_block: &ContainingBlock, + pbm: &PaddingBorderMargin, + ) -> flow_relative::Vec2 { + let min_box_size = self + .min_box_size() + .percentages_relative_to(containing_block); + match self.get_position().box_sizing { + BoxSizing::ContentBox => min_box_size, + BoxSizing::BorderBox => flow_relative::Vec2 { + // Clamp to zero to make sure the used size components are non-negative + inline: min_box_size + .inline + .map(|i| (i - pbm.padding_border_sums.inline).max(Length::zero())), + block: min_box_size + .block + .map(|b| (b - pbm.padding_border_sums.block).max(Length::zero())), + }, + } + } + + fn content_max_box_size( + &self, + containing_block: &ContainingBlock, + pbm: &PaddingBorderMargin, + ) -> flow_relative::Vec2> { + let max_box_size = self + .max_box_size() + .percentages_relative_to(containing_block); + match self.get_position().box_sizing { + BoxSizing::ContentBox => max_box_size, + BoxSizing::BorderBox => { + // This may be negative, but will later be clamped by `min-width` + // which itself is clamped to zero. + flow_relative::Vec2 { + inline: max_box_size + .inline + .map(|i| i - pbm.padding_border_sums.inline), + block: max_box_size + .block + .map(|b| b - pbm.padding_border_sums.block), + } + }, + } + } + fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin { let cbis = containing_block.inline_size; let padding = self.padding().percentages_relative_to(cbis); @@ -313,9 +390,7 @@ impl From for Display { fn size_to_length(size: Size) -> LengthPercentageOrAuto { match size { - Size::LengthPercentage(length) => { - LengthPercentageOrAuto::LengthPercentage(length.0.clone()) - }, + Size::LengthPercentage(length) => LengthPercentageOrAuto::LengthPercentage(length.0), Size::Auto => LengthPercentageOrAuto::Auto, } } diff --git a/components/style/properties/longhands/position.mako.rs b/components/style/properties/longhands/position.mako.rs index 6b7ef1b8f4e..660da1a5a37 100644 --- a/components/style/properties/longhands/position.mako.rs +++ b/components/style/properties/longhands/position.mako.rs @@ -313,7 +313,6 @@ ${helpers.single_keyword( "box-sizing", "content-box border-box", engines="gecko servo-2013 servo-2020", - servo_2020_pref="layout.2020.unimplemented", extra_prefixes="moz:layout.css.prefixes.box-sizing webkit", spec="https://drafts.csswg.org/css-ui/#propdef-box-sizing", gecko_enum_prefix="StyleBoxSizing",