Bug 1547674 - Use rust types for gradient stops. r=boris
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 30 Apr 2019 20:37:54 +0000
changeset 472103 9f1a8afe2391904da48baaca3066f8d25bfd80e9
parent 472102 0e39f1415d9b7edb556eb23fe6fc0a7e3afc4e26
child 472104 99548c52b81f7b1ff00f0fef2eb713911ab4e830
push id112978
push userapavel@mozilla.com
push dateWed, 01 May 2019 16:09:45 +0000
treeherdermozilla-inbound@a35654620197 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersboris
bugs1547674
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1547674 - Use rust types for gradient stops. r=boris This doesn't clean up all that much, yet, but it's a step in the right direction. Differential Revision: https://phabricator.services.mozilla.com/D29168
layout/painting/nsCSSRenderingGradients.cpp
layout/style/GeckoBindings.cpp
layout/style/ServoBindings.toml
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
servo/components/style/gecko/conversions.rs
servo/components/style/values/computed/image.rs
servo/components/style/values/generics/image.rs
servo/components/style/values/specified/image.rs
servo/ports/geckolib/cbindgen.toml
--- a/layout/painting/nsCSSRenderingGradients.cpp
+++ b/layout/painting/nsCSSRenderingGradients.cpp
@@ -513,62 +513,73 @@ static void ClampColorStops(nsTArray<Col
   }
   if (aStops.LastElement().mPosition < 1) {
     aStops.AppendElement(ColorStop(1, false, aStops.LastElement().mColor));
   }
 }
 
 namespace mozilla {
 
-static Maybe<double> GetSpecifiedGradientPosition(const nsStyleCoord& aCoord,
-                                                  int32_t aAppUnitsPerPixel,
-                                                  gfxFloat aLineLength) {
-  auto GetCoord = [&](nscoord aCoord) -> double {
+static Color GetSpecifiedColor(const StyleGradientItem& aItem,
+                               const ComputedStyle& aStyle) {
+  if (aItem.IsInterpolationHint()) {
+    return Color();
+  }
+  const StyleColor& color = aItem.IsSimpleColorStop()
+                                ? aItem.AsSimpleColorStop()
+                                : aItem.AsComplexColorStop().color;
+  return Color::FromABGR(color.CalcColor(aStyle));
+}
+
+static Maybe<double> GetSpecifiedGradientPosition(
+    const StyleGradientItem& aItem, int32_t aAppUnitsPerPixel,
+    gfxFloat aLineLength) {
+  auto GetCoord = [&](CSSCoord aCoord) -> double {
     if (aLineLength < 1e-6) {
       return 0.0;
     }
-    return NSAppUnitsToFloatPixels(aCoord, aAppUnitsPerPixel) / aLineLength;
+    return NSAppUnitsToFloatPixels(CSSPixel::ToAppUnits(aCoord),
+                                   aAppUnitsPerPixel) /
+           aLineLength;
   };
 
-  switch (aCoord.GetUnit()) {
-    case eStyleUnit_None:
-      return Nothing();
-    case eStyleUnit_Percent:
-      return Some(aCoord.GetPercentValue());
-    case eStyleUnit_Coord:
-      return Some(GetCoord(aCoord.GetCoordValue()));
-    case eStyleUnit_Calc: {
-      const nsStyleCoord::Calc* calc = aCoord.GetCalcValue();
-      return Some(calc->mPercent + GetCoord(calc->mLength));
-    }
-    default:
-      MOZ_ASSERT_UNREACHABLE("Unknown unit in gradient color stop position?");
-      return Nothing();
+  if (aItem.IsSimpleColorStop()) {
+    return Nothing();
   }
+
+  const LengthPercentage& pos = aItem.IsComplexColorStop()
+                                    ? aItem.AsComplexColorStop().position
+                                    : aItem.AsInterpolationHint();
+
+  if (pos.ConvertsToPercentage()) {
+    return Some(pos.ToPercentage());
+  }
+
+  return Some(pos.Percentage() + GetCoord(pos.LengthInCSSPixels()));
 }
 
 static nsTArray<ColorStop> ComputeColorStops(ComputedStyle* aComputedStyle,
                                              const nsStyleGradient& aGradient,
                                              int32_t aAppUnitsPerPixel,
                                              gfxFloat aLineLength) {
   MOZ_ASSERT(aGradient.mStops.Length() >= 2,
              "The parser should reject gradients with less than two stops");
 
   nsTArray<ColorStop> stops(aGradient.mStops.Length());
 
   // If there is a run of stops before stop i that did not have specified
   // positions, then this is the index of the first stop in that run, otherwise
   // it's -1.
   int32_t firstUnsetPosition = -1;
   for (uint32_t i = 0; i < aGradient.mStops.Length(); ++i) {
-    const nsStyleGradientStop& stop = aGradient.mStops[i];
+    const StyleGradientItem& stop = aGradient.mStops[i];
     double position;
 
-    Maybe<double> specifiedPosition = GetSpecifiedGradientPosition(
-        stop.mLocation, aAppUnitsPerPixel, aLineLength);
+    Maybe<double> specifiedPosition =
+        GetSpecifiedGradientPosition(stop, aAppUnitsPerPixel, aLineLength);
 
     if (specifiedPosition) {
       position = *specifiedPosition;
     } else if (i == 0) {
       // First stop defaults to position 0.0
       position = 0.0;
     } else if (i == aGradient.mStops.Length() - 1) {
       // Last stop defaults to position 1.0
@@ -576,33 +587,34 @@ static nsTArray<ColorStop> ComputeColorS
     } else {
       // Other stops with no specified position get their position assigned
       // later by interpolation, see below.
       // Remember where the run of stops with no specified position starts,
       // if it starts here.
       if (firstUnsetPosition < 0) {
         firstUnsetPosition = i;
       }
-      auto stopColor = stop.mColor.CalcColor(*aComputedStyle);
-      stops.AppendElement(
-          ColorStop(0, stop.mIsInterpolationHint, Color::FromABGR(stopColor)));
+      MOZ_ASSERT(!stop.IsInterpolationHint(),
+                 "Interpolation hints always specify position");
+      auto color = GetSpecifiedColor(stop, *aComputedStyle);
+      stops.AppendElement(ColorStop(0, false, color));
       continue;
     }
 
     if (i > 0) {
       // Prevent decreasing stop positions by advancing this position
       // to the previous stop position, if necessary
       double previousPosition = firstUnsetPosition > 0
                                     ? stops[firstUnsetPosition - 1].mPosition
                                     : stops[i - 1].mPosition;
       position = std::max(position, previousPosition);
     }
-    auto stopColor = stop.mColor.CalcColor(*aComputedStyle);
-    stops.AppendElement(ColorStop(position, stop.mIsInterpolationHint,
-                                  Color::FromABGR(stopColor)));
+    auto stopColor = GetSpecifiedColor(stop, *aComputedStyle);
+    stops.AppendElement(
+        ColorStop(position, stop.IsInterpolationHint(), stopColor));
     if (firstUnsetPosition > 0) {
       // Interpolate positions for all stops that didn't have a specified
       // position
       double p = stops[firstUnsetPosition - 1].mPosition;
       double d = (stops[i].mPosition - p) / (i - firstUnsetPosition + 1);
       for (uint32_t j = firstUnsetPosition; j < i; ++j) {
         p += d;
         stops[j].mPosition = p;
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -1220,21 +1220,21 @@ nsStyleGradient* Gecko_CreateGradient(ui
   result->mMozLegacySyntax = aMozLegacySyntax;
 
   result->mAngle.SetNoneValue();
   result->mBgPosX.SetNoneValue();
   result->mBgPosY.SetNoneValue();
   result->mRadiusX.SetNoneValue();
   result->mRadiusY.SetNoneValue();
 
-  nsStyleGradientStop dummyStop = {nsStyleCoord(eStyleUnit_None),
-                                   StyleColor::Black(), 0};
+  result->mStops.SetCapacity(aStopCount);
 
+  auto dummyItem = StyleGradientItem::SimpleColorStop(StyleColor::Black());
   for (uint32_t i = 0; i < aStopCount; i++) {
-    result->mStops.AppendElement(dummyStop);
+    result->mStops.AppendElement(dummyItem);
   }
 
   return result;
 }
 
 const nsStyleImageRequest* Gecko_GetImageRequest(const nsStyleImage* aImage) {
   MOZ_ASSERT(aImage);
   return aImage->ImageRequest();
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -270,17 +270,16 @@ whitelist-types = [
     "ComputedStyle",
     "nsStyleCoord",
     "nsStyleCounterData",
     "nsStyleDisplay",
     "nsStyleEffects",
     "nsStyleFilter",
     "nsStyleFont",
     "nsStyleGradient",
-    "nsStyleGradientStop",
     "nsStyleGridTemplate",
     "nsStyleImage",
     "nsStyleImageLayers",
     "nsStyleList",
     "nsStyleMargin",
     "nsStyleOutline",
     "nsStylePadding",
     "nsStylePosition",
@@ -465,16 +464,17 @@ cbindgen-types = [
     { gecko = "StyleStrong", servo = "gecko_bindings::sugar::ownership::Strong" },
     { gecko = "StyleGenericFontFamily", servo = "values::computed::font::GenericFontFamily" },
     { gecko = "StyleFontFamilyNameSyntax", servo = "values::computed::font::FontFamilyNameSyntax" },
     { gecko = "StyleGenericColor", servo = "values::generics::color::Color" },
     { gecko = "StyleGenericColorOrAuto", servo = "values::generics::color::ColorOrAuto" },
     { gecko = "StyleGenericScrollbarColor", servo = "values::generics::ui::ScrollbarColor" },
     { gecko = "StyleRGBA", servo = "cssparser::RGBA" },
     { gecko = "StyleOrigin", servo = "stylesheets::Origin" },
+    { gecko = "StyleGenericGradientItem", servo = "values::generics::image::GradientItem" },
 ]
 
 mapped-generic-types = [
     { generic = true, gecko = "mozilla::RustCell", servo = "::std::cell::Cell" },
     { generic = false, gecko = "ServoNodeData", servo = "AtomicRefCell<ElementData>" },
     { generic = false, gecko = "mozilla::ServoWritingMode", servo = "::logical_geometry::WritingMode" },
     { generic = false, gecko = "mozilla::ServoCustomPropertiesMap", servo = "Option<::servo_arc::Arc<::custom_properties::CustomPropertiesMap>>" },
     { generic = false, gecko = "mozilla::ServoRuleNode", servo = "Option<::rule_tree::StrongRuleNode>" },
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1735,61 +1735,48 @@ bool nsStyleGradient::operator==(const n
       mLegacySyntax != aOther.mLegacySyntax ||
       mMozLegacySyntax != aOther.mMozLegacySyntax ||
       mBgPosX != aOther.mBgPosX || mBgPosY != aOther.mBgPosY ||
       mAngle != aOther.mAngle || mRadiusX != aOther.mRadiusX ||
       mRadiusY != aOther.mRadiusY) {
     return false;
   }
 
-  if (mStops.Length() != aOther.mStops.Length()) {
+  if (mStops != aOther.mStops) {
     return false;
   }
 
-  for (uint32_t i = 0; i < mStops.Length(); i++) {
-    const auto& stop1 = mStops[i];
-    const auto& stop2 = aOther.mStops[i];
-    if (stop1.mLocation != stop2.mLocation ||
-        stop1.mIsInterpolationHint != stop2.mIsInterpolationHint ||
-        (!stop1.mIsInterpolationHint && stop1.mColor != stop2.mColor)) {
-      return false;
-    }
-  }
-
   return true;
 }
 
 nsStyleGradient::nsStyleGradient()
     : mShape(NS_STYLE_GRADIENT_SHAPE_LINEAR),
       mSize(NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER),
       mRepeating(false),
       mLegacySyntax(false),
       mMozLegacySyntax(false) {}
 
 bool nsStyleGradient::IsOpaque() {
-  for (uint32_t i = 0; i < mStops.Length(); i++) {
-    if (mStops[i].mColor.MaybeTransparent()) {
+  for (auto& stop : mStops) {
+    if (stop.IsInterpolationHint()) {
+      continue;
+    }
+
+    auto& color = stop.IsSimpleColorStop() ? stop.AsSimpleColorStop()
+                                           : stop.AsComplexColorStop().color;
+    if (color.MaybeTransparent()) {
       // We don't know the foreground color here, so if it's being used
       // we must assume it might be transparent.
       return false;
     }
   }
+
   return true;
 }
 
-bool nsStyleGradient::HasCalc() {
-  for (uint32_t i = 0; i < mStops.Length(); i++) {
-    if (mStops[i].mLocation.IsCalcUnit()) {
-      return true;
-    }
-  }
-  return mBgPosX.IsCalcUnit() || mBgPosY.IsCalcUnit() || mAngle.IsCalcUnit() ||
-         mRadiusX.IsCalcUnit() || mRadiusY.IsCalcUnit();
-}
-
 // --------------------
 // nsStyleImageRequest
 
 /**
  * Runnable to release the nsStyleImageRequest's mRequestProxy
  * and mImageTracker on the main thread, and to perform
  * any necessary unlocking and untracking of the image.
  */
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -125,26 +125,16 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
 
   // The value mSize would have had if scriptminsize had never been applied
   nscoord mScriptUnconstrainedSize;
   nscoord mScriptMinSize;  // length
   float mScriptSizeMultiplier;
   RefPtr<nsAtom> mLanguage;
 };
 
-struct nsStyleGradientStop {
-  nsStyleCoord mLocation;  // percent, coord, calc, none
-  mozilla::StyleColor mColor;
-  bool mIsInterpolationHint;
-
-  // Use ==/!= on nsStyleGradient instead of on the gradient stop.
-  bool operator==(const nsStyleGradientStop&) const = delete;
-  bool operator!=(const nsStyleGradientStop&) const = delete;
-};
-
 class nsStyleGradient final {
  public:
   nsStyleGradient();
   uint8_t mShape;  // NS_STYLE_GRADIENT_SHAPE_*
   uint8_t mSize;   // NS_STYLE_GRADIENT_SIZE_*;
                    // not used (must be FARTHEST_CORNER) for linear shape
   bool mRepeating;
   bool mLegacySyntax;  // If true, serialization should use a vendor prefix.
@@ -156,26 +146,24 @@ class nsStyleGradient final {
   nsStyleCoord mBgPosX;  // percent, coord, calc, none
   nsStyleCoord mBgPosY;  // percent, coord, calc, none
   nsStyleCoord mAngle;   // none, angle
 
   nsStyleCoord mRadiusX;  // percent, coord, calc, none
   nsStyleCoord mRadiusY;  // percent, coord, calc, none
 
   // stops are in the order specified in the stylesheet
-  nsTArray<nsStyleGradientStop> mStops;
+  nsTArray<mozilla::StyleGradientItem> mStops;
 
   bool operator==(const nsStyleGradient& aOther) const;
   bool operator!=(const nsStyleGradient& aOther) const {
     return !(*this == aOther);
   }
 
   bool IsOpaque();
-  bool HasCalc();
-  uint32_t Hash(PLDHashNumber aHash);
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsStyleGradient)
 
  private:
   // Private destructor, to discourage deletion outside of Release():
   ~nsStyleGradient() {}
 
   nsStyleGradient(const nsStyleGradient& aOther) = delete;
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -19,17 +19,17 @@ use crate::stylesheets::RulesMutateError
 use crate::values::computed::image::LineDirection;
 use crate::values::computed::transform::Matrix3D;
 use crate::values::computed::url::ComputedImageUrl;
 use crate::values::computed::{Angle, Gradient, Image};
 use crate::values::computed::{Integer, LengthPercentage};
 use crate::values::computed::{Length, Percentage, TextAlign};
 use crate::values::generics::box_::VerticalAlign;
 use crate::values::generics::grid::{TrackListValue, TrackSize};
-use crate::values::generics::image::{CompatMode, GradientItem, Image as GenericImage};
+use crate::values::generics::image::{CompatMode, Image as GenericImage};
 use crate::values::generics::rect::Rect;
 use crate::Zero;
 use app_units::Au;
 use std::f32::consts::PI;
 use style_traits::values::specified::AllowedNumericType;
 
 impl From<LengthPercentage> for nsStyleCoord_CalcValue {
     fn from(other: LengthPercentage) -> nsStyleCoord_CalcValue {
@@ -149,17 +149,16 @@ impl nsStyleImage {
             GenericImage::Element(ref element) => unsafe {
                 bindings::Gecko_SetImageElement(self, element.as_ptr());
             },
         }
     }
 
     // FIXME(emilio): This is really complex, we should use cbindgen for this.
     fn set_gradient(&mut self, gradient: Gradient) {
-        use self::structs::nsStyleCoord;
         use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER as CLOSEST_CORNER;
         use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE as CLOSEST_SIDE;
         use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as FARTHEST_CORNER;
         use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE as FARTHEST_SIDE;
         use crate::values::generics::image::{
             Circle, Ellipse, EndingShape, GradientKind, ShapeExtent,
         };
         use crate::values::specified::position::{X, Y};
@@ -324,36 +323,19 @@ impl nsStyleImage {
                     (*gecko_gradient).mBgPosX.set(position.horizontal);
                     (*gecko_gradient).mBgPosY.set(position.vertical);
                 }
 
                 gecko_gradient
             },
         };
 
-        for (index, item) in gradient.items.iter().enumerate() {
-            // NB: stops are guaranteed to be none in the gecko side by
-            // default.
-
+        for (index, item) in gradient.items.into_iter().enumerate() {
             let gecko_stop = unsafe { &mut (*gecko_gradient).mStops[index] };
-            let mut coord = nsStyleCoord::null();
-
-            match *item {
-                GradientItem::ColorStop(ref stop) => {
-                    gecko_stop.mColor = stop.color.into();
-                    gecko_stop.mIsInterpolationHint = false;
-                    coord.set(stop.position);
-                },
-                GradientItem::InterpolationHint(hint) => {
-                    gecko_stop.mIsInterpolationHint = true;
-                    coord.set(Some(hint));
-                },
-            }
-
-            gecko_stop.mLocation.move_from(coord);
+            *gecko_stop = item;
         }
 
         unsafe {
             bindings::Gecko_SetGradientImageValue(self, gecko_gradient);
         }
     }
 
     /// Converts into Image.
@@ -414,17 +396,17 @@ impl nsStyleImage {
     }
 
     unsafe fn get_gradient(self: &nsStyleImage) -> Box<Gradient> {
         use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER as CLOSEST_CORNER;
         use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE as CLOSEST_SIDE;
         use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as FARTHEST_CORNER;
         use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE as FARTHEST_SIDE;
         use crate::values::computed::position::Position;
-        use crate::values::generics::image::{Circle, ColorStop, Ellipse};
+        use crate::values::generics::image::{Circle, Ellipse};
         use crate::values::generics::image::{EndingShape, GradientKind, ShapeExtent};
 
         let gecko_gradient = bindings::Gecko_GetGradientImageValue(self)
             .as_ref()
             .unwrap();
         let angle = Angle::from_gecko_style_coord(&gecko_gradient.mAngle);
         let horizontal_style = LengthPercentage::from_gecko_style_coord(&gecko_gradient.mBgPosX);
         let vertical_style = LengthPercentage::from_gecko_style_coord(&gecko_gradient.mBgPosY);
@@ -526,34 +508,17 @@ impl nsStyleImage {
                         }
                     },
                 };
 
                 GradientKind::Radial(shape, position, angle)
             },
         };
 
-        let items = gecko_gradient
-            .mStops
-            .iter()
-            .map(|ref stop| {
-                if stop.mIsInterpolationHint {
-                    GradientItem::InterpolationHint(
-                        LengthPercentage::from_gecko_style_coord(&stop.mLocation)
-                            .expect("mLocation could not convert to LengthPercentage"),
-                    )
-                } else {
-                    GradientItem::ColorStop(ColorStop {
-                        color: stop.mColor.into(),
-                        position: LengthPercentage::from_gecko_style_coord(&stop.mLocation),
-                    })
-                }
-            })
-            .collect();
-
+        let items = gecko_gradient.mStops.iter().cloned().collect();
         let compat_mode = if gecko_gradient.mMozLegacySyntax {
             CompatMode::Moz
         } else if gecko_gradient.mLegacySyntax {
             CompatMode::WebKit
         } else {
             CompatMode::Modern
         };
 
--- a/servo/components/style/values/computed/image.rs
+++ b/servo/components/style/values/computed/image.rs
@@ -50,17 +50,17 @@ pub enum LineDirection {
     #[cfg(feature = "gecko")]
     MozPosition(Option<Position>, Option<Angle>),
 }
 
 /// A computed radial gradient ending shape.
 pub type EndingShape = generic::EndingShape<Length, LengthPercentage>;
 
 /// A computed gradient item.
-pub type GradientItem = generic::GradientItem<Color, LengthPercentage>;
+pub type GradientItem = generic::GenericGradientItem<Color, LengthPercentage>;
 
 /// A computed color stop.
 pub type ColorStop = generic::ColorStop<Color, LengthPercentage>;
 
 /// Computed values for `-moz-image-rect(...)`.
 pub type MozImageRect = generic::MozImageRect<NumberOrPercentage, ComputedImageUrl>;
 
 impl generic::LineDirection for LineDirection {
--- a/servo/components/style/values/generics/image.rs
+++ b/servo/components/style/values/generics/image.rs
@@ -130,35 +130,59 @@ pub enum ShapeExtent {
     Cover,
 }
 
 /// A gradient item.
 /// <https://drafts.csswg.org/css-images-4/#color-stop-syntax>
 #[derive(
     Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
 )]
-pub enum GradientItem<Color, LengthPercentage> {
-    /// A color stop.
-    ColorStop(ColorStop<Color, LengthPercentage>),
+#[repr(C, u8)]
+pub enum GenericGradientItem<Color, LengthPercentage> {
+    /// A simple color stop, without position.
+    SimpleColorStop(Color),
+    /// A complex color stop, with a position.
+    ComplexColorStop {
+        /// The color for the stop.
+        color: Color,
+        /// The position for the stop.
+        position: LengthPercentage,
+    },
     /// An interpolation hint.
     InterpolationHint(LengthPercentage),
 }
 
+pub use self::GenericGradientItem as GradientItem;
+
 /// A color stop.
 /// <https://drafts.csswg.org/css-images/#typedef-color-stop-list>
 #[derive(
     Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
 )]
 pub struct ColorStop<Color, LengthPercentage> {
     /// The color of this stop.
     pub color: Color,
     /// The position of this stop.
     pub position: Option<LengthPercentage>,
 }
 
+impl<Color, LengthPercentage> ColorStop<Color, LengthPercentage> {
+    /// Convert the color stop into an appropriate `GradientItem`.
+    #[inline]
+    pub fn into_item(self) -> GradientItem<Color, LengthPercentage> {
+        match self.position {
+            Some(position) => GradientItem::ComplexColorStop {
+                color: self.color,
+                position,
+            },
+            None => GradientItem::SimpleColorStop(self.color),
+        }
+    }
+}
+
 /// Specified values for a paint worklet.
 /// <https://drafts.css-houdini.org/css-paint-api/>
 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
 #[derive(Clone, Debug, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
 pub struct PaintWorklet {
     /// The name the worklet was registered with.
     pub name: Atom,
     /// The arguments for the worklet.
--- a/servo/components/style/values/specified/image.rs
+++ b/servo/components/style/values/specified/image.rs
@@ -479,48 +479,48 @@ impl Gradient {
                         if color == Color::CurrentColor {
                             return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                         }
                         Ok((color.into(), p))
                     })?;
                     if reverse_stops {
                         p.reverse();
                     }
-                    Ok(generic::GradientItem::ColorStop(generic::ColorStop {
-                        color: color,
-                        position: Some(p.into()),
-                    }))
+                    Ok(generic::GradientItem::ComplexColorStop {
+                        color,
+                        position: p.into(),
+                    })
                 })
             })
             .unwrap_or(vec![]);
 
         if items.is_empty() {
             items = vec![
-                generic::GradientItem::ColorStop(generic::ColorStop {
+                generic::GradientItem::ComplexColorStop {
                     color: Color::transparent().into(),
-                    position: Some(Percentage::zero().into()),
-                }),
-                generic::GradientItem::ColorStop(generic::ColorStop {
+                    position: Percentage::zero().into(),
+                },
+                generic::GradientItem::ComplexColorStop {
                     color: Color::transparent().into(),
-                    position: Some(Percentage::hundred().into()),
-                }),
+                    position: Percentage::hundred().into(),
+                },
             ];
         } else if items.len() == 1 {
             let first = items[0].clone();
             items.push(first);
         } else {
             items.sort_by(|a, b| {
                 match (a, b) {
                     (
-                        &generic::GradientItem::ColorStop(ref a),
-                        &generic::GradientItem::ColorStop(ref b),
-                    ) => match (&a.position, &b.position) {
+                        &generic::GradientItem::ComplexColorStop { position: ref a_position, .. },
+                        &generic::GradientItem::ComplexColorStop { position: ref b_position, .. },
+                    ) => match (a_position, b_position) {
                         (
-                            &Some(LengthPercentage::Percentage(a)),
-                            &Some(LengthPercentage::Percentage(b)),
+                            &LengthPercentage::Percentage(a),
+                            &LengthPercentage::Percentage(b),
                         ) => {
                             return a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal);
                         },
                         _ => {},
                     },
                     _ => {},
                 }
                 if reverse_stops {
@@ -955,23 +955,23 @@ impl GradientItem {
                         return Ok(());
                     }
                 }
 
                 let stop = ColorStop::parse(context, input)?;
 
                 if let Ok(multi_position) = input.try(|i| LengthPercentage::parse(context, i)) {
                     let stop_color = stop.color.clone();
-                    items.push(generic::GradientItem::ColorStop(stop));
-                    items.push(generic::GradientItem::ColorStop(ColorStop {
+                    items.push(stop.into_item());
+                    items.push(ColorStop {
                         color: stop_color,
                         position: Some(multi_position),
-                    }));
+                    }.into_item());
                 } else {
-                    items.push(generic::GradientItem::ColorStop(stop));
+                    items.push(stop.into_item());
                 }
 
                 seen_stop = true;
                 Ok(())
             })?;
 
             match input.next() {
                 Err(_) => break,
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -111,16 +111,17 @@ include = [
   "TextTransform",
   "MozListReversed",
   "Owned",
   "OwnedOrNull",
   "Strong",
   "ScrollbarColor",
   "Color",
   "ColorOrAuto",
+  "GradientItem",
 ]
 item_types = ["enums", "structs", "typedefs", "functions"]
 renaming_overrides_prefixing = true
 
 # Prevent some renaming for Gecko types that cbindgen doesn't otherwise understand.
 [export.rename]
 "nscolor" = "nscolor"
 "nsAtom" = "nsAtom"