Bug 1550554 - Share computed and specified value representation of -moz-context-properties. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 16 May 2019 23:03:29 +0000
changeset 474255 79010ce706eb83de48b5abb81ea387f2f5f4dc0b
parent 474254 6524a34864c39e0957cc10d124d61a6d795b5894
child 474256 1e32fe8538eddbf6b87bb2dccb716f425d8dd931
push id113144
push usershindli@mozilla.com
push dateFri, 17 May 2019 16:44:55 +0000
treeherdermozilla-inbound@f4c4b796f845 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1550554
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 1550554 - Share computed and specified value representation of -moz-context-properties. r=heycam Differential Revision: https://phabricator.services.mozilla.com/D30545
layout/style/GeckoBindings.cpp
layout/style/ServoBindings.toml
layout/style/ServoStyleConstsInlines.h
layout/style/nsStyleConsts.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/svg/SVGImageContext.cpp
servo/components/style/gecko_string_cache/mod.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhands/inherited_svg.mako.rs
servo/components/style/values/mod.rs
servo/components/style/values/specified/svg.rs
servo/components/style_traits/arc_slice.rs
servo/ports/geckolib/cbindgen.toml
servo/ports/geckolib/glue.rs
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -1614,28 +1614,16 @@ void Gecko_nsStyleSVG_SetDashArrayLength
   aSvg->mStrokeDasharray.Clear();
   aSvg->mStrokeDasharray.SetLength(aLen);
 }
 
 void Gecko_nsStyleSVG_CopyDashArray(nsStyleSVG* aDst, const nsStyleSVG* aSrc) {
   aDst->mStrokeDasharray = aSrc->mStrokeDasharray;
 }
 
