diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 83973900473..9e531bf6dab 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::cell::LazyCell; use std::fmt; use std::sync::{Arc, Mutex}; @@ -19,7 +20,6 @@ use style::logical_geometry::{Direction, WritingMode}; use style::properties::ComputedValues; use style::servo::url::ComputedUrl; use style::values::computed::image::Image as ComputedImage; -use style::values::generics::length::GenericLengthPercentageOrAuto; use style::values::CSSFloat; use style::Zero; use url::Url; @@ -241,9 +241,9 @@ impl ReplacedContent { } } - fn flow_relative_intrinsic_size(&self, writing_mode: WritingMode) -> LogicalVec2> { - let intrinsic_size = PhysicalSize::new(self.natural_size.width, self.natural_size.height); - LogicalVec2::from_physical_size(&intrinsic_size, writing_mode) + fn flow_relative_natural_size(&self, writing_mode: WritingMode) -> LogicalVec2> { + let natural_size = PhysicalSize::new(self.natural_size.width, self.natural_size.height); + LogicalVec2::from_physical_size(&natural_size, writing_mode) } fn inline_size_over_block_size_intrinsic_ratio( @@ -280,7 +280,7 @@ impl ReplacedContent { let writing_mode = containing_block_for_children.style.writing_mode; InlineContentSizesResult { sizes: self - .flow_relative_intrinsic_size(writing_mode) + .flow_relative_natural_size(writing_mode) .inline .unwrap_or_else(|| { Self::flow_relative_default_object_size(writing_mode).inline @@ -491,170 +491,68 @@ impl ReplacedContent { min_box_size: LogicalVec2, max_box_size: LogicalVec2>, ) -> LogicalVec2 { + let box_size = box_size.map(|size| size.non_auto()); + let max_box_size = max_box_size.map(|max_size| max_size.unwrap_or(MAX_AU)); let writing_mode = style.writing_mode; - let intrinsic_size = self.flow_relative_intrinsic_size(writing_mode); - let default_object_size = || Self::flow_relative_default_object_size(writing_mode); - let intrinsic_ratio = self.preferred_aspect_ratio(&containing_block.into(), style); + let natural_size = LazyCell::new(|| self.flow_relative_natural_size(writing_mode)); + let default_object_size = + LazyCell::new(|| Self::flow_relative_default_object_size(writing_mode)); + let ratio = self.preferred_aspect_ratio(&containing_block.into(), style); - let get_tentative_size = |LogicalVec2 { inline, block }| -> LogicalVec2 { - match (inline, block) { - (AuOrAuto::LengthPercentage(inline), AuOrAuto::LengthPercentage(block)) => { - LogicalVec2 { inline, block } - }, - (AuOrAuto::LengthPercentage(inline), AuOrAuto::Auto) => { - let block = if let Some(ratio) = intrinsic_ratio { - ratio.compute_dependent_size(Direction::Block, inline) - } else if let Some(block) = intrinsic_size.block { - block - } else { - default_object_size().block - }; - LogicalVec2 { inline, block } - }, - (AuOrAuto::Auto, AuOrAuto::LengthPercentage(block)) => { - let inline = if let Some(ratio) = intrinsic_ratio { - ratio.compute_dependent_size(Direction::Inline, block) - } else if let Some(inline) = intrinsic_size.inline { - inline - } else { - default_object_size().inline - }; - LogicalVec2 { inline, block } - }, - (AuOrAuto::Auto, AuOrAuto::Auto) => { - let inline_size = - match (intrinsic_size.inline, intrinsic_size.block, intrinsic_ratio) { - (Some(inline), _, _) => inline, - (None, Some(block), Some(ratio)) => { - // “used height” in CSS 2 is always gonna be the intrinsic one, - // since it is available. - ratio.compute_dependent_size(Direction::Inline, block) - }, - // 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(ratio) = intrinsic_ratio { - // “used width” in CSS 2 is what we just computed above - ratio.compute_dependent_size(Direction::Block, inline_size) - } else { - default_object_size().block - }; - LogicalVec2 { - inline: inline_size, - block: 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” - if let (AuOrAuto::Auto, AuOrAuto::Auto, Some(ratio)) = - (box_size.inline, box_size.block, intrinsic_ratio) - { - let tentative_size = get_tentative_size(box_size); - let max_box_size = max_box_size.map(|max_size| max_size.unwrap_or(MAX_AU)); - // This is a simplification of the CSS2 algorithm in a way that properly handles `aspect-ratio`. - // We transfer min and max constraints from the other axis, and apply them in addition to - // non-transferred min and max constraints. In case of conflict, - // - Non-transferred constraints take precedence over transferred ones. - // - Min constraints take precedence over max ones from the same axis. - // - // - let inline = tentative_size.inline.clamp_between_extremums( - ratio - .compute_dependent_size(Direction::Inline, min_box_size.block) - .clamp_between_extremums(min_box_size.inline, Some(max_box_size.inline)), - Some( - ratio - .compute_dependent_size(Direction::Inline, max_box_size.block) - .min(max_box_size.inline), - ), - ); - let block = tentative_size.block.clamp_between_extremums( - ratio - .compute_dependent_size(Direction::Block, min_box_size.inline) - .clamp_between_extremums(min_box_size.block, Some(max_box_size.block)), - Some( - ratio - .compute_dependent_size(Direction::Block, max_box_size.inline) - .min(max_box_size.block), - ), - ); - return LogicalVec2 { inline, block }; - } - - // https://drafts.csswg.org/css2/#min-max-widths "The following algorithm describes how the two properties - // influence the used value of the width property: - // - // 1. The tentative used width is calculated (without min-width and max-width) following the rules under - // "Calculating widths and margins" above. - // 2. If the tentative used width is greater than max-width, the rules above are applied again, but this time - // using the computed value of max-width as the computed value for width. - // 3. If the resulting width is smaller than min-width, the rules above are applied again, but this time using - // the value of min-width as the computed value for width." - let mut tentative_size = get_tentative_size(box_size); - - // Create an inline/block size vector from the given clamped inline and block sizes if they are provided, - // falling back to the regular box size if they are not - let size_from_maybe_clamped = - |(clamped_inline, clamped_block): (Option, Option)| { - let clamped_inline = clamped_inline - .map(GenericLengthPercentageOrAuto::LengthPercentage) - .unwrap_or(box_size.inline); - let clamped_block = clamped_block - .map(GenericLengthPercentageOrAuto::LengthPercentage) - .unwrap_or(box_size.block); - LogicalVec2 { - inline: clamped_inline, - block: clamped_block, + // This is a simplification of the CSS2 algorithm in a way that properly handles `aspect-ratio`. + // Each axis can have preferred, min and max sizing constraints, plus constraints transferred + // from the other axis if there is an aspect ratio, plus a natural and default size. + // In case of conflict, the order of precedence (from highest to lowest) is: + // 1. Non-transferred min constraint + // 2. Non-transferred max constraint + // 3. Non-transferred preferred constraint + // 4. Transferred min constraint + // 5. Transferred max constraint + // 6. Transferred preferred constraint + // 7. Natural size + // 8. Default object size + // + // + box_size.map_inline_and_block_axes( + |inline_size| { + let mut min = min_box_size.inline; + let mut max = max_box_size.inline; + if let Some(ratio) = ratio.filter(|_| inline_size.is_none()) { + min = ratio + .compute_dependent_size(Direction::Inline, min_box_size.block) + .clamp_between_extremums(min, Some(max)); + max.min_assign( + ratio.compute_dependent_size(Direction::Inline, max_box_size.block), + ); } - }; - - let clamped_max = ( - max_box_size - .inline - .filter(|max_inline_size| tentative_size.inline > *max_inline_size), - max_box_size - .block - .filter(|max_block_size| tentative_size.block > *max_block_size), - ); - - if clamped_max.0.is_some() || clamped_max.1.is_some() { - tentative_size = get_tentative_size(size_from_maybe_clamped(clamped_max)); - } - - let clamped_min = ( - Some(min_box_size.inline) - .filter(|min_inline_size| tentative_size.inline < *min_inline_size), - Some(min_box_size.block) - .filter(|min_block_size| tentative_size.block < *min_block_size), - ); - - if clamped_min.0.is_some() || clamped_min.1.is_some() { - tentative_size = get_tentative_size(size_from_maybe_clamped(clamped_min)); - } - - tentative_size + inline_size + .or_else(|| { + Some(ratio?.compute_dependent_size(Direction::Inline, box_size.block?)) + }) + .or_else(|| natural_size.inline) + .unwrap_or_else(|| default_object_size.inline) + .clamp_between_extremums(min, Some(max)) + }, + |block_size| { + let mut min = min_box_size.block; + let mut max = max_box_size.block; + if let Some(ratio) = ratio.filter(|_| block_size.is_none()) { + min = ratio + .compute_dependent_size(Direction::Block, min_box_size.inline) + .clamp_between_extremums(min, Some(max)); + max.min_assign( + ratio.compute_dependent_size(Direction::Block, max_box_size.inline), + ); + } + block_size + .or_else(|| { + Some(ratio?.compute_dependent_size(Direction::Block, box_size.inline?)) + }) + .or_else(|| natural_size.block) + .unwrap_or_else(|| default_object_size.block) + .clamp_between_extremums(min, Some(max)) + }, + ) } } diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json index 2933bbcf217..47b49c3fd01 100644 --- a/tests/wpt/meta/MANIFEST.json +++ b/tests/wpt/meta/MANIFEST.json @@ -687108,7 +687108,7 @@ ] ], "video-intrinsic-width-height.html": [ - "74989be5213d31dd6eef9c913163d370ffe722f4", + "c66a79344cd9deafe42772b05ee2e6c6cc3392d2", [ null, {} diff --git a/tests/wpt/meta/css/css-position/position-absolute-replaced-minmax.html.ini b/tests/wpt/meta/css/css-position/position-absolute-replaced-minmax.html.ini index 5040d8ec3c8..2a1431da4a4 100644 --- a/tests/wpt/meta/css/css-position/position-absolute-replaced-minmax.html.ini +++ b/tests/wpt/meta/css/css-position/position-absolute-replaced-minmax.html.ini @@ -1,10 +1,4 @@ [position-absolute-replaced-minmax.html] - [minmax replaced IFRAME 10] - expected: FAIL - - [minmax replaced IFRAME 11] - expected: FAIL - [minmax replaced IMG svg 23] expected: FAIL diff --git a/tests/wpt/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-intrinsic-width-height.html b/tests/wpt/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-intrinsic-width-height.html index 74989be5213..c66a79344cd 100644 --- a/tests/wpt/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-intrinsic-width-height.html +++ b/tests/wpt/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-intrinsic-width-height.html @@ -25,7 +25,7 @@