diff options
-rw-r--r-- | components/layout/display_list/builder.rs | 8 | ||||
-rw-r--r-- | components/layout_2020/display_list.rs | 216 | ||||
-rw-r--r-- | components/layout_2020/flow/root.rs | 7 | ||||
-rw-r--r-- | components/layout_thread_2020/lib.rs | 4 | ||||
-rw-r--r-- | components/style/traversal.rs | 4 | ||||
-rw-r--r-- | components/style/values/computed/image.rs | 10 | ||||
-rw-r--r-- | components/style/values/generics/image.rs | 12 | ||||
-rw-r--r-- | components/style/values/specified/image.rs | 42 |
8 files changed, 179 insertions, 124 deletions
diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index 542ab95172a..56748d55d2b 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -800,11 +800,9 @@ impl Fragment { ); } }, - Image::Rect(_) => { - // TODO: Implement `-moz-image-rect` - }, - Image::Element(_) => { - // TODO: Implement `-moz-element` + Image::Rect(ref rect) => { + // This is a (boxed) empty enum on non-Gecko + match **rect {} }, } } diff --git a/components/layout_2020/display_list.rs b/components/layout_2020/display_list.rs index 6bd6c60a663..31492b5ca47 100644 --- a/components/layout_2020/display_list.rs +++ b/components/layout_2020/display_list.rs @@ -21,6 +21,11 @@ type HitInfo = Option<ItemTag>; pub struct DisplayListBuilder { current_space_and_clip: wr::SpaceAndClipInfo, pub wr: wr::DisplayListBuilder, + + /// Contentful paint, for the purpose of + /// https://w3c.github.io/paint-timing/#first-contentful-paint + /// (i.e. the display list contains items of type text, + /// image, non-white canvas or SVG). Used by metrics. pub is_contentful: bool, } @@ -33,21 +38,13 @@ impl DisplayListBuilder { } } - fn common_properties( - &self, - clip_rect: units::LayoutRect, - hit_info: HitInfo, - ) -> wr::CommonItemProperties { - wr::CommonItemProperties { - clip_rect, - clip_id: self.current_space_and_clip.clip_id, - spatial_id: self.current_space_and_clip.spatial_id, - hit_info, - // TODO(gw): Make use of the WR backface visibility functionality. - flags: wr::PrimitiveFlags::default(), - } + fn common_properties(&self, clip_rect: units::LayoutRect) -> wr::CommonItemProperties { + // TODO(gw): Make use of the WR backface visibility functionality. + wr::CommonItemProperties::new(clip_rect, self.current_space_and_clip) } + // FIXME: use this for the `overflow` property or anything else that clips an entire subtree. + #[allow(unused)] fn clipping_and_scrolling_scope<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { let previous = self.current_space_and_clip; let result = f(self); @@ -56,32 +53,27 @@ impl DisplayListBuilder { } } -/// Contentful paint, for the purpose of -/// https://w3c.github.io/paint-timing/#first-contentful-paint -/// (i.e. the display list contains items of type text, -/// image, non-white canvas or SVG). Used by metrics. -pub struct IsContentful(pub bool); - impl Fragment { pub(crate) fn build_display_list( &self, builder: &mut DisplayListBuilder, - is_contentful: &mut IsContentful, containing_block: &Rect<Length>, ) { match self { - Fragment::Box(b) => b.build_display_list(builder, is_contentful, containing_block), + Fragment::Box(b) => { + BuilderForBoxFragment::new(b, containing_block).build(builder, containing_block) + }, Fragment::Anonymous(a) => { let rect = a .rect .to_physical(a.mode, containing_block) .translate(&containing_block.top_left); for child in &a.children { - child.build_display_list(builder, is_contentful, &rect) + child.build_display_list(builder, &rect) } }, Fragment::Text(t) => { - is_contentful.0 = true; + builder.is_contentful = true; let rect = t .rect .to_physical(t.parent_style.writing_mode, containing_block) @@ -92,8 +84,8 @@ impl Fragment { if glyphs.is_empty() { return; } - let hit_info = hit_info(&t.parent_style, t.tag, Cursor::Text); - let common = builder.common_properties(rect.clone().into(), hit_info); + let mut common = builder.common_properties(rect.clone().into()); + common.hit_info = hit_info(&t.parent_style, t.tag, Cursor::Text); let color = t.parent_style.clone_color(); builder .wr @@ -101,13 +93,12 @@ impl Fragment { }, Fragment::Image(i) => { use style::computed_values::image_rendering::T as ImageRendering; - is_contentful.0 = true; + builder.is_contentful = true; let rect = i .rect .to_physical(i.style.writing_mode, containing_block) .translate(&containing_block.top_left); - let hit_info = None; - let common = builder.common_properties(rect.clone().into(), hit_info); + let common = builder.common_properties(rect.clone().into()); builder.wr.push_image( &common, rect.into(), @@ -125,89 +116,114 @@ impl Fragment { } } -impl BoxFragment { - fn build_display_list( - &self, - builder: &mut DisplayListBuilder, - is_contentful: &mut IsContentful, - containing_block: &Rect<Length>, - ) { - let border_rect = self +struct BuilderForBoxFragment<'a> { + fragment: &'a BoxFragment, + border_rect: units::LayoutRect, + border_radius: wr::BorderRadius, + + // Outer `Option` is `None`: not initialized yet + // Inner `Option` is `None`: no border radius, no need to clip + border_edge_clip_id: Option<Option<wr::ClipId>>, +} + +impl<'a> BuilderForBoxFragment<'a> { + fn new(fragment: &'a BoxFragment, containing_block: &Rect<Length>) -> Self { + let border_rect: units::LayoutRect = fragment .border_rect() - .to_physical(self.style.writing_mode, containing_block) + .to_physical(fragment.style.writing_mode, containing_block) .translate(&containing_block.top_left) .into(); - let hit_info = hit_info(&self.style, self.tag, Cursor::Default); - let border_radius = self.border_radius(&border_rect); - - self.background_display_items(builder, hit_info, border_rect, &border_radius); - self.border_display_items(builder, hit_info, border_rect, border_radius); - let content_rect = self - .content_rect - .to_physical(self.style.writing_mode, containing_block) - .translate(&containing_block.top_left); - for child in &self.children { - child.build_display_list(builder, is_contentful, &content_rect) - } - } - fn border_radius(&self, border_rect: &units::LayoutRect) -> wr::BorderRadius { - let resolve = |radius: &LengthPercentage, box_size: f32| { - radius.percentage_relative_to(Length::new(box_size)).px() - }; - let corner = |corner: &style::values::computed::BorderCornerRadius| { - Size2D::new( - resolve(&corner.0.width.0, border_rect.size.width), - resolve(&corner.0.height.0, border_rect.size.height), - ) + let border_radius = { + let resolve = |radius: &LengthPercentage, box_size: f32| { + radius.percentage_relative_to(Length::new(box_size)).px() + }; + let corner = |corner: &style::values::computed::BorderCornerRadius| { + Size2D::new( + resolve(&corner.0.width.0, border_rect.size.width), + resolve(&corner.0.height.0, border_rect.size.height), + ) + }; + let b = fragment.style.get_border(); + wr::BorderRadius { + top_left: corner(&b.border_top_left_radius), + top_right: corner(&b.border_top_right_radius), + bottom_right: corner(&b.border_bottom_right_radius), + bottom_left: corner(&b.border_bottom_left_radius), + } }; - let b = self.style.get_border(); - wr::BorderRadius { - top_left: corner(&b.border_top_left_radius), - top_right: corner(&b.border_top_right_radius), - bottom_right: corner(&b.border_bottom_right_radius), - bottom_left: corner(&b.border_bottom_left_radius), + + Self { + fragment, + border_rect, + border_radius, + border_edge_clip_id: None, } } - fn background_display_items( - &self, + fn with_border_edge_clip( + &mut self, builder: &mut DisplayListBuilder, - hit_info: HitInfo, - border_rect: units::LayoutRect, - border_radius: &wr::BorderRadius, + common: &mut wr::CommonItemProperties, ) { + let border_radius = &self.border_radius; + let border_rect = &self.border_rect; + let initialized = self.border_edge_clip_id.get_or_insert_with(|| { + if border_radius.is_zero() { + None + } else { + Some(builder.wr.define_clip( + &builder.current_space_and_clip, + *border_rect, + Some(wr::ComplexClipRegion { + rect: *border_rect, + radii: *border_radius, + mode: wr::ClipMode::Clip, + }), + None, + )) + } + }); + if let Some(clip_id) = *initialized { + common.clip_id = clip_id + } + } + + fn build(&mut self, builder: &mut DisplayListBuilder, containing_block: &Rect<Length>) { + let hit_info = hit_info(&self.fragment.style, self.fragment.tag, Cursor::Default); + if hit_info.is_some() { + let mut common = builder.common_properties(self.border_rect); + common.hit_info = hit_info; + self.with_border_edge_clip(builder, &mut common); + builder.wr.push_hit_test(&common) + } + + self.background_display_items(builder); + self.border_display_items(builder); + let content_rect = self + .fragment + .content_rect + .to_physical(self.fragment.style.writing_mode, containing_block) + .translate(&containing_block.top_left); + for child in &self.fragment.children { + child.build_display_list(builder, &content_rect) + } + } + + fn background_display_items(&mut self, builder: &mut DisplayListBuilder) { let background_color = self + .fragment .style - .resolve_color(self.style.clone_background_color()); - if background_color.alpha > 0 || hit_info.is_some() { - builder.clipping_and_scrolling_scope(|builder| { - if !border_radius.is_zero() { - builder.current_space_and_clip.clip_id = builder.wr.define_clip( - &builder.current_space_and_clip, - border_rect, - Some(wr::ComplexClipRegion { - rect: border_rect, - radii: *border_radius, - mode: wr::ClipMode::Clip, - }), - None, - ); - } - let common = builder.common_properties(border_rect, hit_info); - builder.wr.push_rect(&common, rgba(background_color)) - }); + .resolve_color(self.fragment.style.clone_background_color()); + if background_color.alpha > 0 { + let mut common = builder.common_properties(self.border_rect); + self.with_border_edge_clip(builder, &mut common); + builder.wr.push_rect(&common, rgba(background_color)) } } - fn border_display_items( - &self, - builder: &mut DisplayListBuilder, - hit_info: HitInfo, - border_rect: units::LayoutRect, - radius: wr::BorderRadius, - ) { - let b = self.style.get_border(); + fn border_display_items(&mut self, builder: &mut DisplayListBuilder) { + let b = self.fragment.style.get_border(); let widths = SideOffsets2D::new( b.border_top_width.px(), b.border_right_width.px(), @@ -218,7 +234,7 @@ impl BoxFragment { return; } let side = |style, color| wr::BorderSide { - color: rgba(self.style.resolve_color(color)), + color: rgba(self.fragment.style.resolve_color(color)), style: match style { BorderStyle::None => wr::BorderStyle::None, BorderStyle::Solid => wr::BorderStyle::Solid, @@ -232,18 +248,18 @@ impl BoxFragment { BorderStyle::Outset => wr::BorderStyle::Outset, }, }; - let common = builder.common_properties(border_rect, hit_info); + let common = builder.common_properties(self.border_rect); let details = wr::BorderDetails::Normal(wr::NormalBorder { top: side(b.border_top_style, b.border_top_color), right: side(b.border_right_style, b.border_right_color), bottom: side(b.border_bottom_style, b.border_bottom_color), left: side(b.border_left_style, b.border_left_color), - radius, + radius: self.border_radius, do_aa: true, }); builder .wr - .push_border(&common, border_rect, widths, details) + .push_border(&common, self.border_rect, widths, details) } } diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index fda5eff5116..6d6a5759428 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -3,7 +3,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::context::LayoutContext; -use crate::display_list::IsContentful; use crate::dom_traversal::{Contents, NodeExt}; use crate::flow::construct::ContainsFloats; use crate::flow::float::FloatBox; @@ -140,7 +139,7 @@ impl FragmentTreeRoot { &self, builder: &mut crate::display_list::DisplayListBuilder, viewport_size: webrender_api::units::LayoutSize, - ) -> IsContentful { + ) { let containing_block = geom::physical::Rect { top_left: geom::physical::Vec2 { x: Length::zero(), @@ -151,10 +150,8 @@ impl FragmentTreeRoot { y: Length::new(viewport_size.height), }, }; - let mut is_contentful = IsContentful(false); for fragment in &self.0 { - fragment.build_display_list(builder, &mut is_contentful, &containing_block) + fragment.build_display_list(builder, &containing_block) } - is_contentful } } diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index bb9f614831c..5df365e889b 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -1290,7 +1290,7 @@ impl LayoutThread { self.viewport_size.height.to_f32_px(), )); let mut display_list = DisplayListBuilder::new(self.id.to_webrender(), viewport_size); - let is_contentful = fragment_tree.build_display_list(&mut display_list, viewport_size); + fragment_tree.build_display_list(&mut display_list, viewport_size); debug!("Layout done!"); @@ -1302,7 +1302,7 @@ impl LayoutThread { // sending the display list to WebRender in order to set time related // Progressive Web Metrics. self.paint_time_metrics - .maybe_observe_paint_time(self, epoch, is_contentful.0); + .maybe_observe_paint_time(self, epoch, display_list.is_contentful); self.webrender_api.send_display_list( self.webrender_document, diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 360f5b6a25c..f73a533b718 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -679,7 +679,7 @@ where element.finish_restyle(context, data, new_styles, important_rules_changed) } -#[cfg(feature = "servo")] +#[cfg(feature = "servo-layout-2013")] fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData) where E: TElement, @@ -719,7 +719,7 @@ where } } -#[cfg(feature = "gecko")] +#[cfg(not(feature = "servo-layout-2013"))] fn notify_paint_worklet<E>(_context: &StyleContext<E>, _data: &ElementData) where E: TElement, diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index 841f3293c1a..bd59acac547 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -9,10 +9,11 @@ use crate::values::computed::position::Position; use crate::values::computed::url::ComputedImageUrl; +#[cfg(feature = "gecko")] +use crate::values::computed::NumberOrPercentage; use crate::values::computed::{Angle, Color, Context}; use crate::values::computed::{ - LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage, NumberOrPercentage, - ToComputedValue, + LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage, ToComputedValue, }; use crate::values::generics::image::{self as generic, GradientCompatMode}; use crate::values::specified::image::LineDirection as SpecifiedLineDirection; @@ -63,8 +64,13 @@ pub type GradientItem = generic::GenericGradientItem<Color, LengthPercentage>; pub type ColorStop = generic::ColorStop<Color, LengthPercentage>; /// Computed values for `-moz-image-rect(...)`. +#[cfg(feature = "gecko")] pub type MozImageRect = generic::MozImageRect<NumberOrPercentage, ComputedImageUrl>; +/// Empty enum on non-gecko +#[cfg(not(feature = "gecko"))] +pub type MozImageRect = crate::values::specified::image::MozImageRect; + impl generic::LineDirection for LineDirection { fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool { match *self { diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs index ab2a906ae10..a6b45bc82ad 100644 --- a/components/style/values/generics/image.rs +++ b/components/style/values/generics/image.rs @@ -53,17 +53,24 @@ impl<I> ImageLayer<I> { pub enum GenericImage<Gradient, MozImageRect, ImageUrl> { /// A `<url()>` image. Url(ImageUrl), + /// A `<gradient>` image. Gradients are rather large, and not nearly as /// common as urls, so we box them here to keep the size of this enum sane. Gradient(Box<Gradient>), + /// A `-moz-image-rect` image. Also fairly large and rare. + // not cfg’ed out on non-Gecko to avoid `error[E0392]: parameter `MozImageRect` is never used` + // Instead we make MozImageRect an empty enum Rect(Box<MozImageRect>), + /// A `-moz-element(# <element-id>)` + #[cfg(feature = "gecko")] #[css(function = "-moz-element")] Element(Atom), + /// A paint worklet image. /// <https://drafts.css-houdini.org/css-paint-api/> - #[cfg(feature = "servo")] + #[cfg(feature = "servo-layout-2013")] PaintWorklet(PaintWorklet), } @@ -323,8 +330,9 @@ where Image::Url(ref url) => url.to_css(dest), Image::Gradient(ref gradient) => gradient.to_css(dest), Image::Rect(ref rect) => rect.to_css(dest), - #[cfg(feature = "servo")] + #[cfg(feature = "servo-layout-2013")] Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest), + #[cfg(feature = "gecko")] Image::Element(ref selector) => { dest.write_str("-moz-element(#")?; serialize_atom_identifier(selector, dest)?; diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 126a4cc69d1..14a2a9ad9c8 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -9,7 +9,6 @@ use crate::custom_properties::SpecifiedValue; use crate::parser::{Parse, ParserContext}; -use crate::stylesheets::CorsMode; use crate::values::generics::image::PaintWorklet; use crate::values::generics::image::{ self as generic, Circle, Ellipse, GradientCompatMode, ShapeExtent, @@ -119,8 +118,24 @@ pub type ColorStop = generic::ColorStop<Color, LengthPercentage>; /// Specified values for `moz-image-rect` /// -moz-image-rect(<uri>, top, right, bottom, left); +#[cfg(feature = "gecko")] pub type MozImageRect = generic::MozImageRect<NumberOrPercentage, SpecifiedImageUrl>; +#[cfg(not(feature = "gecko"))] +#[derive( + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +/// Empty enum on non-Gecko +pub enum MozImageRect {} + impl Parse for Image { fn parse<'i, 't>( context: &ParserContext, @@ -132,16 +147,21 @@ impl Parse for Image { if let Ok(gradient) = input.try(|i| Gradient::parse(context, i)) { return Ok(generic::Image::Gradient(Box::new(gradient))); } - #[cfg(feature = "servo")] + #[cfg(feature = "servo-layout-2013")] { if let Ok(paint_worklet) = input.try(|i| PaintWorklet::parse(context, i)) { return Ok(generic::Image::PaintWorklet(paint_worklet)); } } - if let Ok(image_rect) = input.try(|input| MozImageRect::parse(context, input)) { - return Ok(generic::Image::Rect(Box::new(image_rect))); + #[cfg(feature = "gecko")] + { + if let Ok(image_rect) = input.try(|input| MozImageRect::parse(context, input)) { + return Ok(generic::Image::Rect(Box::new(image_rect))); + } + Ok(generic::Image::Element(Image::parse_element(input)?)) } - Ok(generic::Image::Element(Image::parse_element(input)?)) + #[cfg(not(feature = "gecko"))] + Err(input.new_error_for_next_token()) } } @@ -155,6 +175,7 @@ impl Image { } /// Parses a `-moz-element(# <element-id>)`. + #[cfg(feature = "gecko")] fn parse_element<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Atom, ParseError<'i>> { input.try(|i| i.expect_function_matching("-moz-element"))?; let location = input.current_source_location(); @@ -856,6 +877,15 @@ impl Parse for PaintWorklet { } impl Parse for MozImageRect { + #[cfg(not(feature = "gecko"))] + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result<Self, ParseError<'i>> { + Err(input.new_error_for_next_token()) + } + + #[cfg(feature = "gecko")] fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, @@ -866,7 +896,7 @@ impl Parse for MozImageRect { let url = SpecifiedImageUrl::parse_from_string( string.as_ref().to_owned(), context, - CorsMode::None, + crate::stylesheets::CorsMode::None, ); i.expect_comma()?; let top = NumberOrPercentage::parse_non_negative(context, i)?; |