diff --git a/components/layout_2020/display_list.rs b/components/layout_2020/display_list.rs index 03b1ca98535..add3c8ae05f 100644 --- a/components/layout_2020/display_list.rs +++ b/components/layout_2020/display_list.rs @@ -5,6 +5,7 @@ use crate::context::LayoutContext; use crate::fragments::{BoxFragment, Fragment}; use crate::geom::physical::{Rect, Vec2}; +use crate::replaced::IntrinsicSizes; use embedder_traits::Cursor; use euclid::{Point2D, SideOffsets2D, Size2D, Vector2D}; use gfx::text::glyph::GlyphStore; @@ -270,9 +271,17 @@ impl<'a> BuilderForBoxFragment<'a> { key: Some(key), }) = webrender_image { - self.build_background_raster_image( - builder, index, width, height, key, - ) + // FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution + let dppx = 1.0; + + let intrinsic = IntrinsicSizes { + width: Some(Length::new(width as f32 / dppx)), + height: Some(Length::new(height as f32 / dppx)), + // FIXME https://github.com/w3c/csswg-drafts/issues/4572 + ratio: Some(width as f32 / height as f32), + }; + + self.build_background_raster_image(builder, index, intrinsic, key) } } }, @@ -287,8 +296,7 @@ impl<'a> BuilderForBoxFragment<'a> { &mut self, builder: &mut DisplayListBuilder, index: usize, - intrinsic_width: u32, - intrinsic_height: u32, + intrinsic: IntrinsicSizes, key: wr::ImageKey, ) { // Our job here would be easier if WebRender’s `RepeatingImageDisplayItem` @@ -310,6 +318,7 @@ impl<'a> BuilderForBoxFragment<'a> { use style::computed_values::background_clip::single_value::T as Clip; use style::computed_values::background_origin::single_value::T as Origin; + use style::values::computed::background::BackgroundSize as Size; fn get_cyclic(values: &[T], index: usize) -> &T { &values[index % values.len()] @@ -328,21 +337,86 @@ impl<'a> BuilderForBoxFragment<'a> { Origin::BorderBox => &self.border_rect, }; - // FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution - let dppx = 1.0; + // https://drafts.csswg.org/css-backgrounds/#background-size + enum ContainOrCover { + Contain, + Cover, + } + let size_contain_or_cover = |background_size| { + let mut tile_size = positioning_area.size; + if let Some(intrinsic_ratio) = intrinsic.ratio { + let positioning_ratio = positioning_area.size.width / positioning_area.size.height; + // Whether the tile width (as opposed to height) + // is scaled to that of the positioning area + let fit_width = match background_size { + ContainOrCover::Contain => positioning_ratio <= intrinsic_ratio, + ContainOrCover::Cover => positioning_ratio > intrinsic_ratio, + }; + // The other dimension needs to be adjusted + if fit_width { + tile_size.height = tile_size.width / intrinsic_ratio + } else { + tile_size.width = tile_size.height * intrinsic_ratio + } + } + tile_size + }; + let tile_size = match get_cyclic(&b.background_size.0, index) { + Size::Contain => size_contain_or_cover(ContainOrCover::Contain), + Size::Cover => size_contain_or_cover(ContainOrCover::Cover), + Size::ExplicitSize { width, height } => { + let mut width = width.non_auto().map(|lp| { + lp.0.percentage_relative_to(Length::new(positioning_area.size.width)) + }); + let mut height = height.non_auto().map(|lp| { + lp.0.percentage_relative_to(Length::new(positioning_area.size.height)) + }); - let intrinsic_size = units::LayoutSize::new( - intrinsic_width as f32 / dppx, - intrinsic_height as f32 / dppx, - ); - // FIXME: background-size - // Size of one tile: - let stretch_size = intrinsic_size; + if width.is_none() && height.is_none() { + // Both computed values are 'auto': + // use intrinsic sizes, treating missing width or height as 'auto' + width = intrinsic.width; + height = intrinsic.height; + } + + match (width, height) { + (Some(w), Some(h)) => units::LayoutSize::new(w.px(), h.px()), + (Some(w), None) => { + let h = if let Some(intrinsic_ratio) = intrinsic.ratio { + w / intrinsic_ratio + } else if let Some(intrinsic_height) = intrinsic.height { + intrinsic_height + } else { + // Treated as 100% + Length::new(positioning_area.size.height) + }; + units::LayoutSize::new(w.px(), h.px()) + }, + (None, Some(h)) => { + let w = if let Some(intrinsic_ratio) = intrinsic.ratio { + h * intrinsic_ratio + } else if let Some(intrinsic_width) = intrinsic.width { + intrinsic_width + } else { + // Treated as 100% + Length::new(positioning_area.size.width) + }; + units::LayoutSize::new(w.px(), h.px()) + }, + // Both comptued values were 'auto', and neither intrinsic size is present + (None, None) => size_contain_or_cover(ContainOrCover::Contain), + } + }, + }; + + // FIXME: background-repeat let tile_spacing = units::LayoutSize::zero(); - let tile_stride = stretch_size + tile_spacing; + + let tile_stride = tile_size + tile_spacing; // FIXME: background-position let positioned_tile_origin = positioning_area.origin; + let offset = positioned_tile_origin - clipping_area.origin; let first_tile_origin = positioned_tile_origin - Vector2D::new( @@ -359,7 +433,7 @@ impl<'a> BuilderForBoxFragment<'a> { builder.wr.push_repeating_image( &common, bounds, - stretch_size, + tile_size, tile_spacing, image_rendering(self.fragment.style.clone_image_rendering()), wr::AlphaType::PremultipliedAlpha, diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 72071dbf684..f27d3af9abf 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -20,21 +20,25 @@ use style::Zero; #[derive(Debug)] pub(crate) struct ReplacedContent { pub kind: ReplacedContentKind, + intrinsic: IntrinsicSizes, +} - /// * 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, +/// * 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. +#[derive(Debug)] +pub(crate) struct IntrinsicSizes { + pub width: Option, + pub height: Option, + pub ratio: Option, } #[derive(Debug)] @@ -55,10 +59,12 @@ impl ReplacedContent { let height = (intrinsic_size_in_dots.y as CSSFloat) / dppx; return Some(Self { kind: ReplacedContentKind::Image(image), - 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), + intrinsic: IntrinsicSizes { + width: Some(Length::new(width)), + height: Some(Length::new(height)), + // FIXME https://github.com/w3c/csswg-drafts/issues/4572 + ratio: Some(width / height), + }, }); } None @@ -66,8 +72,8 @@ impl ReplacedContent { fn flow_relative_intrinsic_size(&self, style: &ComputedValues) -> Vec2> { let intrinsic_size = physical::Vec2 { - x: self.intrinsic_width, - y: self.intrinsic_height, + x: self.intrinsic.width, + y: self.intrinsic.height, }; intrinsic_size.size_to_flow_relative(style.writing_mode) } @@ -76,7 +82,7 @@ impl ReplacedContent { &self, style: &ComputedValues, ) -> Option { - self.intrinsic_ratio.map(|width_over_height| { + self.intrinsic.ratio.map(|width_over_height| { if style.writing_mode.is_vertical() { 1. / width_over_height } else { diff --git a/components/style/properties/longhands/background.mako.rs b/components/style/properties/longhands/background.mako.rs index bebc985f0e1..cc8d6c0f34c 100644 --- a/components/style/properties/longhands/background.mako.rs +++ b/components/style/properties/longhands/background.mako.rs @@ -92,7 +92,7 @@ ${helpers.single_keyword( ${helpers.predefined_type( "background-size", "BackgroundSize", - engines="gecko servo-2013", + engines="gecko servo-2013 servo-2020", initial_value="computed::BackgroundSize::auto()", initial_specified_value="specified::BackgroundSize::auto()", spec="https://drafts.csswg.org/css-backgrounds/#the-background-size",