-void Gecko_nsStyleSVG_SetContextPropertiesLength(nsStyleSVG* aSvg,
-                                                 uint32_t aLen) {
-  aSvg->mContextProps.Clear();
-  aSvg->mContextProps.SetLength(aLen);
-}
-
-void Gecko_nsStyleSVG_CopyContextProperties(nsStyleSVG* aDst,
-                                            const nsStyleSVG* aSrc) {
-  aDst->mContextProps = aSrc->mContextProps;
-  aDst->mContextPropsBits = aSrc->mContextPropsBits;
-}
-
 URLValue* Gecko_URLValue_Create(StyleStrong<RawServoCssUrlData> aCssUrl,
                                 CORSMode aCORSMode) {
   RefPtr<URLValue> url = new URLValue(aCssUrl.Consume(), aCORSMode);
   return url.forget().take();
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(GeckoURLValueMallocSizeOf)
 
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -475,16 +475,17 @@ cbindgen-types = [
     { gecko = "StyleRGBA", servo = "cssparser::RGBA" },
     { gecko = "StyleOrigin", servo = "stylesheets::Origin" },
     { gecko = "StyleGenericGradientItem", servo = "values::generics::image::GradientItem" },
     { gecko = "StyleGenericVerticalAlign", servo = "values::generics::box_::VerticalAlign" },
     { gecko = "StyleVerticalAlignKeyword", servo = "values::generics::box_::VerticalAlignKeyword" },
     { gecko = "StyleGenericBasicShape", servo = "values::generics::basic_shape::BasicShape" },
     { gecko = "StyleArcSlice", servo = "style_traits::arc_slice::ArcSlice" },
     { gecko = "StyleForgottenArcSlicePtr", servo = "style_traits::arc_slice::ForgottenArcSlicePtr" },
+    { gecko = "StyleMozContextProperties", servo = "values::specified::svg::MozContextProperties" },
 ]
 
 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/ServoStyleConstsInlines.h
+++ b/layout/style/ServoStyleConstsInlines.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Some inline functions declared in cbindgen.toml */
 
 #ifndef mozilla_ServoStyleConstsInlines_h
 #define mozilla_ServoStyleConstsInlines_h
 
 #include "mozilla/ServoStyleConsts.h"
+#include "nsGkAtoms.h"
 #include <type_traits>
 
 // TODO(emilio): there are quite a few other implementations scattered around
 // that should move here.
 
 namespace mozilla {
 
 // This code is basically a C++ port of the Arc::clone() implementation in
@@ -24,16 +25,21 @@ static constexpr const size_t kStaticRef
 static constexpr const size_t kMaxRefcount =
     std::numeric_limits<intptr_t>::max();
 static constexpr const uint64_t kArcSliceCanary = 0xf3f3f3f3f3f3f3f3;
 
 #define ASSERT_CANARY \
   MOZ_DIAGNOSTIC_ASSERT(_0.ptr->data.header.header == kArcSliceCanary, "Uh?");
 
 template <typename T>
+inline StyleArcSlice<T>::StyleArcSlice() {
+  _0.ptr = reinterpret_cast<decltype(_0.ptr)>(Servo_StyleArcSlice_EmptyPtr());
+}
+
+template <typename T>
 inline StyleArcSlice<T>::StyleArcSlice(const StyleArcSlice& aOther) {
   MOZ_DIAGNOSTIC_ASSERT(aOther._0.ptr);
   _0.ptr = aOther._0.ptr;
   if (_0.ptr->count.load(std::memory_order_relaxed) != kStaticRefcount) {
     auto old_size = _0.ptr->count.fetch_add(1, std::memory_order_relaxed);
     if (MOZ_UNLIKELY(old_size > kMaxRefcount)) {
       ::abort();
     }
@@ -86,11 +92,25 @@ inline StyleArcSlice<T>::~StyleArcSlice(
   for (T& elem : MakeSpan(_0.ptr->data.slice, Length())) {
     elem.~T();
   }
   free(_0.ptr);  // Drop the allocation now.
 }
 
 #undef ASSERT_CANARY
 
+inline bool StyleAtom::IsStatic() const { return !!(_0 & 1); }
+
+inline StyleAtom::~StyleAtom() {
+  if (!IsStatic()) {
+    reinterpret_cast<nsAtom*>(_0)->Release();
+  }
+}
+
+inline StyleAtom::StyleAtom(const StyleAtom& aOther) : _0(aOther._0) {
+  if (!IsStatic()) {
+    reinterpret_cast<nsAtom*>(_0)->AddRef();
+  }
+}
+
 }  // namespace mozilla
 
 #endif
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -677,22 +677,16 @@ enum class StyleWhiteSpace : uint8_t {
 #define NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE 0
 #define NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER 1
 #define NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE 2
 #define NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER 3
 #define NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE 4
 
 // See nsStyleSVG
 
-// -moz-context-properties
-#define NS_STYLE_CONTEXT_PROPERTY_FILL (1 << 0)
-#define NS_STYLE_CONTEXT_PROPERTY_STROKE (1 << 1)
-#define NS_STYLE_CONTEXT_PROPERTY_FILL_OPACITY (1 << 2)
-#define NS_STYLE_CONTEXT_PROPERTY_STROKE_OPACITY (1 << 3)
-
 /*
  * -moz-window-shadow
  * Also used in widget code
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE 0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT 1
 #define NS_STYLE_WINDOW_SHADOW_MENU 2
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -669,62 +669,61 @@ nsChangeHint nsStyleColumn::CalcDifferen
 }
 
 // --------------------
 // nsStyleSVG
 //
 nsStyleSVG::nsStyleSVG(const Document& aDocument)
     : mFill(eStyleSVGPaintType_Color),  // Will be initialized to NS_RGB(0,0,0)
       mStroke(eStyleSVGPaintType_None),
+      mMozContextProperties{{}, {0}},
       mStrokeDashoffset(LengthPercentage::Zero()),
       mStrokeWidth(LengthPercentage::FromPixels(1.0f)),
       mFillOpacity(1.0f),
       mStrokeMiterlimit(4.0f),
       mStrokeOpacity(1.0f),
       mClipRule(StyleFillRule::Nonzero),
       mColorInterpolation(NS_STYLE_COLOR_INTERPOLATION_SRGB),
       mColorInterpolationFilters(NS_STYLE_COLOR_INTERPOLATION_LINEARRGB),
       mFillRule(StyleFillRule::Nonzero),
       mPaintOrder(NS_STYLE_PAINT_ORDER_NORMAL),
       mShapeRendering(NS_STYLE_SHAPE_RENDERING_AUTO),
       mStrokeLinecap(NS_STYLE_STROKE_LINECAP_BUTT),
       mStrokeLinejoin(NS_STYLE_STROKE_LINEJOIN_MITER),
       mTextAnchor(NS_STYLE_TEXT_ANCHOR_START),
-      mContextPropsBits(0),
       mContextFlags(
           (eStyleSVGOpacitySource_Normal << FILL_OPACITY_SOURCE_SHIFT) |
           (eStyleSVGOpacitySource_Normal << STROKE_OPACITY_SOURCE_SHIFT)) {
   MOZ_COUNT_CTOR(nsStyleSVG);
 }
 
 nsStyleSVG::~nsStyleSVG() { MOZ_COUNT_DTOR(nsStyleSVG); }
 
 nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource)
     : mFill(aSource.mFill),
       mStroke(aSource.mStroke),
       mMarkerEnd(aSource.mMarkerEnd),
       mMarkerMid(aSource.mMarkerMid),
       mMarkerStart(aSource.mMarkerStart),
       mStrokeDasharray(aSource.mStrokeDasharray),
-      mContextProps(aSource.mContextProps),
+      mMozContextProperties(aSource.mMozContextProperties),
       mStrokeDashoffset(aSource.mStrokeDashoffset),
       mStrokeWidth(aSource.mStrokeWidth),
       mFillOpacity(aSource.mFillOpacity),
       mStrokeMiterlimit(aSource.mStrokeMiterlimit),
       mStrokeOpacity(aSource.mStrokeOpacity),
       mClipRule(aSource.mClipRule),
       mColorInterpolation(aSource.mColorInterpolation),
       mColorInterpolationFilters(aSource.mColorInterpolationFilters),
       mFillRule(aSource.mFillRule),
       mPaintOrder(aSource.mPaintOrder),
       mShapeRendering(aSource.mShapeRendering),
       mStrokeLinecap(aSource.mStrokeLinecap),
       mStrokeLinejoin(aSource.mStrokeLinejoin),
       mTextAnchor(aSource.mTextAnchor),
-      mContextPropsBits(aSource.mContextPropsBits),
       mContextFlags(aSource.mContextFlags) {
   MOZ_COUNT_CTOR(nsStyleSVG);
 }
 
 static bool PaintURIChanged(const nsStyleSVGPaint& aPaint1,
                             const nsStyleSVGPaint& aPaint2) {
   if (aPaint1.Type() != aPaint2.Type()) {
     return aPaint1.Type() == eStyleSVGPaintType_Server ||
@@ -792,22 +791,22 @@ nsChangeHint nsStyleSVG::CalcDifference(
   if (mStrokeDashoffset != aNewData.mStrokeDashoffset ||
       mClipRule != aNewData.mClipRule ||
       mColorInterpolation != aNewData.mColorInterpolation ||
       mColorInterpolationFilters != aNewData.mColorInterpolationFilters ||
       mFillRule != aNewData.mFillRule || mPaintOrder != aNewData.mPaintOrder ||
       mShapeRendering != aNewData.mShapeRendering ||
       mStrokeDasharray != aNewData.mStrokeDasharray ||
       mContextFlags != aNewData.mContextFlags ||
-      mContextPropsBits != aNewData.mContextPropsBits) {
+      mMozContextProperties.bits != aNewData.mMozContextProperties.bits) {
     return hint | nsChangeHint_RepaintFrame;
   }
 
   if (!hint) {
-    if (mContextProps != aNewData.mContextProps) {
+    if (mMozContextProperties.idents != aNewData.mMozContextProperties.idents) {
       hint = nsChangeHint_NeutralChange;
     }
   }
 
   return hint;
 }
 
 // --------------------
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1661,22 +1661,19 @@ struct StyleAnimation {
   dom::FillMode mFillMode;
   StyleAnimationPlayState mPlayState;
   float mIterationCount;  // mozilla::PositiveInfinity<float>() means infinite
 };
 
 struct StyleSVGPath final {
   StyleSVGPath(StyleForgottenArcSlicePtr<StylePathCommand> aPath,
                StyleFillRule aFill)
-    : mPath(aPath),
-      mFillRule(aFill) {}
-
-  Span<const StylePathCommand> Path() const {
-    return mPath.AsSpan();
-  }
+      : mPath(aPath), mFillRule(aFill) {}
+
+  Span<const StylePathCommand> Path() const { return mPath.AsSpan(); }
 
   StyleFillRule FillRule() const { return mFillRule; }
 
   bool operator==(const StyleSVGPath& aOther) const {
     return mPath == aOther.mPath && mFillRule == aOther.mFillRule;
   }
 
   bool operator!=(const StyleSVGPath& aOther) const {
@@ -2693,40 +2690,40 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   nsChangeHint CalcDifference(const nsStyleSVG& aNewData) const;
 
   nsStyleSVGPaint mFill;
   nsStyleSVGPaint mStroke;
   RefPtr<mozilla::css::URLValue> mMarkerEnd;
   RefPtr<mozilla::css::URLValue> mMarkerMid;
   RefPtr<mozilla::css::URLValue> mMarkerStart;
   nsTArray<mozilla::NonNegativeLengthPercentage> mStrokeDasharray;
-  nsTArray<RefPtr<nsAtom>> mContextProps;
+  mozilla::StyleMozContextProperties mMozContextProperties;
 
   mozilla::LengthPercentage mStrokeDashoffset;
   mozilla::NonNegativeLengthPercentage mStrokeWidth;
 
   float mFillOpacity;
   float mStrokeMiterlimit;
   float mStrokeOpacity;
 
   mozilla::StyleFillRule mClipRule;
   uint8_t mColorInterpolation;         // NS_STYLE_COLOR_INTERPOLATION_*
   uint8_t mColorInterpolationFilters;  // NS_STYLE_COLOR_INTERPOLATION_*
   mozilla::StyleFillRule mFillRule;
-  uint8_t mPaintOrder;        // bitfield of NS_STYLE_PAINT_ORDER_* values
-  uint8_t mShapeRendering;    // NS_STYLE_SHAPE_RENDERING_*
-  uint8_t mStrokeLinecap;     // NS_STYLE_STROKE_LINECAP_*
-  uint8_t mStrokeLinejoin;    // NS_STYLE_STROKE_LINEJOIN_*
-  uint8_t mTextAnchor;        // NS_STYLE_TEXT_ANCHOR_*
-  uint8_t mContextPropsBits;  // bitfield of
-                              // NS_STYLE_CONTEXT_PROPERTY_FILL_* values
+  uint8_t mPaintOrder;      // bitfield of NS_STYLE_PAINT_ORDER_* values
+  uint8_t mShapeRendering;  // NS_STYLE_SHAPE_RENDERING_*
+  uint8_t mStrokeLinecap;   // NS_STYLE_STROKE_LINECAP_*
+  uint8_t mStrokeLinejoin;  // NS_STYLE_STROKE_LINEJOIN_*
+  uint8_t mTextAnchor;      // NS_STYLE_TEXT_ANCHOR_*
 
   /// Returns true if style has been set to expose the computed values of
   /// certain properties (such as 'fill') to the contents of any linked images.
-  bool ExposesContextProperties() const { return bool(mContextPropsBits); }
+  bool ExposesContextProperties() const {
+    return bool(mMozContextProperties.bits);
+  }
 
   nsStyleSVGOpacitySource FillOpacitySource() const {
     uint8_t value =
         (mContextFlags & FILL_OPACITY_SOURCE_MASK) >> FILL_OPACITY_SOURCE_SHIFT;
     return nsStyleSVGOpacitySource(value);
   }
   nsStyleSVGOpacitySource StrokeOpacitySource() const {
     uint8_t value = (mContextFlags & STROKE_OPACITY_SOURCE_MASK) >>
--- a/layout/svg/SVGImageContext.cpp
+++ b/layout/svg/SVGImageContext.cpp
@@ -40,31 +40,31 @@ void SVGImageContext::MaybeStoreContextP
     return;
   }
 
   bool haveContextPaint = false;
 
   RefPtr<SVGEmbeddingContextPaint> contextPaint =
       new SVGEmbeddingContextPaint();
 
-  if ((style->mContextPropsBits & NS_STYLE_CONTEXT_PROPERTY_FILL) &&
+  if ((style->mMozContextProperties.bits & StyleContextPropertyBits_FILL) &&
       style->mFill.Type() == eStyleSVGPaintType_Color) {
     haveContextPaint = true;
     contextPaint->SetFill(style->mFill.GetColor(aFromComputedStyle));
   }
-  if ((style->mContextPropsBits & NS_STYLE_CONTEXT_PROPERTY_STROKE) &&
+  if ((style->mMozContextProperties.bits & StyleContextPropertyBits_STROKE) &&
       style->mStroke.Type() == eStyleSVGPaintType_Color) {
     haveContextPaint = true;
     contextPaint->SetStroke(style->mStroke.GetColor(aFromComputedStyle));
   }
-  if (style->mContextPropsBits & NS_STYLE_CONTEXT_PROPERTY_FILL_OPACITY) {
+  if (style->mMozContextProperties.bits & StyleContextPropertyBits_FILL_OPACITY) {
     haveContextPaint = true;
     contextPaint->SetFillOpacity(style->mFillOpacity);
   }
-  if (style->mContextPropsBits & NS_STYLE_CONTEXT_PROPERTY_STROKE_OPACITY) {
+  if (style->mMozContextProperties.bits & StyleContextPropertyBits_STROKE_OPACITY) {
     haveContextPaint = true;
     contextPaint->SetStrokeOpacity(style->mStrokeOpacity);
   }
 
   if (haveContextPaint) {
     if (!aContext) {
       aContext.emplace();
     }
--- a/servo/components/style/gecko_string_cache/mod.rs
+++ b/servo/components/style/gecko_string_cache/mod.rs
@@ -48,16 +48,17 @@ macro_rules! local_name {
     };
 }
 
 /// A handle to a Gecko atom.
 ///
 /// This is either a strong reference to a dynamic atom (an nsAtom pointer),
 /// or an offset from gGkAtoms to the nsStaticAtom object.
 #[derive(Eq, PartialEq)]
+#[repr(C)]
 pub struct Atom(usize);
 
 /// An atom *without* a strong reference.
 ///
 /// Only usable as `&'a WeakAtom`,
 /// where `'a` is the lifetime of something that holds a strong reference to that atom.
 pub struct WeakAtom(nsAtom);
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -298,24 +298,24 @@ impl ${style_struct.gecko_struct_name} {
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         ${set_gecko_property(gecko_ffi_name, "From::from(v)")}
     }
 </%def>
 
 <%def name="impl_simple_clone(ident, gecko_ffi_name)">
     #[allow(non_snake_case)]
     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
-        From::from(self.gecko.${gecko_ffi_name})
+        From::from(self.gecko.${gecko_ffi_name}.clone())
     }
 </%def>
 
 <%def name="impl_simple_copy(ident, gecko_ffi_name, *kwargs)">
     #[allow(non_snake_case)]
     pub fn copy_${ident}_from(&mut self, other: &Self) {
-        self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name};
+        self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}.clone();
     }
 
     #[allow(non_snake_case)]
     pub fn reset_${ident}(&mut self, other: &Self) {
         self.copy_${ident}_from(other)
     }
 </%def>
 
@@ -4115,17 +4115,17 @@ clip-path
 
     <% impl_common_image_layer_properties("mask") %>
     <% impl_simple_image_array_property("mode", "mask", "mMask", "mMaskMode", "SVG") %>
     <% impl_simple_image_array_property("composite", "mask", "mMask", "mComposite", "SVG") %>
     <% impl_shape_source("clip_path", "mClipPath") %>
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="InheritedSVG"
-                  skip_longhands="paint-order stroke-dasharray -moz-context-properties">
+                  skip_longhands="paint-order stroke-dasharray">
     pub fn set_paint_order(&mut self, v: longhands::paint_order::computed_value::T) {
         self.gecko.mPaintOrder = v.0;
     }
 
     ${impl_simple_copy('paint_order', 'mPaintOrder')}
 
     pub fn clone_paint_order(&self) -> longhands::paint_order::computed_value::T {
         use crate::properties::longhands::paint_order::computed_value::T;
@@ -4175,70 +4175,16 @@ clip-path
         use crate::values::generics::svg::SVGStrokeDashArray;
 
         if self.gecko.mContextFlags & CONTEXT_VALUE != 0 {
             debug_assert_eq!(self.gecko.mStrokeDasharray.len(), 0);
             return SVGStrokeDashArray::ContextValue;
         }
         SVGStrokeDashArray::Values(self.gecko.mStrokeDasharray.iter().cloned().collect())
     }
-
-    #[allow(non_snake_case)]
-    pub fn _moz_context_properties_count(&self) -> usize {
-        self.gecko.mContextProps.len()
-    }
-
-    // FIXME(emilio): Remove by sharing representation.
-    #[allow(non_snake_case)]
-    pub fn clone__moz_context_properties(&self) -> longhands::_moz_context_properties::computed_value::T {
-        use crate::values::specified::svg::MozContextProperties;
-        let buf = self.gecko.mContextProps.iter().map(|v| {
-            MozContextProperties(CustomIdent(unsafe { Atom::from_raw(v.mRawPtr) }))
-        }).collect::<Vec<_>>();
-        longhands::_moz_context_properties::computed_value::List(crate::ArcSlice::from_iter(buf.into_iter()))
-    }
-
-    #[allow(non_snake_case)]
-    pub fn set__moz_context_properties<I>(&mut self, v: I)
-    where
-        I: IntoIterator<Item = longhands::_moz_context_properties::computed_value::single_value::T>,
-        I::IntoIter: ExactSizeIterator
-    {
-        let v = v.into_iter();
-        unsafe {
-            bindings::Gecko_nsStyleSVG_SetContextPropertiesLength(&mut *self.gecko, v.len() as u32);
-        }
-
-        let s = &mut *self.gecko;
-        s.mContextPropsBits = 0;
-        for (gecko, servo) in s.mContextProps.iter_mut().zip(v) {
-            if (servo.0).0 == atom!("fill") {
-                s.mContextPropsBits |= structs::NS_STYLE_CONTEXT_PROPERTY_FILL as u8;
-            } else if (servo.0).0 == atom!("stroke") {
-                s.mContextPropsBits |= structs::NS_STYLE_CONTEXT_PROPERTY_STROKE as u8;
-            } else if (servo.0).0 == atom!("fill-opacity") {
-                s.mContextPropsBits |= structs::NS_STYLE_CONTEXT_PROPERTY_FILL_OPACITY as u8;
-            } else if (servo.0).0 == atom!("stroke-opacity") {
-                s.mContextPropsBits |= structs::NS_STYLE_CONTEXT_PROPERTY_STROKE_OPACITY as u8;
-            }
-            gecko.mRawPtr = (servo.0).0.into_addrefed();
-        }
-    }
-
-    #[allow(non_snake_case)]
-    pub fn copy__moz_context_properties_from(&mut self, other: &Self) {
-        unsafe {
-            bindings::Gecko_nsStyleSVG_CopyContextProperties(&mut *self.gecko, &*other.gecko);
-        }
-    }
-
-    #[allow(non_snake_case)]
-    pub fn reset__moz_context_properties(&mut self, other: &Self) {
-        self.copy__moz_context_properties_from(other)
-    }
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Color">
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="InheritedUI" skip_longhands="cursor">
     pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) {
         self.gecko.mCursor = v.keyword;
--- a/servo/components/style/properties/longhands/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_svg.mako.rs
@@ -189,15 +189,13 @@
     products="gecko",
     animation_value_type="discrete",
     spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder",
 )}
 
 ${helpers.predefined_type(
     "-moz-context-properties",
     "MozContextProperties",
-    initial_value=None,
-    vector=True,
-    animation_value_type="none",
+    "computed::MozContextProperties::default()",
     products="gecko",
+    animation_value_type="none",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)",
-    allow_empty=True,
 )}
--- a/servo/components/style/values/mod.rs
+++ b/servo/components/style/values/mod.rs
@@ -169,16 +169,17 @@ impl<A: Debug, B: Debug> Debug for Eithe
     Hash,
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToComputedValue,
     ToResolvedValue,
     ToShmem,
 )]
+#[repr(C)]
 pub struct CustomIdent(pub Atom);
 
 impl CustomIdent {
     /// Parse an already-tokenizer identifier
     pub fn from_ident<'i>(
         location: SourceLocation,
         ident: &CowRcStr<'i>,
         excluding: &[&str],
--- a/servo/components/style/values/specified/svg.rs
+++ b/servo/components/style/values/specified/svg.rs
@@ -7,17 +7,17 @@
 use crate::parser::{Parse, ParserContext};
 use crate::values::generics::svg as generic;
 use crate::values::specified::color::Color;
 use crate::values::specified::url::SpecifiedUrl;
 use crate::values::specified::AllowQuirks;
 use crate::values::specified::LengthPercentage;
 use crate::values::specified::{NonNegativeLengthPercentage, Opacity};
 use crate::values::CustomIdent;
-use cssparser::Parser;
+use cssparser::{Parser, Token};
 use std::fmt::{self, Write};
 use style_traits::{CommaWithSpace, CssWriter, ParseError, Separator};
 use style_traits::{StyleParseErrorKind, ToCss};
 
 /// Specified SVG Paint value
 pub type SVGPaint = generic::SVGPaint<Color, SpecifiedUrl>;
 
 /// Specified SVG Paint Kind value
@@ -238,37 +238,100 @@ impl ToCss for SVGPaintOrder {
                 dest.write_str(" ")?
             }
             self.order_at(pos).to_css(dest)?;
         }
         Ok(())
     }
 }
 
+bitflags! {
+    /// The context properties we understand.
+    #[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
+    #[repr(C)]
+    pub struct ContextPropertyBits: u8 {
+        /// `fill`
+        const FILL = 1 << 0;
+        /// `stroke`
+        const STROKE = 1 << 1;
+        /// `fill-opacity`
+        const FILL_OPACITY = 1 << 2;
+        /// `stroke-opacity`
+        const STROKE_OPACITY = 1 << 3;
+    }
+}
+
 /// Specified MozContextProperties value.
 /// Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)
 #[derive(
     Clone,
     Debug,
+    Default,
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToComputedValue,
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
-pub struct MozContextProperties(pub CustomIdent);
+#[repr(C)]
+pub struct MozContextProperties {
+    #[css(iterable, if_empty = "none")]
+    #[ignore_malloc_size_of = "Arc"]
+    idents: crate::ArcSlice<CustomIdent>,
+    #[css(skip)]
+    bits: ContextPropertyBits,
+}
 
 impl Parse for MozContextProperties {
     fn parse<'i, 't>(
         _context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<MozContextProperties, ParseError<'i>> {
-        let location = input.current_source_location();
-        let i = input.expect_ident()?;
-        Ok(MozContextProperties(CustomIdent::from_ident(
-            location,
-            i,
-            &["all", "none", "auto"],
-        )?))
+        let mut values = vec![];
+        let mut bits = ContextPropertyBits::empty();
+        loop {
+            {
+                let location = input.current_source_location();
+                let ident = input.expect_ident()?;
+
+                if ident.eq_ignore_ascii_case("none") && values.is_empty() {
+                    return Ok(Self::default());
+                }
+
+                let ident = CustomIdent::from_ident(
+                    location,
+                    ident,
+                    &["all", "none", "auto"],
+                )?;
+
+                if ident.0 == atom!("fill") {
+                    bits.insert(ContextPropertyBits::FILL);
+                } else if ident.0 == atom!("stroke") {
+                    bits.insert(ContextPropertyBits::STROKE);
+                } else if ident.0 == atom!("fill-opacity") {
+                    bits.insert(ContextPropertyBits::FILL_OPACITY);
+                } else if ident.0 == atom!("stroke-opacity") {
+                    bits.insert(ContextPropertyBits::STROKE_OPACITY);
+                }
+
+                values.push(ident);
+            }
+
+            let location = input.current_source_location();
+            match input.next() {
+                Ok(&Token::Comma) => continue,
+                Err(..) => break,
+                Ok(other) => return Err(location.new_unexpected_token_error(other.clone())),
+            }
+        }
+
+        if values.is_empty() {
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
+        }
+
+        Ok(MozContextProperties {
+            idents: crate::ArcSlice::from_iter(values.into_iter()),
+            bits,
+        })
     }
 }
--- a/servo/components/style_traits/arc_slice.rs
+++ b/servo/components/style_traits/arc_slice.rs
@@ -80,16 +80,26 @@ impl<T> ArcSlice<T> {
     #[allow(unsafe_code)]
     pub fn forget(self) -> ForgottenArcSlicePtr<T> {
         let ret = unsafe {
             ForgottenArcSlicePtr(NonNull::new_unchecked(self.0.ptr() as *const _ as *mut _))
         };
         mem::forget(self);
         ret
     }
+
+    /// Leaks an empty arc slice pointer, and returns it. Only to be used to
+    /// construct ArcSlices from FFI.
+    #[inline]
+    pub fn leaked_empty_ptr() -> *mut std::os::raw::c_void {
+        let empty: ArcSlice<_> = EMPTY_ARC_SLICE.clone();
+        let ptr = empty.0.ptr();
+        std::mem::forget(empty);
+        ptr as *mut _
+    }
 }
 
 /// The inner pointer of an ArcSlice<T>, to be sent via FFI.
 /// The type of the pointer is a bit of a lie, we just want to preserve the type
 /// but these pointers cannot be constructed outside of this crate, so we're
 /// good.
 #[repr(C)]
 pub struct ForgottenArcSlicePtr<T>(NonNull<T>);
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -122,16 +122,17 @@ include = [
   "ColorOrAuto",
   "GradientItem",
   "VerticalAlign",
   "BasicShape",
   "ShapeRadius",
   "ArcSlice",
   "ForgottenArcSlicePtr",
   "HeaderWithLength",
+  "MozContextProperties",
 ]
 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"
@@ -374,17 +375,28 @@ renaming_overrides_prefixing = true
   }
 
   bool operator!=(const StyleOwnedSlice& other) const {
     return !(*this == other);
   }
 """
 
 "ArcSlice" = """
-  StyleArcSlice() = delete;
+  inline StyleArcSlice();
   inline StyleArcSlice(const StyleArcSlice& aOther);
   inline explicit StyleArcSlice(const StyleForgottenArcSlicePtr<T>& aPtr);
   inline ~StyleArcSlice();
   inline Span<const T> AsSpan() const;
   inline size_t Length() const;
   inline bool operator==(const StyleArcSlice& other) const;
   inline bool operator!=(const StyleArcSlice& other) const;
 """
+
+"Atom" = """
+  StyleAtom(size_t) = delete;
+  StyleAtom() = delete;
+
+  // NOTE(emilio): For now we don't need to expose anything else, but it'd be trivial if we wanted to.
+  inline bool IsStatic() const;
+
+  inline StyleAtom(const StyleAtom& aOther);
+  inline ~StyleAtom();
+"""
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -6406,8 +6406,13 @@ pub unsafe extern "C" fn Servo_SharedMem
 /// Returns a unique pointer to a clone of the shape image.
 ///
 /// Probably temporary, as we move more stuff to cbindgen.
 #[no_mangle]
 #[must_use]
 pub unsafe extern "C" fn Servo_CloneBasicShape(v: &computed::basic_shape::BasicShape) -> *mut computed::basic_shape::BasicShape {
     Box::into_raw(Box::new(v.clone()))
 }
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleArcSlice_EmptyPtr() -> *mut c_void {
+    style_traits::arc_slice::ArcSlice::<u64>::leaked_empty_ptr()
+}