Bug 1397614 part 1. Box gradients and rects in Image. r=xidorn
Gradients and rects are rare, and large. Image is much smaller with them boxed.
MozReview-Commit-ID: 2Een72yFFRL
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -146,18 +146,18 @@ impl Angle {
}
}
}
impl nsStyleImage {
/// Set a given Servo `Image` value into this `nsStyleImage`.
pub fn set(&mut self, image: Image) {
match image {
- GenericImage::Gradient(gradient) => {
- self.set_gradient(gradient)
+ GenericImage::Gradient(boxed_gradient) => {
+ self.set_gradient(*boxed_gradient)
},
GenericImage::Url(ref url) => {
unsafe {
Gecko_SetLayerImageImageValue(self, url.image_value.clone().unwrap().get());
}
},
GenericImage::Rect(ref image_rect) => {
unsafe {
@@ -394,17 +394,17 @@ impl nsStyleImage {
Some(GenericImage::Url(url))
} else {
let ref rect = *self.mCropRect.mPtr;
match (NumberOrPercentage::from_gecko_style_coord(&rect.data_at(0)),
NumberOrPercentage::from_gecko_style_coord(&rect.data_at(1)),
NumberOrPercentage::from_gecko_style_coord(&rect.data_at(2)),
NumberOrPercentage::from_gecko_style_coord(&rect.data_at(3))) {
(Some(top), Some(right), Some(bottom), Some(left)) =>
- Some(GenericImage::Rect(MozImageRect { url, top, right, bottom, left } )),
+ Some(GenericImage::Rect(Box::new(MozImageRect { url, top, right, bottom, left } ))),
_ => {
debug_assert!(false, "mCropRect could not convert to NumberOrPercentage");
None
}
}
}
},
nsStyleImageType::eStyleImageType_Gradient => {
@@ -423,17 +423,17 @@ impl nsStyleImage {
use gecko_bindings::bindings::Gecko_GetURLValue;
let url_value = Gecko_GetURLValue(self);
let mut url = SpecifiedUrl::from_url_value_data(url_value.as_ref().unwrap())
.expect("Could not convert to SpecifiedUrl");
url.build_image_value();
url
}
- unsafe fn get_gradient(self: &nsStyleImage) -> Gradient {
+ unsafe fn get_gradient(self: &nsStyleImage) -> Box<Gradient> {
use gecko::values::convert_nscolor_to_rgba;
use gecko_bindings::bindings::Gecko_GetGradientImageValue;
use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_CIRCULAR, NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL};
use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_LINEAR, NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER};
use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE, NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE};
use gecko_bindings::structs::{NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER, NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE};
use values::computed::{Length, LengthOrPercentage};
use values::computed::image::LineDirection;
@@ -576,17 +576,17 @@ impl nsStyleImage {
if gecko_gradient.mMozLegacySyntax {
CompatMode::Moz
} else if gecko_gradient.mLegacySyntax {
CompatMode::WebKit
} else {
CompatMode::Modern
};
- Gradient { items, repeating: gecko_gradient.mRepeating, kind, compat_mode }
+ Box::new(Gradient { items, repeating: gecko_gradient.mRepeating, kind, compat_mode })
}
}
pub mod basic_shape {
//! Conversions from and to CSS shape representations.
use gecko::values::GeckoStyleCoordConvertible;
use gecko_bindings::structs;
--- a/servo/components/style/values/generics/image.rs
+++ b/servo/components/style/values/generics/image.rs
@@ -6,38 +6,80 @@
//!
//! [images]: https://drafts.csswg.org/css-images/#image-values
use Atom;
use cssparser::serialize_identifier;
use custom_properties::SpecifiedValue;
use std::fmt;
use style_traits::ToCss;
-use values::computed::ComputedValueAsSpecified;
+use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue};
/// An [image].
///
/// [image]: https://drafts.csswg.org/css-images/#image-values
-#[derive(Clone, PartialEq, ToComputedValue)]
+#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Image<Gradient, MozImageRect, ImageUrl> {
/// A `<url()>` image.
Url(ImageUrl),
- /// A `<gradient>` image.
- Gradient(Gradient),
- /// A `-moz-image-rect` image
- Rect(MozImageRect),
+ /// 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.
+ Rect(Box<MozImageRect>),
/// A `-moz-element(# <element-id>)`
Element(Atom),
/// A paint worklet image.
/// https://drafts.css-houdini.org/css-paint-api/
#[cfg(feature = "servo")]
PaintWorklet(PaintWorklet),
}
+// Can't just use derive(ToComputedValue) on Image, because when trying to do
+// "impl<T> ToComputedValue for Box<T>" the Rust compiler complains that
+// "impl<T> ToComputedValue for T where T: ComputedValueAsSpecified + Clone"
+// aleady implements ToComputedValue for std::boxed::Box<_> and hence we have
+// conflicting implementations.
+impl<Gradient: ToComputedValue,
+ MozImageRect: ToComputedValue,
+ ImageUrl: ToComputedValue> ToComputedValue for Image<Gradient, MozImageRect, ImageUrl> {
+ type ComputedValue = Image<<Gradient as ToComputedValue>::ComputedValue,
+ <MozImageRect as ToComputedValue>::ComputedValue,
+ <ImageUrl as ToComputedValue>::ComputedValue>;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ match *self {
+ Image::Url(ref url) => Image::Url(url.to_computed_value(context)),
+ Image::Gradient(ref gradient) =>
+ Image::Gradient(Box::new(gradient.to_computed_value(context))),
+ Image::Rect(ref rect) => Image::Rect(Box::new(rect.to_computed_value(context))),
+ Image::Element(ref atom) => Image::Element(atom.to_computed_value(context)),
+ #[cfg(feature = "servo")]
+ Image::PaintWorklet(ref worklet) => Image::PaintWorklet(worklet.to_computed_value(context)),
+ }
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ match *computed {
+ Image::Url(ref url) => Image::Url(ImageUrl::from_computed_value(url)),
+ Image::Gradient(ref boxed_gradient) =>
+ Image::Gradient(Box::new(Gradient::from_computed_value(&*boxed_gradient))),
+ Image::Rect(ref boxed_rect) =>
+ Image::Rect(Box::new(MozImageRect::from_computed_value(&*boxed_rect))),
+ Image::Element(ref atom) => Image::Element(Atom::from_computed_value(atom)),
+ #[cfg(feature = "servo")]
+ Image::PaintWorklet(ref worklet) =>
+ Image::PaintWorklet(PaintWorklet::from_computed_value(worklet)),
+ }
+ }
+}
+
/// A CSS gradient.
/// https://drafts.csswg.org/css-images/#gradients
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq, ToComputedValue)]
pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color, Angle> {
/// Gradients can be linear or radial.
pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle>,
/// The color stops and interpolation hints.
--- a/servo/components/style/values/specified/image.rs
+++ b/servo/components/style/values/specified/image.rs
@@ -133,30 +133,30 @@ impl Parse for Image {
if let Ok(mut url) = input.try(|input| SpecifiedUrl::parse(context, input)) {
#[cfg(feature = "gecko")]
{
url.build_image_value();
}
return Ok(GenericImage::Url(url));
}
if let Ok(gradient) = input.try(|i| Gradient::parse(context, i)) {
- return Ok(GenericImage::Gradient(gradient));
+ return Ok(GenericImage::Gradient(Box::new(gradient)));
}
#[cfg(feature = "servo")]
{
if let Ok(paint_worklet) = input.try(|i| PaintWorklet::parse(context, i)) {
return Ok(GenericImage::PaintWorklet(paint_worklet));
}
}
if let Ok(mut image_rect) = input.try(|input| MozImageRect::parse(context, input)) {
#[cfg(feature = "gecko")]
{
image_rect.url.build_image_value();
}
- return Ok(GenericImage::Rect(image_rect));
+ return Ok(GenericImage::Rect(Box::new(image_rect)));
}
Ok(GenericImage::Element(Image::parse_element(input)?))
}
}
impl Image {
/// Creates an already specified image value from an already resolved URL
/// for insertion in the cascade.
--- a/servo/tests/unit/stylo/size_of.rs
+++ b/servo/tests/unit/stylo/size_of.rs
@@ -6,16 +6,18 @@ use selectors::gecko_like_types as dummi
use servo_arc::Arc;
use std::mem::{size_of, align_of};
use style;
use style::applicable_declarations::ApplicableDeclarationBlock;
use style::data::{ElementData, ElementStyles, RestyleData};
use style::gecko::selector_parser as real;
use style::properties::ComputedValues;
use style::rule_tree::{RuleNode, StrongRuleNode};
+use style::values::computed;
+use style::values::specified;
#[test]
fn size_of_selectors_dummy_types() {
assert_eq!(size_of::<dummies::PseudoClass>(), size_of::<real::NonTSPseudoClass>());
assert_eq!(align_of::<dummies::PseudoClass>(), align_of::<real::NonTSPseudoClass>());
assert_eq!(size_of::<dummies::PseudoElement>(), size_of::<real::PseudoElement>());
assert_eq!(align_of::<dummies::PseudoElement>(), align_of::<real::PseudoElement>());
@@ -42,8 +44,16 @@ size_of_test!(test_size_of_application_d
// FIXME(bholley): This can shrink with a little bit of work.
// See https://github.com/servo/servo/issues/17280
size_of_test!(test_size_of_rule_node, RuleNode, 80);
// This is huge, but we allocate it on the stack and then never move it,
// we only pass `&mut SourcePropertyDeclaration` references around.
size_of_test!(test_size_of_parsed_declaration, style::properties::SourcePropertyDeclaration, 704);
+
+size_of_test!(test_size_of_computed_image, computed::image::Image, 40);
+size_of_test!(test_size_of_specified_image, specified::image::Image, 40);
+
+// FIXME(bz): These can shrink if we move the None_ value inside the
+// enum instead of paying an extra word for the Either discriminant.
+size_of_test!(test_size_of_computed_image_layer, computed::image::ImageLayer, 48);
+size_of_test!(test_size_of_specified_image_layer, specified::image::ImageLayer, 48);