Bug 1449068 part 2 - Use Servo data to back @counter-style rule. r?emilio draft
authorXidorn Quan <me@upsuper.org>
Wed, 04 Apr 2018 21:23:25 +1000
changeset 777681 8efa4efd9b7ca559cf356376e1c1e375a3102eec
parent 777680 d62f21e305bdfcf600d4d750bd4277f2f883de23
child 777682 19ac679c06e453e22c7bd124a72768ba28ac6b94
push id105263
push userxquan@mozilla.com
push dateThu, 05 Apr 2018 05:22:06 +0000
reviewersemilio
bugs1449068
milestone61.0a1
Bug 1449068 part 2 - Use Servo data to back @counter-style rule. r?emilio This patch basically does: * Add descriptor setters and generation count to CounterStyleRule in Servo. (This code is mostly based on the old code inside nsCSSCounterStyleRule for handling mutation.) * Use RawServoCounterStyleRule in CounterStyleManager. * Add ServoCounterStyleRule and remove nsCSSCounterStyleRule. Test change: * "fixed" was parsed as and thus serialized to "fixed 1", but Servo doesn't do so. It preserves whether the number presents. Either way is probably fine. MozReview-Commit-ID: EtKTeu32isi
dom/bindings/Bindings.conf
layout/style/CounterStyleManager.cpp
layout/style/ServoArcTypeList.h
layout/style/ServoBindingList.h
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/ServoBindings.toml
layout/style/ServoCSSParser.cpp
layout/style/ServoCSSParser.h
layout/style/ServoCSSRuleList.cpp
layout/style/ServoCounterStyleRule.cpp
layout/style/ServoCounterStyleRule.h
layout/style/ServoStyleSet.cpp
layout/style/ServoStyleSet.h
layout/style/moz.build
layout/style/nsCSSCounterStyleRule.cpp
layout/style/nsCSSCounterStyleRule.h
layout/style/test/test_counter_descriptor_storage.html
servo/components/style/counter_style/mod.rs
servo/components/style/gecko/arc_types.rs
servo/components/style/gecko/rules.rs
servo/components/style/gecko_bindings/sugar/refptr.rs
servo/components/style/stylesheets/counter_style_rule.rs
servo/components/style/stylesheets/mod.rs
servo/components/style/stylist.rs
servo/ports/geckolib/glue.rs
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -177,18 +177,18 @@ DOMInterfaces = {
 
 'CSSConditionRule': {
     'concrete': False,
     'nativeType': 'mozilla::css::ConditionRule',
     'headerFile': 'mozilla/css/GroupRule.h',
 },
 
 'CSSCounterStyleRule': {
-    'nativeType': 'nsCSSCounterStyleRule',
-    'headerFile': 'nsCSSCounterStyleRule.h',
+    'nativeType': 'mozilla::ServoCounterStyleRule',
+    'headerFile': 'mozilla/ServoCounterStyleRule.h',
 },
 
 'CSSFontFaceRule': {
     'nativeType': 'mozilla::ServoFontFaceRule',
     'headerFile': 'mozilla/ServoFontFaceRule.h',
 },
 
 'CSSGroupingRule': {
--- a/layout/style/CounterStyleManager.cpp
+++ b/layout/style/CounterStyleManager.cpp
@@ -7,21 +7,21 @@
 #include "CounterStyleManager.h"
 
 #include "mozilla/ArenaObjectID.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Types.h"
 #include "mozilla/WritingModes.h"
-#include "nsCSSCounterStyleRule.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"
 #include "nsUnicodeProperties.h"
+#include "mozilla/ServoBindings.h"
 #include "mozilla/ServoStyleSet.h"
 
 namespace mozilla {
 
 struct AdditiveSymbol
 {
   CounterValue weight;
   nsString symbol;
@@ -1025,23 +1025,23 @@ DependentBuiltinCounterStyle::GetFallbac
   }
 }
 
 class CustomCounterStyle final : public CounterStyle
 {
 public:
   CustomCounterStyle(nsAtom* aName,
                      CounterStyleManager* aManager,
-                     nsCSSCounterStyleRule* aRule)
+                     const RawServoCounterStyleRule* aRule)
     : CounterStyle(NS_STYLE_LIST_STYLE_CUSTOM),
       mName(aName),
       mManager(aManager),
       mRule(aRule),
-      mRuleGeneration(aRule->GetGeneration()),
-      mSystem(aRule->GetSystem()),
+      mRuleGeneration(Servo_CounterStyleRule_GetGeneration(aRule)),
+      mSystem(Servo_CounterStyleRule_GetSystem(aRule)),
       mFlags(0),
       mFallback(nullptr),
       mSpeakAsCounter(nullptr),
       mExtends(nullptr),
       mExtendsRoot(nullptr)
   {
   }
 
@@ -1052,17 +1052,17 @@ public:
 
   // This method will reset all cached data which may depend on other
   // counter style. It will reset all pointers to other counter styles.
   // For counter style extends other, in addition, all fields will be
   // reset to uninitialized state. This method should be called when any
   // other counter style is added, removed, or changed.
   void ResetDependentData();
 
-  nsCSSCounterStyleRule* GetRule() const { return mRule; }
+  const RawServoCounterStyleRule* GetRule() const { return mRule; }
   uint32_t GetRuleGeneration() const { return mRuleGeneration; }
 
   virtual nsAtom* GetStyleName() const override;
   virtual void GetPrefix(nsAString& aResult) override;
   virtual void GetSuffix(nsAString& aResult) override;
   virtual void GetSpokenCounterText(CounterValue aOrdinal,
                                     WritingMode aWritingMode,
                                     nsAString& aResult,
@@ -1102,16 +1102,23 @@ public:
     nsIPresShell* shell = mManager->PresContext()->PresShell();
     this->~CustomCounterStyle();
     shell->FreeByObjectID(eArenaObjectID_CustomCounterStyle, this);
   }
 
 private:
   ~CustomCounterStyle() {}
 
+  nsCSSValue GetDesc(nsCSSCounterDesc aDesc) const
+  {
+    nsCSSValue value;
+    Servo_CounterStyleRule_GetDescriptor(mRule, aDesc, &value);
+    return value;
+  }
+
   const nsTArray<nsString>& GetSymbols();
   const nsTArray<AdditiveSymbol>& GetAdditiveSymbols();
 
   // The speak-as values of counter styles may form a loop, and the
   // loops may have complex interaction with the loop formed by
   // extending. To solve this problem, the computation of speak-as is
   // divided into two phases:
   // 1. figure out the raw value, by ComputeRawSpeakAs, and
@@ -1128,17 +1135,17 @@ private:
 
   RefPtr<nsAtom> mName;
 
   // CounterStyleManager should always overlive any CounterStyle as it
   // is owned by nsPresContext, and will be released after all nodes and
   // frames are released.
   CounterStyleManager* mManager;
 
-  RefPtr<nsCSSCounterStyleRule> mRule;
+  RefPtr<const RawServoCounterStyleRule> mRule;
   uint32_t mRuleGeneration;
 
   uint8_t mSystem;
   // GetSpeakAs will ensure that private member mSpeakAs is initialized before used
   MOZ_INIT_OUTSIDE_CTOR uint8_t mSpeakAs;
 
   enum {
     // loop detection
@@ -1193,17 +1200,17 @@ CustomCounterStyle::ResetCachedData()
               FLAG_PREFIX_INITED |
               FLAG_SUFFIX_INITED |
               FLAG_PAD_INITED |
               FLAG_SPEAKAS_INITED);
   mFallback = nullptr;
   mSpeakAsCounter = nullptr;
   mExtends = nullptr;
   mExtendsRoot = nullptr;
-  mRuleGeneration = mRule->GetGeneration();
+  mRuleGeneration = Servo_CounterStyleRule_GetGeneration(mRule);
 }
 
 void
 CustomCounterStyle::ResetDependentData()
 {
   mFlags &= ~FLAG_SPEAKAS_INITED;
   mSpeakAsCounter = nullptr;
   mFallback = nullptr;
@@ -1224,17 +1231,17 @@ CustomCounterStyle::GetStyleName() const
 }
 
 /* virtual */ void
 CustomCounterStyle::GetPrefix(nsAString& aResult)
 {
   if (!(mFlags & FLAG_PREFIX_INITED)) {
     mFlags |= FLAG_PREFIX_INITED;
 
-    const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Prefix);
+    nsCSSValue value = GetDesc(eCSSCounterDesc_Prefix);
     if (value.UnitHasStringValue()) {
       value.GetStringValue(mPrefix);
     } else if (IsExtendsSystem()) {
       GetExtends()->GetPrefix(mPrefix);
     } else {
       mPrefix.Truncate();
     }
   }
@@ -1242,17 +1249,17 @@ CustomCounterStyle::GetPrefix(nsAString&
 }
 
 /* virtual */ void
 CustomCounterStyle::GetSuffix(nsAString& aResult)
 {
   if (!(mFlags & FLAG_SUFFIX_INITED)) {
     mFlags |= FLAG_SUFFIX_INITED;
 
-    const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Suffix);
+    nsCSSValue value = GetDesc(eCSSCounterDesc_Suffix);
     if (value.UnitHasStringValue()) {
       value.GetStringValue(mSuffix);
     } else if (IsExtendsSystem()) {
       GetExtends()->GetSuffix(mSuffix);
     } else {
       mSuffix.AssignLiteral(u". ");
     }
   }
@@ -1290,17 +1297,17 @@ CustomCounterStyle::IsBullet()
   }
 }
 
 /* virtual */ void
 CustomCounterStyle::GetNegative(NegativeType& aResult)
 {
   if (!(mFlags & FLAG_NEGATIVE_INITED)) {
     mFlags |= FLAG_NEGATIVE_INITED;
-    const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Negative);
+    nsCSSValue value = GetDesc(eCSSCounterDesc_Negative);
     switch (value.GetUnit()) {
       case eCSSUnit_Ident:
       case eCSSUnit_String:
         value.GetStringValue(mNegative.before);
         mNegative.after.Truncate();
         break;
       case eCSSUnit_Pair: {
         const nsCSSValuePair& pair = value.GetPairValue();
@@ -1326,17 +1333,17 @@ IsRangeValueInfinite(const nsCSSValue& a
 {
   return aValue.GetUnit() == eCSSUnit_Enumerated &&
          aValue.GetIntValue() == NS_STYLE_COUNTER_RANGE_INFINITE;
 }
 
 /* virtual */ bool
 CustomCounterStyle::IsOrdinalInRange(CounterValue aOrdinal)
 {
-  const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Range);
+  nsCSSValue value = GetDesc(eCSSCounterDesc_Range);
   if (value.GetUnit() == eCSSUnit_PairList) {
     for (const nsCSSValuePairList* item = value.GetPairListValue();
          item != nullptr; item = item->mNext) {
       const nsCSSValue& lowerBound = item->mXValue;
       const nsCSSValue& upperBound = item->mYValue;
       if ((IsRangeValueInfinite(lowerBound) ||
            aOrdinal >= lowerBound.GetIntValue()) &&
           (IsRangeValueInfinite(upperBound) ||
@@ -1373,17 +1380,17 @@ CustomCounterStyle::IsOrdinalInAutoRange
   }
 }
 
 /* virtual */ void
 CustomCounterStyle::GetPad(PadType& aResult)
 {
   if (!(mFlags & FLAG_PAD_INITED)) {
     mFlags |= FLAG_PAD_INITED;
-    const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Pad);
+    nsCSSValue value = GetDesc(eCSSCounterDesc_Pad);
     if (value.GetUnit() == eCSSUnit_Pair) {
       const nsCSSValuePair& pair = value.GetPairValue();
       mPad.width = pair.mXValue.GetIntValue();
       pair.mYValue.GetStringValue(mPad.symbol);
     } else if (IsExtendsSystem()) {
       GetExtends()->GetPad(mPad);
     } else {
       mPad.width = 0;
@@ -1392,24 +1399,19 @@ CustomCounterStyle::GetPad(PadType& aRes
   }
   aResult = mPad;
 }
 
 /* virtual */ CounterStyle*
 CustomCounterStyle::GetFallback()
 {
   if (!mFallback) {
-    const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Fallback);
     mFallback = CounterStyleManager::GetDecimalStyle();
-    if (value.GetUnit() != eCSSUnit_Null) {
-      if (value.GetUnit() == eCSSUnit_AtomIdent) {
-        mFallback = mManager->BuildCounterStyle(value.GetAtomValue());
-      } else {
-        MOZ_ASSERT_UNREACHABLE("Unknown unit!");
-      }
+    if (nsAtom* fallback = Servo_CounterStyleRule_GetFallback(mRule)) {
+      mFallback = mManager->BuildCounterStyle(fallback);
     } else if (IsExtendsSystem()) {
       mFallback = GetExtends()->GetFallback();
     }
   }
   return mFallback;
 }
 
 /* virtual */ uint8_t
@@ -1449,17 +1451,17 @@ CustomCounterStyle::GetInitialCounterTex
                                           WritingMode aWritingMode,
                                           nsAString& aResult,
                                           bool& aIsRTL)
 {
   switch (mSystem) {
     case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
       return GetCyclicCounterText(aOrdinal, aResult, GetSymbols());
     case NS_STYLE_COUNTER_SYSTEM_FIXED: {
-      int32_t start = mRule->GetSystemArgument().GetIntValue();
+      int32_t start = Servo_CounterStyleRule_GetFixedFirstValue(mRule);
       return GetFixedCounterText(aOrdinal, aResult, start, GetSymbols());
     }
     case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC:
       return GetSymbolicCounterText(aOrdinal, aResult, GetSymbols());
     case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
       return GetAlphabeticCounterText(aOrdinal, aResult, GetSymbols());
     case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
       return GetNumericCounterText(aOrdinal, aResult, GetSymbols());
@@ -1473,32 +1475,32 @@ CustomCounterStyle::GetInitialCounterTex
       return false;
   }
 }
 
 const nsTArray<nsString>&
 CustomCounterStyle::GetSymbols()
 {
   if (mSymbols.IsEmpty()) {
-    const nsCSSValue& values = mRule->GetDesc(eCSSCounterDesc_Symbols);
+    nsCSSValue values = GetDesc(eCSSCounterDesc_Symbols);
     for (const nsCSSValueList* item = values.GetListValue();
          item; item = item->mNext) {
       nsString* symbol = mSymbols.AppendElement();
       item->mValue.GetStringValue(*symbol);
     }
     mSymbols.Compact();
   }
   return mSymbols;
 }
 
 const nsTArray<AdditiveSymbol>&
 CustomCounterStyle::GetAdditiveSymbols()
 {
   if (mAdditiveSymbols.IsEmpty()) {
-    const nsCSSValue& values = mRule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
+    nsCSSValue values = GetDesc(eCSSCounterDesc_AdditiveSymbols);
     for (const nsCSSValuePairList* item = values.GetPairListValue();
          item; item = item->mNext) {
       AdditiveSymbol* symbol = mAdditiveSymbols.AppendElement();
       symbol->weight = item->mXValue.GetIntValue();
       item->mYValue.GetStringValue(symbol->symbol);
     }
     mAdditiveSymbols.Compact();
   }
@@ -1529,17 +1531,17 @@ CustomCounterStyle::GetSpeakAsAutoValue(
 // fields in the style.)
 void
 CustomCounterStyle::ComputeRawSpeakAs(uint8_t& aSpeakAs,
                                     CounterStyle*& aSpeakAsCounter)
 {
   NS_ASSERTION(!(mFlags & FLAG_SPEAKAS_INITED),
                "ComputeRawSpeakAs is called with speak-as inited.");
 
-  const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_SpeakAs);
+  nsCSSValue value = GetDesc(eCSSCounterDesc_SpeakAs);
   switch (value.GetUnit()) {
     case eCSSUnit_Auto:
       aSpeakAs = GetSpeakAsAutoValue();
       break;
     case eCSSUnit_Enumerated:
       aSpeakAs = value.GetIntValue();
       break;
     case eCSSUnit_AtomIdent:
@@ -1645,18 +1647,18 @@ CustomCounterStyle::ComputeExtends()
     return this;
   }
   if (mFlags & FLAG_EXTENDS_VISITED) {
     // loop detected
     mFlags |= FLAG_EXTENDS_LOOP;
     return nullptr;
   }
 
-  const nsCSSValue& value = mRule->GetSystemArgument();
-  CounterStyle* nextCounter = mManager->BuildCounterStyle(value.GetAtomValue());
+  nsAtom* extended = Servo_CounterStyleRule_GetExtended(mRule);
+  CounterStyle* nextCounter = mManager->BuildCounterStyle(extended);
   CounterStyle* target = nextCounter;
   if (nextCounter->IsCustomStyle()) {
     mFlags |= FLAG_EXTENDS_VISITED;
     target = static_cast<CustomCounterStyle*>(nextCounter)->ComputeExtends();
     mFlags &= ~FLAG_EXTENDS_VISITED;
   }
 
   if (target) {
@@ -2030,19 +2032,19 @@ CounterStyleManager::BuildCounterStyle(n
   CounterStyle* data = GetCounterStyle(aName);
   if (data) {
     return data;
   }
 
   // Names are compared case-sensitively here. Predefined names should
   // have been lowercased by the parser.
   ServoStyleSet* styleSet = mPresContext->StyleSet();
-  nsCSSCounterStyleRule* rule = styleSet->CounterStyleRuleForName(aName);
+  auto* rule = styleSet->CounterStyleRuleForName(aName);
   if (rule) {
-    MOZ_ASSERT(rule->Name() == aName);
+    MOZ_ASSERT(Servo_CounterStyleRule_GetName(rule) == aName);
     data = new (mPresContext) CustomCounterStyle(aName, this, rule);
   } else {
     for (const BuiltinCounterStyle& item : gBuiltinStyleTable) {
       if (item.GetStyleName() == aName) {
         int32_t style = item.GetStyle();
         data = item.IsDependentStyle()
           ? new (mPresContext) DependentBuiltinCounterStyle(style, this)
           : GetBuiltinStyle(style);
@@ -2081,31 +2083,34 @@ bool
 CounterStyleManager::NotifyRuleChanged()
 {
   bool changed = false;
   for (auto iter = mStyles.Iter(); !iter.Done(); iter.Next()) {
     CounterStyle* style = iter.Data();
     bool toBeUpdated = false;
     bool toBeRemoved = false;
     ServoStyleSet* styleSet = mPresContext->StyleSet();
-    nsCSSCounterStyleRule* newRule = styleSet->CounterStyleRuleForName(iter.Key());
+    auto* newRule = styleSet->CounterStyleRuleForName(iter.Key());
     if (!newRule) {
       if (style->IsCustomStyle()) {
         toBeRemoved = true;
       }
     } else {
       if (!style->IsCustomStyle()) {
         toBeRemoved = true;
       } else {
         auto custom = static_cast<CustomCounterStyle*>(style);
         if (custom->GetRule() != newRule) {
           toBeRemoved = true;
-        } else if (custom->GetRuleGeneration() != newRule->GetGeneration()) {
-          toBeUpdated = true;
-          custom->ResetCachedData();
+        } else {
+          auto generation = Servo_CounterStyleRule_GetGeneration(newRule);
+          if (custom->GetRuleGeneration() != generation) {
+            toBeUpdated = true;
+            custom->ResetCachedData();
+          }
         }
       }
     }
     changed = changed || toBeUpdated || toBeRemoved;
     if (toBeRemoved) {
       if (style->IsDependentStyle()) {
         // Add object to retired list so we can clean them up later.
         mRetiredStyles.AppendElement(style);
--- a/layout/style/ServoArcTypeList.h
+++ b/layout/style/ServoArcTypeList.h
@@ -18,8 +18,9 @@ SERVO_ARC_TYPE(MediaList, RawServoMediaL
 SERVO_ARC_TYPE(MediaRule, RawServoMediaRule)
 SERVO_ARC_TYPE(NamespaceRule, RawServoNamespaceRule)
 SERVO_ARC_TYPE(PageRule, RawServoPageRule)
 SERVO_ARC_TYPE(SupportsRule, RawServoSupportsRule)
 SERVO_ARC_TYPE(DocumentRule, RawServoDocumentRule)
 SERVO_ARC_TYPE(FontFeatureValuesRule, RawServoFontFeatureValuesRule)
 SERVO_ARC_TYPE(RuleNode, RawServoRuleNode)
 SERVO_ARC_TYPE(FontFaceRule, RawServoFontFaceRule)
+SERVO_ARC_TYPE(CounterStyleRule, RawServoCounterStyleRule)
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -126,17 +126,18 @@ SERVO_BINDING_FUNC(Servo_StyleSet_NoteSt
 SERVO_BINDING_FUNC(Servo_StyleSet_GetKeyframesForName, bool,
                    RawServoStyleSetBorrowed set,
                    nsAtom* name,
                    nsTimingFunctionBorrowed timing_function,
                    RawGeckoKeyframeListBorrowedMut keyframe_list)
 SERVO_BINDING_FUNC(Servo_StyleSet_GetFontFaceRules, void,
                    RawServoStyleSetBorrowed set,
                    RawGeckoFontFaceRuleListBorrowedMut list)
-SERVO_BINDING_FUNC(Servo_StyleSet_GetCounterStyleRule, nsCSSCounterStyleRule*,
+SERVO_BINDING_FUNC(Servo_StyleSet_GetCounterStyleRule,
+                   const RawServoCounterStyleRule*,
                    RawServoStyleSetBorrowed set, nsAtom* name)
 // This function may return nullptr or gfxFontFeatureValueSet with zero reference.
 SERVO_BINDING_FUNC(Servo_StyleSet_BuildFontFeatureValueSet,
                    gfxFontFeatureValueSet*,
                    RawServoStyleSetBorrowed set)
 SERVO_BINDING_FUNC(Servo_StyleSet_ResolveForDeclarations,
                    ComputedStyleStrong,
                    RawServoStyleSetBorrowed set,
@@ -258,21 +259,20 @@ BASIC_RULE_FUNCS_WITHOUT_GETTER(Keyframe
 BASIC_RULE_FUNCS(Keyframes)
 GROUP_RULE_FUNCS(Media)
 BASIC_RULE_FUNCS(Namespace)
 BASIC_RULE_FUNCS(Page)
 GROUP_RULE_FUNCS(Supports)
 GROUP_RULE_FUNCS(Document)
 BASIC_RULE_FUNCS(FontFeatureValues)
 BASIC_RULE_FUNCS(FontFace)
+BASIC_RULE_FUNCS(CounterStyle)
 #undef GROUP_RULE_FUNCS
 #undef BASIC_RULE_FUNCS
 #undef BASIC_RULE_FUNCS_WITHOUT_GETTER
-SERVO_BINDING_FUNC(Servo_CssRules_GetCounterStyleRuleAt, nsCSSCounterStyleRule*,
-                   ServoCssRulesBorrowed rules, uint32_t index)
 SERVO_BINDING_FUNC(Servo_StyleRule_GetStyle, RawServoDeclarationBlockStrong,
                    RawServoStyleRuleBorrowed rule)
 SERVO_BINDING_FUNC(Servo_StyleRule_SetStyle, void,
                    RawServoStyleRuleBorrowed rule,
                    RawServoDeclarationBlockBorrowed declarations)
 SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorText, void,
                    RawServoStyleRuleBorrowed rule, nsAString* result)
 SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorTextAtIndex, void,
@@ -362,16 +362,40 @@ SERVO_BINDING_FUNC(Servo_FontFaceRule_Ge
                    nsCSSFontDesc desc, nsAString* result)
 SERVO_BINDING_FUNC(Servo_FontFaceRule_SetDescriptor, bool,
                    RawServoFontFaceRuleBorrowed rule,
                    nsCSSFontDesc desc, const nsACString* value,
                    RawGeckoURLExtraData* data)
 SERVO_BINDING_FUNC(Servo_FontFaceRule_ResetDescriptor, void,
                    RawServoFontFaceRuleBorrowed rule,
                    nsCSSFontDesc desc)
+SERVO_BINDING_FUNC(Servo_CounterStyleRule_GetName, nsAtom*,
+                   RawServoCounterStyleRuleBorrowed rule)
+SERVO_BINDING_FUNC(Servo_CounterStyleRule_SetName, bool,
+                   RawServoCounterStyleRuleBorrowed rule,
+                   const nsACString* name)
+SERVO_BINDING_FUNC(Servo_CounterStyleRule_GetGeneration, uint32_t,
+                   RawServoCounterStyleRuleBorrowed rule)
+SERVO_BINDING_FUNC(Servo_CounterStyleRule_GetSystem, uint8_t,
+                   RawServoCounterStyleRuleBorrowed rule)
+SERVO_BINDING_FUNC(Servo_CounterStyleRule_GetExtended, nsAtom*,
+                   RawServoCounterStyleRuleBorrowed rule)
+SERVO_BINDING_FUNC(Servo_CounterStyleRule_GetFixedFirstValue, int32_t,
+                   RawServoCounterStyleRuleBorrowed rule)
+SERVO_BINDING_FUNC(Servo_CounterStyleRule_GetFallback, nsAtom*,
+                   RawServoCounterStyleRuleBorrowed rule)
+SERVO_BINDING_FUNC(Servo_CounterStyleRule_GetDescriptor, void,
+                   RawServoCounterStyleRuleBorrowed rule,
+                   nsCSSCounterDesc desc, nsCSSValueBorrowedMut result)
+SERVO_BINDING_FUNC(Servo_CounterStyleRule_GetDescriptorCssText, void,
+                   RawServoCounterStyleRuleBorrowed rule,
+                   nsCSSCounterDesc desc, nsAString* result)
+SERVO_BINDING_FUNC(Servo_CounterStyleRule_SetDescriptor, bool,
+                   RawServoCounterStyleRuleBorrowed rule,
+                   nsCSSCounterDesc desc, const nsACString* value)
 
 // Animations API
 SERVO_BINDING_FUNC(Servo_ParseProperty,
                    RawServoDeclarationBlockStrong,
                    nsCSSPropertyID property, const nsACString* value,
                    RawGeckoURLExtraData* data,
                    mozilla::ParsingMode parsing_mode,
                    nsCompatibility quirks_mode,
@@ -817,23 +841,16 @@ SERVO_BINDING_FUNC(Servo_ParseIntersecti
                    nsCSSRect* result);
 // Returning false means the parsed transform contains relative lengths or
 // percentage value, so we cannot compute the matrix. In this case, we keep
 // |result| and |contains_3d_transform| as-is.
 SERVO_BINDING_FUNC(Servo_ParseTransformIntoMatrix, bool,
                    const nsAString* value,
                    bool* contains_3d_transform,
                    RawGeckoGfxMatrix4x4* result);
-SERVO_BINDING_FUNC(Servo_ParseCounterStyleName, nsAtom*,
-                   const nsACString* value);
-SERVO_BINDING_FUNC(Servo_ParseCounterStyleDescriptor, bool,
-                   nsCSSCounterDesc aDescriptor,
-                   const nsACString* aValue,
-                   RawGeckoURLExtraData* aURLExtraData,
-                   nsCSSValue* aResult);
 SERVO_BINDING_FUNC(Servo_ParseFontShorthandForMatching, bool,
                    const nsAString* value,
                    RawGeckoURLExtraData* data,
                    RefPtr<SharedFontList>* family,
                    nsCSSValueBorrowedMut style,
                    nsCSSValueBorrowedMut stretch,
                    nsCSSValueBorrowedMut weight);
 
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -8,17 +8,16 @@
 
 #include "ChildIterator.h"
 #include "ErrorReporter.h"
 #include "GeckoProfiler.h"
 #include "gfxFontFamilyList.h"
 #include "gfxFontFeatures.h"
 #include "nsAnimationManager.h"
 #include "nsAttrValueInlines.h"
-#include "nsCSSCounterStyleRule.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsCSSProps.h"
 #include "nsCSSParser.h"
 #include "nsCSSPseudoElements.h"
 #include "nsContentUtils.h"
 #include "nsDOMTokenList.h"
 #include "nsDeviceContext.h"
 #include "nsIContentInlines.h"
@@ -2596,40 +2595,16 @@ Gecko_RegisterNamespace(nsAtom* aNamespa
   nsresult rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(str, id);
 
   if (NS_FAILED(rv)) {
     return -1;
   }
   return id;
 }
 
-nsCSSCounterStyleRule*
-Gecko_CSSCounterStyle_Create(nsAtom* aName)
-{
-  RefPtr<nsCSSCounterStyleRule> rule = new nsCSSCounterStyleRule(aName, 0, 0);
-  return rule.forget().take();
-}
-
-nsCSSCounterStyleRule*
-Gecko_CSSCounterStyle_Clone(const nsCSSCounterStyleRule* aRule)
-{
-  RefPtr<css::Rule> rule = aRule->Clone();
-  return static_cast<nsCSSCounterStyleRule*>(rule.forget().take());
-}
-
-void
-Gecko_CSSCounterStyle_GetCssText(const nsCSSCounterStyleRule* aRule,
-                                 nsAString* aResult)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  aRule->GetCssText(*aResult);
-}
-
-NS_IMPL_FFI_REFCOUNTING(nsCSSCounterStyleRule, CSSCounterStyleRule);
-
 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsCSSValueSharedList, CSSValueSharedList);
 
 #define STYLE_STRUCT(name)                                                    \
                                                                               \
 void                                                                          \
 Gecko_Construct_Default_nsStyle##name(nsStyle##name* ptr,                     \
                                       const nsPresContext* pres_context)      \
 {                                                                             \
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -57,17 +57,16 @@ namespace mozilla {
   class ServoStyleSheet;
   class ServoElementSnapshotTable;
 }
 using mozilla::FontFamilyList;
 using mozilla::FontFamilyName;
 using mozilla::FontFamilyType;
 using mozilla::ServoElementSnapshot;
 using mozilla::SharedFontList;
-class nsCSSCounterStyleRule;
 struct nsMediaFeature;
 class nsSimpleContentList;
 struct nsStyleList;
 struct nsStyleImage;
 struct nsStyleGradientStop;
 class nsStyleGradient;
 class nsStyleCoord;
 struct nsStyleDisplay;
@@ -623,23 +622,16 @@ mozilla::ServoStyleSheet* Gecko_StyleShe
     const mozilla::ServoStyleSheet* aSheet,
     const mozilla::ServoStyleSheet* aNewParentSheet);
 void Gecko_StyleSheet_AddRef(const mozilla::ServoStyleSheet* aSheet);
 void Gecko_StyleSheet_Release(const mozilla::ServoStyleSheet* aSheet);
 
 nsCSSKeyword Gecko_LookupCSSKeyword(const uint8_t* string, uint32_t len);
 const char* Gecko_CSSKeywordString(nsCSSKeyword keyword, uint32_t* len);
 
-// Counter style rule
-// Creates and returns a new (already-addrefed) nsCSSCounterStyleRule object.
-nsCSSCounterStyleRule* Gecko_CSSCounterStyle_Create(nsAtom* name);
-nsCSSCounterStyleRule* Gecko_CSSCounterStyle_Clone(const nsCSSCounterStyleRule* rule);
-void Gecko_CSSCounterStyle_GetCssText(const nsCSSCounterStyleRule* rule, nsAString* result);
-NS_DECL_FFI_REFCOUNTING(nsCSSCounterStyleRule, CSSCounterStyleRule);
-
 bool Gecko_IsDocumentBody(RawGeckoElementBorrowed element);
 
 // We use an int32_t here instead of a LookAndFeel::ColorID
 // because forward-declaring a nested enum/struct is impossible
 nscolor Gecko_GetLookAndFeelSystemColor(int32_t color_id,
                                         RawGeckoPresContextBorrowed pres_context);
 
 void Gecko_AddPropertyToSet(nsCSSPropertyIDSetBorrowedMut, nsCSSPropertyID);
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -73,17 +73,16 @@ headers = [
     "mozilla/LookAndFeel.h",
     "mozilla/StaticPrefs.h",
     "mozilla/ServoBindings.h",
     "mozilla/ServoMediaList.h",
     "mozilla/ComputedStyle.h",
     "mozilla/ServoDeclarationBlock.h",
     "mozilla/ServoTraversalStatistics.h",
     "mozilla/SizeOfState.h",
-    "nsCSSCounterStyleRule.h",
     "nsContentUtils.h",
     "nsNameSpaceManager.h",
     "nsMediaFeatures.h",
     "nsXBLBinding.h",
 ]
 raw-lines = [
     # FIXME(emilio): Incrementally remove these "pub use"s. Probably
     # mozilla::css and mozilla::dom are easier.
@@ -273,17 +272,16 @@ whitelist-types = [
     "ImageURL",
     "Keyframe",
     "MediumFeaturesChangedResult",
     "nsAttrName",
     "nsAttrValue",
     "nscolor",
     "nsChangeHint",
     "nsCSSCounterDesc",
-    "nsCSSCounterStyleRule",
     "nsCSSFontDesc",
     "nsCSSKeyword",
     "nsCSSPropertyID",
     "nsCSSPropertyIDSet",
     "nsCSSProps",
     "nsCSSRect",
     "nsCSSRect_heap",
     "nsCSSShadowArray",
@@ -421,17 +419,16 @@ opaque-types = [
     "mozilla::Maybe",
     "gfxSize",  # <- union { struct { T width; T height; }; T components[2] };
     "gfxSize_Super",  # Ditto.
     "mozilla::StyleAnimationValue",
     "StyleAnimationValue", # pulls in a whole bunch of stuff we don't need in the bindings
     "mozilla::dom::.*Callback", # Pulls in ErrorResult and other things that
                                 # don't align properly on Linux 32-bit
     "mozilla::SchedulerGroup", # Non-standard-layout packing of field into superclass
-    "nsCSSCounterStyleRule_Getter", # thiscall function pointer
 ]
 mapped-generic-types = [
     { generic = true, gecko = "mozilla::ServoUnsafeCell", servo = "::std::cell::UnsafeCell" },
     { generic = true, gecko = "mozilla::ServoCell", 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>" },
@@ -528,17 +525,16 @@ structs-types = [
     "ServoElementSnapshotTable",
     "ServoStyleSetSizes",
     "SheetParsingMode",
     "StyleBasicShapeType",
     "StyleShapeSource",
     "StyleTransition",
     "gfxFontFeatureValueSet",
     "nsCSSCounterDesc",
-    "nsCSSCounterStyleRule",
     "nsCSSFontDesc",
     "nsCSSKeyword",
     "nsCSSPropertyID",
     "nsCSSPropertyIDSet",
     "nsCSSRect",
     "nsCSSShadowArray",
     "nsCSSUnit",
     "nsCSSValue",
--- a/layout/style/ServoCSSParser.cpp
+++ b/layout/style/ServoCSSParser.cpp
@@ -35,35 +35,16 @@ ServoCSSParser::ComputeColor(ServoStyleS
 
 /* static */ bool
 ServoCSSParser::ParseIntersectionObserverRootMargin(const nsAString& aValue,
                                                     nsCSSRect* aResult)
 {
   return Servo_ParseIntersectionObserverRootMargin(&aValue, aResult);
 }
 
-/* static */ already_AddRefed<nsAtom>
-ServoCSSParser::ParseCounterStyleName(const nsAString& aValue)
-{
-  NS_ConvertUTF16toUTF8 value(aValue);
-  nsAtom* atom = Servo_ParseCounterStyleName(&value);
-  return already_AddRefed<nsAtom>(atom);
-}
-
-/* static */ bool
-ServoCSSParser::ParseCounterStyleDescriptor(nsCSSCounterDesc aDescriptor,
-                                            const nsAString& aValue,
-                                            URLExtraData* aURLExtraData,
-                                            nsCSSValue& aResult)
-{
-  NS_ConvertUTF16toUTF8 value(aValue);
-  return Servo_ParseCounterStyleDescriptor(aDescriptor, &value, aURLExtraData,
-                                           &aResult);
-}
-
 /* static */ already_AddRefed<RawServoDeclarationBlock>
 ServoCSSParser::ParseProperty(nsCSSPropertyID aProperty,
                               const nsAString& aValue,
                               const ParsingEnvironment& aParsingEnvironment,
                               ParsingMode aParsingMode)
 {
   NS_ConvertUTF16toUTF8 value(aValue);
   return Servo_ParseProperty(aProperty,
--- a/layout/style/ServoCSSParser.h
+++ b/layout/style/ServoCSSParser.h
@@ -79,41 +79,16 @@ public:
    * @param aValue The rootMargin value.
    * @param aResult The nsCSSRect object to write the result into.
    * @return Whether the value was successfully parsed.
    */
   static bool ParseIntersectionObserverRootMargin(const nsAString& aValue,
                                                   nsCSSRect* aResult);
 
   /**
-   * Parses a @counter-style name.
-   *
-   * @param aValue The name to parse.
-   * @return The name as an atom, lowercased if a built-in counter style name,
-   *   or nullptr if parsing failed or if the name was invalid (like "inherit").
-   */
-  static already_AddRefed<nsAtom> ParseCounterStyleName(const nsAString& aValue);
-
-  /**
-   * Parses a @counter-style descriptor.
-   *
-   * @param aDescriptor The descriptor to parse.
-   * @param aValue The value of the descriptor.
-   * @param aURLExtraData URL data for parsing. This would be used for
-   *   image value URL resolution.
-   * @param aResult The nsCSSValue to store the result in.
-   * @return Whether parsing succeeded.
-   */
-  static bool
-  ParseCounterStyleDescriptor(nsCSSCounterDesc aDescriptor,
-                              const nsAString& aValue,
-                              URLExtraData* aURLExtraData,
-                              nsCSSValue& aResult);
-
-  /**
    * Parse a string representing a CSS property value into a
    * RawServoDeclarationBlock.
    *
    * @param aProperty The property to be parsed.
    * @param aValue The specified value.
    * @param aParsingEnvironment All the parsing environment data we need.
    * @param aParsingMode The paring mode we apply.
    * @return The parsed value as a RawServoDeclarationBlock. We put the value
--- a/layout/style/ServoCSSRuleList.cpp
+++ b/layout/style/ServoCSSRuleList.cpp
@@ -5,28 +5,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* representation of CSSRuleList for stylo */
 
 #include "mozilla/ServoCSSRuleList.h"
 
 #include "mozilla/IntegerRange.h"
 #include "mozilla/ServoBindings.h"
+#include "mozilla/ServoCounterStyleRule.h"
 #include "mozilla/ServoDocumentRule.h"
 #include "mozilla/ServoImportRule.h"
 #include "mozilla/ServoFontFaceRule.h"
 #include "mozilla/ServoFontFeatureValuesRule.h"
 #include "mozilla/ServoKeyframesRule.h"
 #include "mozilla/ServoMediaRule.h"
 #include "mozilla/ServoNamespaceRule.h"
 #include "mozilla/ServoPageRule.h"
 #include "mozilla/ServoStyleRule.h"
 #include "mozilla/ServoStyleSheet.h"
 #include "mozilla/ServoSupportsRule.h"
-#include "nsCSSCounterStyleRule.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 ServoCSSRuleList::ServoCSSRuleList(already_AddRefed<ServoCssRules> aRawRules,
                                    ServoStyleSheet* aDirectOwnerStyleSheet)
   : mStyleSheet(aDirectOwnerStyleSheet)
@@ -48,24 +48,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Se
   tmp->DropAllRules();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(dom::CSSRuleList)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServoCSSRuleList,
                                                   dom::CSSRuleList)
   tmp->EnumerateInstantiatedRules([&](css::Rule* aRule) {
     if (!aRule->IsCCLeaf()) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRules[i]");
       cb.NoteXPCOMChild(aRule);
-      // Note about @counter-style rule again, since there is an indirect owning
-      // edge through Servo's struct that CounterStyleRule in Servo owns a Gecko
-      // nsCSSCounterStyleRule object.
-      auto type = aRule->Type();
-      if (type == CSSRuleBinding::COUNTER_STYLE_RULE) {
-        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRawRules[i]");
-        cb.NoteXPCOMChild(aRule);
-      }
     }
   });
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 void
 ServoCSSRuleList::SetParentRule(css::GroupRule* aParentRule)
 {
   mParentRule = aParentRule;
@@ -106,25 +98,18 @@ ServoCSSRuleList::GetRule(uint32_t aInde
       CASE_RULE(MEDIA, Media)
       CASE_RULE(NAMESPACE, Namespace)
       CASE_RULE(PAGE, Page)
       CASE_RULE(SUPPORTS, Supports)
       CASE_RULE(DOCUMENT, Document)
       CASE_RULE(IMPORT, Import)
       CASE_RULE(FONT_FEATURE_VALUES, FontFeatureValues)
       CASE_RULE(FONT_FACE, FontFace)
+      CASE_RULE(COUNTER_STYLE, CounterStyle)
 #undef CASE_RULE
-      // For @counter-style rules, the function returns a borrowed Gecko
-      // rule object directly, so we don't need to create anything here.
-      // But we still need to have the style sheet and parent rule set
-      // properly.
-      case CSSRuleBinding::COUNTER_STYLE_RULE: {
-        ruleObj = Servo_CssRules_GetCounterStyleRuleAt(mRawRules, aIndex);
-        break;
-      }
       case CSSRuleBinding::KEYFRAME_RULE:
         MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here");
         return nullptr;
       default:
         NS_WARNING("stylo: not implemented yet");
         return nullptr;
     }
     ruleObj->SetStyleSheet(mStyleSheet);
new file mode 100644
--- /dev/null
+++ b/layout/style/ServoCounterStyleRule.cpp
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ServoCounterStyleRule.h"
+
+#include "mozAutoDocUpdate.h"
+#include "mozilla/dom/CSSCounterStyleRuleBinding.h"
+#include "mozilla/ServoBindings.h"
+#include "nsStyleUtil.h"
+
+namespace mozilla {
+
+/* virtual */ already_AddRefed<css::Rule>
+ServoCounterStyleRule::Clone() const
+{
+  MOZ_ASSERT_UNREACHABLE("Shouldn't be cloning ServoCounterStyleRule");
+  return nullptr;
+}
+
+bool
+ServoCounterStyleRule::IsCCLeaf() const
+{
+  return Rule::IsCCLeaf();
+}
+
+#ifdef DEBUG
+void
+ServoCounterStyleRule::List(FILE* out, int32_t aIndent) const
+{
+  nsAutoCString str;
+  for (int32_t i = 0; i < aIndent; i++) {
+    str.AppendLiteral("  ");
+  }
+  Servo_CounterStyleRule_Debug(mRawRule, &str);
+  fprintf_stderr(out, "%s\n", str.get());
+}
+#endif
+
+uint16_t
+ServoCounterStyleRule::Type() const
+{
+  return CSSRuleBinding::COUNTER_STYLE_RULE;
+}
+
+void
+ServoCounterStyleRule::GetCssText(nsAString& aCssText) const
+{
+  Servo_CounterStyleRule_GetCssText(mRawRule, &aCssText);
+}
+
+void
+ServoCounterStyleRule::GetName(nsAString& aName)
+{
+  aName.Truncate();
+  nsAtom* name = Servo_CounterStyleRule_GetName(mRawRule);
+  nsDependentAtomString nameStr(name);
+  nsStyleUtil::AppendEscapedCSSIdent(nameStr, aName);
+}
+
+void
+ServoCounterStyleRule::SetName(const nsAString& aName)
+{
+  nsIDocument* doc = GetDocument();
+  NS_ConvertUTF16toUTF8 name(aName);
+  if (Servo_CounterStyleRule_SetName(mRawRule, &name)) {
+    MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
+    if (StyleSheet* sheet = GetStyleSheet()) {
+      sheet->RuleChanged(this);
+    }
+  }
+}
+
+#define CSS_COUNTER_DESC(name_, method_)                        \
+  void                                                          \
+  ServoCounterStyleRule::Get##method_(nsAString& aValue)        \
+  {                                                             \
+    aValue.Truncate();                                          \
+    Servo_CounterStyleRule_GetDescriptorCssText(                \
+      mRawRule, eCSSCounterDesc_##method_, &aValue);            \
+  }                                                             \
+  void                                                          \
+  ServoCounterStyleRule::Set##method_(const nsAString& aValue)  \
+  {                                                             \
+    NS_ConvertUTF16toUTF8 value(aValue);                        \
+    if (Servo_CounterStyleRule_SetDescriptor(                   \
+          mRawRule, eCSSCounterDesc_##method_, &value)) {       \
+      nsIDocument* doc = GetDocument();                         \
+      MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);             \
+      if (StyleSheet* sheet = GetStyleSheet()) {                \
+        sheet->RuleChanged(this);                               \
+      }                                                         \
+    }                                                           \
+  }
+#include "nsCSSCounterDescList.h"
+#undef CSS_COUNTER_DESC
+
+/* virtual */ size_t
+ServoCounterStyleRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this);
+}
+
+/* virtual */ JSObject*
+ServoCounterStyleRule::WrapObject(JSContext* aCx,
+                                  JS::Handle<JSObject*> aGivenProto)
+{
+  return CSSCounterStyleRuleBinding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/style/ServoCounterStyleRule.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ServoCounterStyleRule_h
+#define mozilla_ServoCounterStyleRule_h
+
+#include "mozilla/css/Rule.h"
+#include "mozilla/ServoBindingTypes.h"
+
+struct RawServoCounterStyleRule;
+
+namespace mozilla {
+
+class ServoCounterStyleRule final : public css::Rule
+{
+public:
+  ServoCounterStyleRule(already_AddRefed<RawServoCounterStyleRule> aRawRule,
+                        uint32_t aLine, uint32_t aColumn)
+    : mozilla::css::Rule(aLine, aColumn)
+    , mRawRule(Move(aRawRule))
+  {
+  }
+
+private:
+  ServoCounterStyleRule(const ServoCounterStyleRule& aCopy) = delete;
+  ~ServoCounterStyleRule() = default;
+
+public:
+  bool IsCCLeaf() const final;
+
+#ifdef DEBUG
+  void List(FILE* out = stdout, int32_t aIndent = 0) const final;
+#endif
+  already_AddRefed<mozilla::css::Rule> Clone() const final;
+
+  // WebIDL interface
+  uint16_t Type() const override;
+  void GetCssText(nsAString& aCssText) const override;
+  void GetName(nsAString& aName);
+  void SetName(const nsAString& aName);
+#define CSS_COUNTER_DESC(name_, method_) \
+  void Get##method_(nsAString& aValue); \
+  void Set##method_(const nsAString& aValue);
+#include "nsCSSCounterDescList.h"
+#undef CSS_COUNTER_DESC
+
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const final;
+
+  JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
+
+private:
+  RefPtr<RawServoCounterStyleRule> mRawRule;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_ServoCounterStyleRule_h
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -1431,17 +1431,17 @@ ServoStyleSet::ResolveStyleLazilyInterna
 bool
 ServoStyleSet::AppendFontFaceRules(nsTArray<nsFontFaceRuleContainer>& aArray)
 {
   UpdateStylistIfNeeded();
   Servo_StyleSet_GetFontFaceRules(mRawSet.get(), &aArray);
   return true;
 }
 
-nsCSSCounterStyleRule*
+const RawServoCounterStyleRule*
 ServoStyleSet::CounterStyleRuleForName(nsAtom* aName)
 {
   MOZ_ASSERT(!StylistNeedsUpdate());
   return Servo_StyleSet_GetCounterStyleRule(mRawSet.get(), aName);
 }
 
 already_AddRefed<gfxFontFeatureValueSet>
 ServoStyleSet::BuildFontFeatureValueSet()
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -37,17 +37,16 @@ class ServoRestyleManager;
 class ServoStyleSheet;
 struct Keyframe;
 class ServoElementSnapshotTable;
 class ComputedStyle;
 class ServoStyleRuleMap;
 class StyleSheet;
 } // namespace mozilla
 class gfxFontFeatureValueSet;
-class nsCSSCounterStyleRule;
 class nsIContent;
 class nsIDocument;
 class nsPresContext;
 struct nsTimingFunction;
 struct RawServoRuleNode;
 struct TreeMatchContext;
 
 namespace mozilla {
@@ -355,17 +354,17 @@ public:
   void
   GetAnimationValues(RawServoDeclarationBlock* aDeclarations,
                      dom::Element* aElement,
                      const mozilla::ComputedStyle* aStyle,
                      nsTArray<RefPtr<RawServoAnimationValue>>& aAnimationValues);
 
   bool AppendFontFaceRules(nsTArray<nsFontFaceRuleContainer>& aArray);
 
-  nsCSSCounterStyleRule* CounterStyleRuleForName(nsAtom* aName);
+  const RawServoCounterStyleRule* CounterStyleRuleForName(nsAtom* aName);
 
   // Get all the currently-active font feature values set.
   already_AddRefed<gfxFontFeatureValueSet> BuildFontFeatureValueSet();
 
   already_AddRefed<ComputedStyle>
   GetBaseContextForElement(dom::Element* aElement,
                            const ComputedStyle* aStyle);
 
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -25,17 +25,16 @@ TEST_DIRS += ['test']
 EXPORTS += [
     'AnimationCommon.h',
     'CounterStyleManager.h',
     'nsAnimationManager.h',
     'nsComputedDOMStylePropertyList.h',
     'nsCSSAnonBoxes.h',
     'nsCSSAnonBoxList.h',
     'nsCSSCounterDescList.h',
-    'nsCSSCounterStyleRule.h',
     'nsCSSFontDescList.h',
     'nsCSSKeywordList.h',
     'nsCSSKeywords.h',
     'nsCSSParser.h',
     'nsCSSPropAliasList.h',
     'nsCSSPropertyID.h',
     'nsCSSPropertyIDSet.h',
     'nsCSSPropList.h',
@@ -78,16 +77,17 @@ EXPORTS.mozilla += [
     'LayerAnimationInfo.h',
     'MediaFeatureChange.h',
     'PostTraversalTask.h',
     'PreloadedStyleSheet.h',
     'ServoArcTypeList.h',
     'ServoBindingList.h',
     'ServoBindings.h',
     'ServoBindingTypes.h',
+    'ServoCounterStyleRule.h',
     'ServoCSSParser.h',
     'ServoCSSRuleList.h',
     'ServoDeclarationBlock.h',
     'ServoDocumentRule.h',
     'ServoElementSnapshot.h',
     'ServoElementSnapshotTable.h',
     'ServoFontFaceRule.h',
     'ServoFontFeatureValuesRule.h',
@@ -175,17 +175,16 @@ UNIFIED_SOURCES += [
     'GroupRule.cpp',
     'ImageLoader.cpp',
     'LayerAnimationInfo.cpp',
     'Loader.cpp',
     'MediaList.cpp',
     'MediaQueryList.cpp',
     'nsAnimationManager.cpp',
     'nsComputedDOMStyle.cpp',
-    'nsCSSCounterStyleRule.cpp',
     'nsCSSKeywords.cpp',
     'nsCSSProps.cpp',
     'nsCSSScanner.cpp',
     'nsCSSValue.cpp',
     'nsDOMCSSAttrDeclaration.cpp',
     'nsDOMCSSDeclaration.cpp',
     'nsDOMCSSRect.cpp',
     'nsDOMCSSRGBColor.cpp',
@@ -201,16 +200,17 @@ UNIFIED_SOURCES += [
     'nsStyleStruct.cpp',
     'nsStyleTransformMatrix.cpp',
     'nsStyleUtil.cpp',
     'nsTransitionManager.cpp',
     'PostTraversalTask.cpp',
     'PreloadedStyleSheet.cpp',
     'Rule.cpp',
     'ServoBindings.cpp',
+    'ServoCounterStyleRule.cpp',
     'ServoCSSParser.cpp',
     'ServoCSSRuleList.cpp',
     'ServoDeclarationBlock.cpp',
     'ServoDocumentRule.cpp',
     'ServoElementSnapshot.cpp',
     'ServoFontFaceRule.cpp',
     'ServoFontFeatureValuesRule.cpp',
     'ServoImportRule.cpp',
deleted file mode 100644
--- a/layout/style/nsCSSCounterStyleRule.cpp
+++ /dev/null
@@ -1,427 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/* a Gecko @counter-style rule */
-
-#include "nsCSSCounterStyleRule.h"
-
-#include "mozAutoDocUpdate.h"
-#include "mozilla/ArrayUtils.h"
-#include "mozilla/dom/CSSCounterStyleRuleBinding.h"
-#include "mozilla/ServoCSSParser.h"
-#include "nsCSSParser.h"
-#include "nsStyleUtil.h"
-
-using namespace mozilla;
-using namespace mozilla::dom;
-
-nsCSSCounterStyleRule::nsCSSCounterStyleRule(const nsCSSCounterStyleRule& aCopy)
-  : Rule(aCopy)
-  , mName(aCopy.mName)
-  , mGeneration(aCopy.mGeneration)
-{
-  for (size_t i = 0; i < ArrayLength(mValues); ++i) {
-    mValues[i] = aCopy.mValues[i];
-  }
-}
-
-nsCSSCounterStyleRule::~nsCSSCounterStyleRule()
-{
-}
-
-/* virtual */ already_AddRefed<css::Rule>
-nsCSSCounterStyleRule::Clone() const
-{
-  RefPtr<css::Rule> clone = new nsCSSCounterStyleRule(*this);
-  return clone.forget();
-}
-
-nsCSSCounterStyleRule::Getter const
-nsCSSCounterStyleRule::kGetters[] = {
-#define CSS_COUNTER_DESC(name_, method_) &nsCSSCounterStyleRule::Get##method_,
-#include "nsCSSCounterDescList.h"
-#undef CSS_COUNTER_DESC
-};
-
-// If this class gets its own cycle-collection bits, reevaluate our IsCCLeaf
-// implementation.
-
-bool
-nsCSSCounterStyleRule::IsCCLeaf() const
-{
-  return Rule::IsCCLeaf();
-}
-
-#ifdef DEBUG
-void
-nsCSSCounterStyleRule::List(FILE* out, int32_t aIndent) const
-{
-  nsCString baseInd, descInd;
-  for (int32_t indent = aIndent; --indent >= 0; ) {
-    baseInd.AppendLiteral("  ");
-  }
-  descInd = baseInd;
-  descInd.AppendLiteral("  ");
-
-  nsDependentAtomString name(mName);
-  fprintf_stderr(out, "%s@counter-style %s (rev.%u) {\n",
-                 baseInd.get(), NS_ConvertUTF16toUTF8(name).get(),
-                 mGeneration);
-  // TODO
-  fprintf_stderr(out, "%s}\n", baseInd.get());
-}
-#endif
-
-uint16_t
-nsCSSCounterStyleRule::Type() const
-{
-  return CSSRuleBinding::COUNTER_STYLE_RULE;
-}
-
-void
-nsCSSCounterStyleRule::GetCssText(nsAString& aCssText) const
-{
-  aCssText.AssignLiteral(u"@counter-style ");
-  nsDependentAtomString name(mName);
-  nsStyleUtil::AppendEscapedCSSIdent(name, aCssText);
-  aCssText.AppendLiteral(u" {\n");
-  for (nsCSSCounterDesc id = nsCSSCounterDesc(0);
-       id < eCSSCounterDesc_COUNT;
-       id = nsCSSCounterDesc(id + 1)) {
-    if (mValues[id].GetUnit() != eCSSUnit_Null) {
-      nsAutoString tmp;
-      // This is annoying.  We want to be a const method, but kGetters stores
-      // XPCOM method pointers, which aren't const methods.  The thing is,
-      // none of those mutate "this".  So it's OK to cast away const here.
-      (const_cast<nsCSSCounterStyleRule*>(this)->*kGetters[id])(tmp);
-      aCssText.AppendLiteral(u"  ");
-      AppendASCIItoUTF16(nsCSSProps::GetStringValue(id), aCssText);
-      aCssText.AppendLiteral(u": ");
-      aCssText.Append(tmp);
-      aCssText.AppendLiteral(u";\n");
-    }
-  }
-  aCssText.AppendLiteral(u"}");
-}
-
-void
-nsCSSCounterStyleRule::GetName(nsAString& aName)
-{
-  aName.Truncate();
-  nsDependentAtomString name(mName);
-  nsStyleUtil::AppendEscapedCSSIdent(name, aName);
-}
-
-void
-nsCSSCounterStyleRule::SetName(const nsAString& aName)
-{
-
-  nsIDocument* doc = GetDocument();
-  if (RefPtr<nsAtom> name = ServoCSSParser::ParseCounterStyleName(aName)) {
-    MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
-
-    mName = name;
-
-    if (StyleSheet* sheet = GetStyleSheet()) {
-      sheet->RuleChanged(this);
-    }
-  }
-}
-
-int32_t
-nsCSSCounterStyleRule::GetSystem() const
-{
-  const nsCSSValue& system = GetDesc(eCSSCounterDesc_System);
-  switch (system.GetUnit()) {
-    case eCSSUnit_Enumerated:
-      return system.GetIntValue();
-    case eCSSUnit_Pair:
-      return system.GetPairValue().mXValue.GetIntValue();
-    default:
-      return NS_STYLE_COUNTER_SYSTEM_SYMBOLIC;
-  }
-}
-
-const nsCSSValue&
-nsCSSCounterStyleRule::GetSystemArgument() const
-{
-  const nsCSSValue& system = GetDesc(eCSSCounterDesc_System);
-  MOZ_ASSERT(system.GetUnit() == eCSSUnit_Pair,
-             "Invalid system value");
-  return system.GetPairValue().mYValue;
-}
-
-void
-nsCSSCounterStyleRule::SetDesc(nsCSSCounterDesc aDescID, const nsCSSValue& aValue)
-{
-  MOZ_ASSERT(aDescID >= 0 && aDescID < eCSSCounterDesc_COUNT,
-             "descriptor ID out of range");
-
-  nsIDocument* doc = GetDocument();
-  MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
-
-  mValues[aDescID] = aValue;
-  mGeneration++;
-
-  if (StyleSheet* sheet = GetStyleSheet()) {
-    sheet->RuleChanged(this);
-  }
-}
-
-void
-nsCSSCounterStyleRule::GetSystem(nsAString& aSystem)
-{
-  const nsCSSValue& value = GetDesc(eCSSCounterDesc_System);
-  if (value.GetUnit() == eCSSUnit_Null) {
-    aSystem.Truncate();
-    return;
-  }
-
-  aSystem = NS_ConvertASCIItoUTF16(nsCSSProps::ValueToKeyword(
-          GetSystem(), nsCSSProps::kCounterSystemKTable));
-  if (value.GetUnit() == eCSSUnit_Pair) {
-    aSystem.Append(' ');
-    GetSystemArgument().AppendToString(eCSSProperty_UNKNOWN, aSystem);
-  }
-}
-
-void
-nsCSSCounterStyleRule::GetSymbols(nsAString& aSymbols)
-{
-  const nsCSSValue& value = GetDesc(eCSSCounterDesc_Symbols);
-
-  aSymbols.Truncate();
-  if (value.GetUnit() == eCSSUnit_List) {
-    for (const nsCSSValueList* item = value.GetListValue();
-         item; item = item->mNext) {
-      item->mValue.AppendToString(eCSSProperty_UNKNOWN, aSymbols);
-      if (item->mNext) {
-        aSymbols.Append(' ');
-      }
-    }
-  }
-}
-
-void
-nsCSSCounterStyleRule::GetAdditiveSymbols(nsAString& aSymbols)
-{
-  const nsCSSValue& value = GetDesc(eCSSCounterDesc_AdditiveSymbols);
-
-  aSymbols.Truncate();
-  if (value.GetUnit() == eCSSUnit_PairList) {
-    for (const nsCSSValuePairList* item = value.GetPairListValue();
-         item; item = item->mNext) {
-      item->mXValue.AppendToString(eCSSProperty_UNKNOWN, aSymbols);
-      aSymbols.Append(' ');
-      item->mYValue.AppendToString(eCSSProperty_UNKNOWN, aSymbols);
-      if (item->mNext) {
-        aSymbols.AppendLiteral(", ");
-      }
-    }
-  }
-}
-
-void
-nsCSSCounterStyleRule::GetRange(nsAString& aRange)
-{
-  const nsCSSValue& value = GetDesc(eCSSCounterDesc_Range);
-
-  switch (value.GetUnit()) {
-    case eCSSUnit_Auto:
-      aRange.AssignLiteral(u"auto");
-      break;
-
-    case eCSSUnit_PairList:
-      aRange.Truncate();
-      for (const nsCSSValuePairList* item = value.GetPairListValue();
-          item; item = item->mNext) {
-        const nsCSSValue& lower = item->mXValue;
-        const nsCSSValue& upper = item->mYValue;
-        if (lower.GetUnit() == eCSSUnit_Enumerated) {
-          NS_ASSERTION(lower.GetIntValue() ==
-                       NS_STYLE_COUNTER_RANGE_INFINITE,
-                       "Unrecognized keyword");
-          aRange.AppendLiteral("infinite");
-        } else {
-          aRange.AppendInt(lower.GetIntValue());
-        }
-        aRange.Append(' ');
-        if (upper.GetUnit() == eCSSUnit_Enumerated) {
-          NS_ASSERTION(upper.GetIntValue() ==
-                       NS_STYLE_COUNTER_RANGE_INFINITE,
-                       "Unrecognized keyword");
-          aRange.AppendLiteral("infinite");
-        } else {
-          aRange.AppendInt(upper.GetIntValue());
-        }
-        if (item->mNext) {
-          aRange.AppendLiteral(", ");
-        }
-      }
-      break;
-
-    default:
-      aRange.Truncate();
-  }
-}
-
-void
-nsCSSCounterStyleRule::GetSpeakAs(nsAString& aSpeakAs)
-{
-  const nsCSSValue& value = GetDesc(eCSSCounterDesc_SpeakAs);
-
-  switch (value.GetUnit()) {
-    case eCSSUnit_Enumerated:
-      switch (value.GetIntValue()) {
-        case NS_STYLE_COUNTER_SPEAKAS_BULLETS:
-          aSpeakAs.AssignLiteral(u"bullets");
-          break;
-        case NS_STYLE_COUNTER_SPEAKAS_NUMBERS:
-          aSpeakAs.AssignLiteral(u"numbers");
-          break;
-        case NS_STYLE_COUNTER_SPEAKAS_WORDS:
-          aSpeakAs.AssignLiteral(u"words");
-          break;
-        case NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT:
-          aSpeakAs.AssignLiteral(u"spell-out");
-          break;
-        default:
-          NS_NOTREACHED("Unknown speech synthesis");
-      }
-      break;
-
-    case eCSSUnit_Auto:
-    case eCSSUnit_AtomIdent:
-      aSpeakAs.Truncate();
-      value.AppendToString(eCSSProperty_UNKNOWN, aSpeakAs);
-      break;
-
-    case eCSSUnit_Null:
-      aSpeakAs.Truncate();
-      break;
-
-    default:
-      NS_NOTREACHED("Unknown speech synthesis");
-      aSpeakAs.Truncate();
-  }
-}
-
-void
-nsCSSCounterStyleRule::GetDescriptor(nsCSSCounterDesc aDescID,
-                                     nsAString& aValue)
-{
-  NS_ASSERTION(aDescID == eCSSCounterDesc_Negative ||
-               aDescID == eCSSCounterDesc_Prefix ||
-               aDescID == eCSSCounterDesc_Suffix ||
-               aDescID == eCSSCounterDesc_Pad ||
-               aDescID == eCSSCounterDesc_Fallback,
-               "Unexpected descriptor");
-  const nsCSSValue& value = GetDesc(aDescID);
-  aValue.Truncate();
-  if (value.GetUnit() != eCSSUnit_Null) {
-    value.AppendToString(eCSSProperty_UNKNOWN, aValue);
-  }
-}
-
-#define CSS_COUNTER_DESC_GETTER(name_)                    \
-void                                                      \
-nsCSSCounterStyleRule::Get##name_(nsAString& a##name_)    \
-{                                                         \
-  GetDescriptor(eCSSCounterDesc_##name_, a##name_);       \
-}
-CSS_COUNTER_DESC_GETTER(Negative)
-CSS_COUNTER_DESC_GETTER(Prefix)
-CSS_COUNTER_DESC_GETTER(Suffix)
-CSS_COUNTER_DESC_GETTER(Pad)
-CSS_COUNTER_DESC_GETTER(Fallback)
-#undef CSS_COUNTER_DESC_GETTER
-
-/* static */ bool
-nsCSSCounterStyleRule::CheckDescValue(int32_t aSystem,
-                                      nsCSSCounterDesc aDescID,
-                                      const nsCSSValue& aValue)
-{
-  switch (aDescID) {
-    case eCSSCounterDesc_System:
-      if (aValue.GetUnit() != eCSSUnit_Pair) {
-        return aValue.GetIntValue() == aSystem;
-      } else {
-        return aValue.GetPairValue().mXValue.GetIntValue() == aSystem;
-      }
-
-    case eCSSCounterDesc_Symbols:
-      switch (aSystem) {
-        case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
-        case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
-          // for these two system, the list must contain at least 2 elements
-          return aValue.GetListValue()->mNext;
-        case NS_STYLE_COUNTER_SYSTEM_EXTENDS:
-          // for extends system, no symbols should be set
-          return false;
-        default:
-          return true;
-      }
-
-    case eCSSCounterDesc_AdditiveSymbols:
-      switch (aSystem) {
-        case NS_STYLE_COUNTER_SYSTEM_EXTENDS:
-          return false;
-        default:
-          return true;
-      }
-
-    default:
-      return true;
-  }
-}
-
-void
-nsCSSCounterStyleRule::SetDescriptor(nsCSSCounterDesc aDescID,
-                                     const nsAString& aValue)
-{
-  nsCSSValue value;
-
-  StyleSheet* sheet = GetStyleSheet();
-
-  URLExtraData* data = sheet ? sheet->AsServo()->URLData() : nullptr;
-  bool ok = ServoCSSParser::ParseCounterStyleDescriptor(aDescID, aValue, data,
-                                                        value);
-
-  if (ok && CheckDescValue(GetSystem(), aDescID, value)) {
-    SetDesc(aDescID, value);
-  }
-}
-
-#define CSS_COUNTER_DESC_SETTER(name_)                        \
-void                                                          \
-nsCSSCounterStyleRule::Set##name_(const nsAString& a##name_)  \
-{                                                             \
-  SetDescriptor(eCSSCounterDesc_##name_, a##name_);           \
-}
-CSS_COUNTER_DESC_SETTER(System)
-CSS_COUNTER_DESC_SETTER(Symbols)
-CSS_COUNTER_DESC_SETTER(AdditiveSymbols)
-CSS_COUNTER_DESC_SETTER(Negative)
-CSS_COUNTER_DESC_SETTER(Prefix)
-CSS_COUNTER_DESC_SETTER(Suffix)
-CSS_COUNTER_DESC_SETTER(Range)
-CSS_COUNTER_DESC_SETTER(Pad)
-CSS_COUNTER_DESC_SETTER(Fallback)
-CSS_COUNTER_DESC_SETTER(SpeakAs)
-#undef CSS_COUNTER_DESC_SETTER
-
-/* virtual */ size_t
-nsCSSCounterStyleRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
-{
-  return aMallocSizeOf(this);
-}
-
-/* virtual */ JSObject*
-nsCSSCounterStyleRule::WrapObject(JSContext* aCx,
-                                  JS::Handle<JSObject*> aGivenProto)
-{
-  return CSSCounterStyleRuleBinding::Wrap(aCx, this, aGivenProto);
-}
deleted file mode 100644
--- a/layout/style/nsCSSCounterStyleRule.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsCSSCounterStyleRule_h
-#define nsCSSCounterStyleRule_h
-
-#include "mozilla/css/Rule.h"
-#include "nsCSSValue.h"
-
-class nsCSSCounterStyleRule final : public mozilla::css::Rule
-{
-public:
-  explicit nsCSSCounterStyleRule(nsAtom* aName,
-                                 uint32_t aLineNumber, uint32_t aColumnNumber)
-    : mozilla::css::Rule(aLineNumber, aColumnNumber)
-    , mName(aName)
-    , mGeneration(0)
-  {
-    MOZ_ASSERT(aName, "Must have non-null name");
-  }
-
-private:
-  nsCSSCounterStyleRule(const nsCSSCounterStyleRule& aCopy);
-  ~nsCSSCounterStyleRule();
-
-public:
-  virtual bool IsCCLeaf() const override;
-
-#ifdef DEBUG
-  virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
-#endif
-  virtual already_AddRefed<mozilla::css::Rule> Clone() const override;
-
-  // WebIDL interface
-  uint16_t Type() const override;
-  void GetCssText(nsAString& aCssText) const override;
-  void GetName(nsAString& aName);
-  void SetName(const nsAString& aName);
-  void GetSystem(nsAString& aSystem);
-  void SetSystem(const nsAString& aSystem);
-  void GetSymbols(nsAString& aSymbols);
-  void SetSymbols(const nsAString& aSymbols);
-  void GetAdditiveSymbols(nsAString& aAdditiveSymbols);
-  void SetAdditiveSymbols(const nsAString& aAdditiveSymbols);
-  void GetNegative(nsAString& aNegative);
-  void SetNegative(const nsAString& aNegative);
-  void GetPrefix(nsAString& aPrefix);
-  void SetPrefix(const nsAString& aPrefix);
-  void GetSuffix(nsAString& aSuffix);
-  void SetSuffix(const nsAString& aSuffix);
-  void GetRange(nsAString& aRange);
-  void SetRange(const nsAString& aRange);
-  void GetPad(nsAString& aPad);
-  void SetPad(const nsAString& aPad);
-  void GetSpeakAs(nsAString& aSpeakAs);
-  void SetSpeakAs(const nsAString& aSpeakAs);
-  void GetFallback(nsAString& aFallback);
-  void SetFallback(const nsAString& aFallback);
-
-  // This function is only used to check whether a non-empty value, which has
-  // been accepted by parser, is valid for the given system and descriptor.
-  static bool CheckDescValue(int32_t aSystem,
-                             nsCSSCounterDesc aDescID,
-                             const nsCSSValue& aValue);
-
-  nsAtom* Name() const { return mName; }
-
-  uint32_t GetGeneration() const { return mGeneration; }
-
-  int32_t GetSystem() const;
-  const nsCSSValue& GetSystemArgument() const;
-
-  const nsCSSValue& GetDesc(nsCSSCounterDesc aDescID) const
-  {
-    MOZ_ASSERT(aDescID >= 0 && aDescID < eCSSCounterDesc_COUNT,
-               "descriptor ID out of range");
-    return mValues[aDescID];
-  }
-
-  void SetDesc(nsCSSCounterDesc aDescID, const nsCSSValue& aValue);
-
-  virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
-
-  virtual JSObject* WrapObject(JSContext* aCx,
-                               JS::Handle<JSObject*> aGivenProto) override;
-
-private:
-  typedef decltype(&nsCSSCounterStyleRule::GetSymbols) Getter;
-  static const Getter kGetters[];
-
-  void GetDescriptor(nsCSSCounterDesc aDescID, nsAString& aValue);
-  void SetDescriptor(nsCSSCounterDesc aDescID, const nsAString& aValue);
-
-  RefPtr<nsAtom> mName;
-  nsCSSValue mValues[eCSSCounterDesc_COUNT];
-  uint32_t   mGeneration;
-};
-
-#endif // nsCSSCounterStyleRule_h
--- a/layout/style/test/test_counter_descriptor_storage.html
+++ b/layout/style/test/test_counter_descriptor_storage.html
@@ -74,24 +74,25 @@ function test_system_dep_desc() {
         ],
         symbols: oneSymbolTests
       }
     },
     {
       system: "fixed",
       base: "symbols: x",
       base_tests: {
-        system: "fixed 1",
+        system: "fixed",
         symbols: "x"
       },
       tests: {
         system: [
           [null, "", "symbolic"],
           ["fixed 0"],
-          ["fixed 1", "fixed", "FixeD"],
+          ["fixed", "FixeD"],
+          ["fixed 1", "FixeD 1"],
           ["fixed -1"],
           [null, "fixed a", "fixed \"0\"", "fixed 0 1"],
         ],
         symbols: oneSymbolTests
       }
     },
     {
       system: "symbolic",
--- a/servo/components/style/counter_style/mod.rs
+++ b/servo/components/style/counter_style/mod.rs
@@ -5,22 +5,22 @@
 //! The [`@counter-style`][counter-style] at-rule.
 //!
 //! [counter-style]: https://drafts.csswg.org/css-counter-styles/
 
 use Atom;
 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser};
 use cssparser::{Parser, Token, CowRcStr, SourceLocation};
 use error_reporting::{ContextualParseError, ParseErrorReporter};
-#[cfg(feature = "gecko")] use gecko::rules::CounterStyleDescriptors;
-#[cfg(feature = "gecko")] use gecko_bindings::structs::{ nsCSSCounterDesc, nsCSSValue };
 use parser::{ParserContext, ParserErrorContext, Parse};
 use selectors::parser::SelectorParseErrorKind;
 use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt::{self, Write};
+use std::mem;
+use std::num::Wrapping;
 use std::ops::Range;
 use str::CssStringWriter;
 use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
 use style_traits::{StyleParseErrorKind, ToCss};
 use values::CustomIdent;
 use values::specified::Integer;
 
 /// Parse a counter style name reference.
@@ -50,23 +50,27 @@ pub fn parse_counter_style_name<'i, 't>(
                     CustomIdent::from_ident(location, ident, &["none"])
                 }
             }
         }
     }
     include!("predefined.rs")
 }
 
+fn is_valid_name_definition(ident: &CustomIdent) -> bool {
+    ident.0 != atom!("decimal") && ident.0 != atom!("disc")
+}
+
 /// Parse the prelude of an @counter-style rule
 pub fn parse_counter_style_name_definition<'i, 't>(
     input: &mut Parser<'i, 't>
 ) -> Result<CustomIdent, ParseError<'i>> {
     parse_counter_style_name(input)
         .and_then(|ident| {
-            if ident.0 == atom!("decimal") || ident.0 == atom!("disc") {
+            if !is_valid_name_definition(&ident) {
                 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             } else {
                 Ok(ident)
             }
         })
 }
 
 /// Parse the body (inside `{}`) of an @counter-style rule
@@ -138,73 +142,70 @@ struct CounterStyleRuleParser<'a, 'b: 'a
 /// Default methods reject all at rules.
 impl<'a, 'b, 'i> AtRuleParser<'i> for CounterStyleRuleParser<'a, 'b> {
     type PreludeNoBlock = ();
     type PreludeBlock = ();
     type AtRule = ();
     type Error = StyleParseErrorKind<'i>;
 }
 
+macro_rules! checker {
+    ($self:ident._($value:ident)) => {};
+    ($self:ident.$checker:ident($value:ident)) => {
+        if !$self.$checker(&$value) {
+            return false;
+        }
+    };
+}
+
 macro_rules! counter_style_descriptors {
     (
-        $( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty, )+
+        $( #[$doc: meta] $name: tt $ident: ident / $setter: ident [$checker: tt]: $ty: ty, )+
     ) => {
         /// An @counter-style rule
         #[derive(Clone, Debug)]
         pub struct CounterStyleRuleData {
             name: CustomIdent,
+            generation: Wrapping<u32>,
             $(
                 #[$doc]
                 $ident: Option<$ty>,
             )+
             /// Line and column of the @counter-style rule source code.
             pub source_location: SourceLocation,
         }
 
         impl CounterStyleRuleData {
             fn empty(name: CustomIdent, source_location: SourceLocation) -> Self {
                 CounterStyleRuleData {
                     name: name,
+                    generation: Wrapping(0),
                     $(
                         $ident: None,
                     )+
                     source_location,
                 }
             }
 
-            /// Get the name of the counter style rule.
-            pub fn name(&self) -> &CustomIdent {
-                &self.name
-            }
-
-            /// Get the system of this counter style rule, default to
-            /// `symbolic` if not specified.
-            pub fn get_system(&self) -> &System {
-                match self.system {
-                    Some(ref system) => system,
-                    None => &System::Symbolic,
-                }
-            }
-
             $(
                 #[$doc]
                 pub fn $ident(&self) -> Option<&$ty> {
                     self.$ident.as_ref()
                 }
             )+
 
-            /// Convert to Gecko types
-            #[cfg(feature = "gecko")]
-            pub fn set_descriptors(self, descriptors: &mut CounterStyleDescriptors) {
-                $(
-                    if let Some(value) = self.$ident {
-                        descriptors[nsCSSCounterDesc::$gecko_ident as usize].set_from(value)
-                    }
-                )*
-            }
+            $(
+                #[$doc]
+                pub fn $setter(&mut self, value: $ty) -> bool {
+                    checker!(self.$checker(value));
+                    self.$ident = Some(value);
+                    self.generation += Wrapping(1);
+                    true
+                }
+            )+
         }
 
         impl<'a, 'b, 'i> DeclarationParser<'i> for CounterStyleRuleParser<'a, 'b> {
             type Declaration = ();
             type Error = StyleParseErrorKind<'i>;
 
             fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                                -> Result<(), ParseError<'i>> {
@@ -235,73 +236,106 @@ macro_rules! counter_style_descriptors {
                         dest.write_str(concat!("  ", $name, ": "))?;
                         ToCss::to_css(value, &mut CssWriter::new(dest))?;
                         dest.write_str(";\n")?;
                     }
                 )+
                 dest.write_str("}")
             }
         }
-
-        /// Parse a descriptor into an `nsCSSValue`.
-        #[cfg(feature = "gecko")]
-        pub fn parse_counter_style_descriptor<'i, 't>(
-            context: &ParserContext,
-            input: &mut Parser<'i, 't>,
-            descriptor: nsCSSCounterDesc,
-            value: &mut nsCSSValue
-        ) -> Result<(), ParseError<'i>> {
-            match descriptor {
-                $(
-                    nsCSSCounterDesc::$gecko_ident => {
-                        let v: $ty =
-                            input.parse_entirely(|i| Parse::parse(context, i))?;
-                        value.set_from(v);
-                    }
-                )*
-                nsCSSCounterDesc::eCSSCounterDesc_COUNT |
-                nsCSSCounterDesc::eCSSCounterDesc_UNKNOWN => {
-                    panic!("invalid counter descriptor");
-                }
-            }
-            Ok(())
-        }
     }
 }
 
 counter_style_descriptors! {
     /// <https://drafts.csswg.org/css-counter-styles/#counter-style-system>
-    "system" system / eCSSCounterDesc_System: System,
+    "system" system / set_system [check_system]: System,
 
     /// <https://drafts.csswg.org/css-counter-styles/#counter-style-negative>
-    "negative" negative / eCSSCounterDesc_Negative: Negative,
+    "negative" negative / set_negative [_]: Negative,
 
     /// <https://drafts.csswg.org/css-counter-styles/#counter-style-prefix>
-    "prefix" prefix / eCSSCounterDesc_Prefix: Symbol,
+    "prefix" prefix / set_prefix [_]: Symbol,
 
     /// <https://drafts.csswg.org/css-counter-styles/#counter-style-suffix>
-    "suffix" suffix / eCSSCounterDesc_Suffix: Symbol,
+    "suffix" suffix / set_suffix [_]: Symbol,
 
     /// <https://drafts.csswg.org/css-counter-styles/#counter-style-range>
-    "range" range / eCSSCounterDesc_Range: Ranges,
+    "range" range / set_range [_]: Ranges,
 
     /// <https://drafts.csswg.org/css-counter-styles/#counter-style-pad>
-    "pad" pad / eCSSCounterDesc_Pad: Pad,
+    "pad" pad / set_pad [_]: Pad,
 
     /// <https://drafts.csswg.org/css-counter-styles/#counter-style-fallback>
-    "fallback" fallback / eCSSCounterDesc_Fallback: Fallback,
+    "fallback" fallback / set_fallback [_]: Fallback,
 
     /// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols>
-    "symbols" symbols / eCSSCounterDesc_Symbols: Symbols,
+    "symbols" symbols / set_symbols [check_symbols]: Symbols,
 
     /// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-additive-symbols>
-    "additive-symbols" additive_symbols / eCSSCounterDesc_AdditiveSymbols: AdditiveSymbols,
+    "additive-symbols" additive_symbols /
+        set_additive_symbols [check_additive_symbols]: AdditiveSymbols,
 
     /// <https://drafts.csswg.org/css-counter-styles/#counter-style-speak-as>
-    "speak-as" speak_as / eCSSCounterDesc_SpeakAs: SpeakAs,
+    "speak-as" speak_as / set_speak_as [_]: SpeakAs,
+}
+
+// Implements the special checkers for some setters.
+// See <https://drafts.csswg.org/css-counter-styles/#the-csscounterstylerule-interface>
+impl CounterStyleRuleData {
+    /// Check that the system is effectively not changed. Only params
+    /// of system descriptor is changeable.
+    fn check_system(&self, value: &System) -> bool {
+        mem::discriminant(self.get_system()) == mem::discriminant(value)
+    }
+
+    fn check_symbols(&self, value: &Symbols) -> bool {
+        match *self.get_system() {
+            // These two systems require at least two symbols.
+            System::Numeric | System::Alphabetic => value.0.len() >= 2,
+            // No symbols should be set for extends system.
+            System::Extends(_) => false,
+            _ => true,
+        }
+    }
+
+    fn check_additive_symbols(&self, _value: &AdditiveSymbols) -> bool {
+        match *self.get_system() {
+            // No additive symbols should be set for extends system.
+            System::Extends(_) => false,
+            _ => true,
+        }
+    }
+}
+
+impl CounterStyleRuleData {
+    /// Get the name of the counter style rule.
+    pub fn name(&self) -> &CustomIdent {
+        &self.name
+    }
+
+    /// Set the name of the counter style rule. Caller must ensure that
+    /// the name is valid.
+    pub fn set_name(&mut self, name: CustomIdent) {
+        debug_assert!(is_valid_name_definition(&name));
+        self.name = name;
+    }
+
+    /// Get the current generation of the counter style rule.
+    pub fn generation(&self) -> u32 {
+        self.generation.0
+    }
+
+    /// Get the system of this counter style rule, default to
+    /// `symbolic` if not specified.
+    pub fn get_system(&self) -> &System {
+        match self.system {
+            Some(ref system) => system,
+            None => &System::Symbolic,
+        }
+    }
 }
 
 /// <https://drafts.csswg.org/css-counter-styles/#counter-style-system>
 #[derive(Clone, Debug)]
 pub enum System {
     /// 'cyclic'
     Cyclic,
     /// 'numeric'
--- a/servo/components/style/gecko/arc_types.rs
+++ b/servo/components/style/gecko/arc_types.rs
@@ -3,33 +3,34 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! This file lists all arc FFI types and defines corresponding addref
 //! and release functions. This list corresponds to ServoArcTypeList.h
 //! file in Gecko.
 
 #![allow(non_snake_case, missing_docs)]
 
-use gecko_bindings::bindings::{RawServoFontFeatureValuesRule, RawServoImportRule};
+use gecko_bindings::bindings::{RawServoCounterStyleRule, RawServoFontFeatureValuesRule, RawServoImportRule};
 use gecko_bindings::bindings::{RawServoKeyframe, RawServoKeyframesRule, RawServoSupportsRule};
 use gecko_bindings::bindings::{RawServoMediaRule, RawServoNamespaceRule, RawServoPageRule};
 use gecko_bindings::bindings::{RawServoRuleNode, RawServoRuleNodeStrong, RawServoDocumentRule};
 use gecko_bindings::bindings::ServoCssRules;
 use gecko_bindings::structs::{RawServoAnimationValue, RawServoDeclarationBlock, RawServoFontFaceRule};
 use gecko_bindings::structs::{RawServoMediaList, RawServoStyleRule, RawServoStyleSheetContents};
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI, Strong};
 use media_queries::MediaList;
 use properties::{ComputedValues, PropertyDeclarationBlock};
 use properties::animated_properties::AnimationValue;
 use rule_tree::StrongRuleNode;
 use servo_arc::{Arc, ArcBorrow};
 use shared_lock::Locked;
 use std::{mem, ptr};
 use stylesheets::{CssRules, StylesheetContents, StyleRule, ImportRule, KeyframesRule, MediaRule};
-use stylesheets::{FontFaceRule, FontFeatureValuesRule, NamespaceRule, PageRule, SupportsRule, DocumentRule};
+use stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule};
+use stylesheets::{NamespaceRule, PageRule, SupportsRule, DocumentRule};
 use stylesheets::keyframes_rule::Keyframe;
 
 macro_rules! impl_arc_ffi {
     ($servo_type:ty => $gecko_type:ty [$addref:ident, $release:ident]) => {
         unsafe impl HasFFI for $servo_type {
             type FFIType = $gecko_type;
         }
         unsafe impl HasArcFFI for $servo_type {}
@@ -89,16 +90,19 @@ impl_arc_ffi!(Locked<DocumentRule> => Ra
               [Servo_DocumentRule_AddRef, Servo_DocumentRule_Release]);
 
 impl_arc_ffi!(Locked<FontFeatureValuesRule> => RawServoFontFeatureValuesRule
               [Servo_FontFeatureValuesRule_AddRef, Servo_FontFeatureValuesRule_Release]);
 
 impl_arc_ffi!(Locked<FontFaceRule> => RawServoFontFaceRule
               [Servo_FontFaceRule_AddRef, Servo_FontFaceRule_Release]);
 
+impl_arc_ffi!(Locked<CounterStyleRule> => RawServoCounterStyleRule
+              [Servo_CounterStyleRule_AddRef, Servo_CounterStyleRule_Release]);
+
 // RuleNode is a Arc-like type but it does not use Arc.
 
 impl StrongRuleNode {
     pub fn into_strong(self) -> RawServoRuleNodeStrong {
         let ptr = self.ptr();
         mem::forget(self);
         unsafe { mem::transmute(ptr) }
     }
--- a/servo/components/style/gecko/rules.rs
+++ b/servo/components/style/gecko/rules.rs
@@ -4,27 +4,20 @@
 
 //! Bindings for CSS Rule objects
 
 use byteorder::{BigEndian, WriteBytesExt};
 use computed_values::{font_stretch, font_style, font_weight};
 use counter_style::{self, CounterBound};
 use cssparser::UnicodeRange;
 use font_face::{Source, FontDisplay, FontWeight};
-use gecko_bindings::bindings;
 use gecko_bindings::structs::{self, nsCSSValue};
-use gecko_bindings::structs::{nsCSSCounterDesc, nsCSSCounterStyleRule};
 use gecko_bindings::sugar::ns_css_value::ToNsCssValue;
-use gecko_bindings::sugar::refptr::{RefPtr, UniqueRefPtr};
-use nsstring::nsString;
 use properties::longhands::font_language_override;
-use shared_lock::{ToCssWithGuard, SharedRwLockReadGuard};
-use std::fmt::{self, Write};
 use std::str;
-use str::CssStringWriter;
 use values::computed::font::FamilyName;
 use values::generics::font::FontTag;
 use values::specified::font::{SpecifiedFontVariationSettings, SpecifiedFontFeatureSettings};
 
 impl<'a> ToNsCssValue for &'a FamilyName {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         nscssvalue.set_string_from_atom(&self.name)
     }
@@ -187,111 +180,72 @@ impl<'a> ToNsCssValue for &'a FontDispla
             FontDisplay::Block => structs::NS_FONT_DISPLAY_BLOCK,
             FontDisplay::Swap => structs::NS_FONT_DISPLAY_SWAP,
             FontDisplay::Fallback => structs::NS_FONT_DISPLAY_FALLBACK,
             FontDisplay::Optional => structs::NS_FONT_DISPLAY_OPTIONAL,
         } as i32)
     }
 }
 
-/// A @counter-style rule
-pub type CounterStyleRule = RefPtr<nsCSSCounterStyleRule>;
-
-impl CounterStyleRule {
-    /// Ask Gecko to deep clone the nsCSSCounterStyleRule, and then construct
-    /// a CounterStyleRule object from it.
-    pub fn deep_clone_from_gecko(&self) -> CounterStyleRule {
-        let result = unsafe {
-            UniqueRefPtr::from_addrefed(
-                bindings::Gecko_CSSCounterStyle_Clone(self.get()))
-        };
-        result.get()
-    }
-}
-
-impl From<counter_style::CounterStyleRuleData> for CounterStyleRule {
-    fn from(data: counter_style::CounterStyleRuleData) -> CounterStyleRule {
-        let mut result = unsafe {
-            UniqueRefPtr::from_addrefed(
-                bindings::Gecko_CSSCounterStyle_Create(data.name().0.as_ptr()))
-        };
-        data.set_descriptors(&mut result.mValues);
-        result.get()
-    }
-}
-
-impl ToCssWithGuard for CounterStyleRule {
-    fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
-        let mut css_text = nsString::new();
-        unsafe {
-            bindings::Gecko_CSSCounterStyle_GetCssText(self.get(), &mut *css_text);
-        }
-        write!(dest, "{}", css_text)
-    }
-}
-
-/// The type of nsCSSCounterStyleRule::mValues
-pub type CounterStyleDescriptors = [nsCSSValue; nsCSSCounterDesc::eCSSCounterDesc_COUNT as usize];
-
-impl ToNsCssValue for counter_style::System {
+impl<'a> ToNsCssValue for &'a counter_style::System {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         use counter_style::System::*;
-        match self {
+        match *self {
             Cyclic => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_CYCLIC as i32),
             Numeric => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_NUMERIC as i32),
             Alphabetic => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_ALPHABETIC as i32),
             Symbolic => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_SYMBOLIC as i32),
             Additive => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_ADDITIVE as i32),
-            Fixed { first_symbol_value } => {
+            Fixed { ref first_symbol_value } => {
                 let mut a = nsCSSValue::null();
                 let mut b = nsCSSValue::null();
                 a.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_FIXED as i32);
                 b.set_integer(first_symbol_value.map_or(1, |v| v.value()));
                 nscssvalue.set_pair(&a, &b);
             }
-            Extends(other) => {
+            Extends(ref other) => {
                 let mut a = nsCSSValue::null();
                 let mut b = nsCSSValue::null();
                 a.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_EXTENDS as i32);
-                b.set_atom_ident(other.0);
+                b.set_atom_ident(other.0.clone());
                 nscssvalue.set_pair(&a, &b);
             }
         }
     }
 }
 
-impl ToNsCssValue for counter_style::Negative {
+impl<'a> ToNsCssValue for &'a counter_style::Negative {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
-        if let Some(second) = self.1 {
+        if let Some(ref second) = self.1 {
             let mut a = nsCSSValue::null();
             let mut b = nsCSSValue::null();
-            a.set_from(self.0);
+            a.set_from(&self.0);
             b.set_from(second);
             nscssvalue.set_pair(&a, &b);
         } else {
-            nscssvalue.set_from(self.0)
+            nscssvalue.set_from(&self.0)
         }
     }
 }
 
-impl ToNsCssValue for counter_style::Symbol {
+impl<'a> ToNsCssValue for &'a counter_style::Symbol {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
-        match self {
-            counter_style::Symbol::String(s) => nscssvalue.set_string(&s),
-            counter_style::Symbol::Ident(s) => nscssvalue.set_ident_from_atom(&s.0),
+        match *self {
+            counter_style::Symbol::String(ref s) => nscssvalue.set_string(s),
+            counter_style::Symbol::Ident(ref s) => nscssvalue.set_ident_from_atom(&s.0),
         }
     }
 }
 
-impl ToNsCssValue for counter_style::Ranges {
+impl<'a> ToNsCssValue for &'a counter_style::Ranges {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         if self.0.is_empty() {
             nscssvalue.set_auto();
         } else {
-            nscssvalue.set_pair_list(self.0.into_iter().map(|range| {
+            nscssvalue.set_pair_list(self.0.iter().map(|range| {
                 fn set_bound(bound: CounterBound, nscssvalue: &mut nsCSSValue) {
                     if let CounterBound::Integer(finite) = bound {
                         nscssvalue.set_integer(finite.value())
                     } else {
                         nscssvalue.set_enum(structs::NS_STYLE_COUNTER_RANGE_INFINITE as i32)
                     }
                 }
                 let mut start = nsCSSValue::null();
@@ -299,58 +253,58 @@ impl ToNsCssValue for counter_style::Ran
                 set_bound(range.start, &mut start);
                 set_bound(range.end, &mut end);
                 (start, end)
             }));
         }
     }
 }
 
-impl ToNsCssValue for counter_style::Pad {
+impl<'a> ToNsCssValue for &'a counter_style::Pad {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         let mut min_length = nsCSSValue::null();
         let mut pad_with = nsCSSValue::null();
         min_length.set_integer(self.0.value());
-        pad_with.set_from(self.1);
+        pad_with.set_from(&self.1);
         nscssvalue.set_pair(&min_length, &pad_with);
     }
 }
 
-impl ToNsCssValue for counter_style::Fallback {
+impl<'a> ToNsCssValue for &'a counter_style::Fallback {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
-        nscssvalue.set_atom_ident(self.0 .0)
+        nscssvalue.set_atom_ident(self.0 .0.clone())
     }
 }
 
-impl ToNsCssValue for counter_style::Symbols {
+impl<'a> ToNsCssValue for &'a counter_style::Symbols {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
-        nscssvalue.set_list(self.0.into_iter().map(|item| {
+        nscssvalue.set_list(self.0.iter().map(|item| {
             let mut value = nsCSSValue::null();
             value.set_from(item);
             value
         }));
     }
 }
 
-impl ToNsCssValue for counter_style::AdditiveSymbols {
+impl<'a> ToNsCssValue for &'a counter_style::AdditiveSymbols {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
-        nscssvalue.set_pair_list(self.0.into_iter().map(|tuple| {
+        nscssvalue.set_pair_list(self.0.iter().map(|tuple| {
             let mut weight = nsCSSValue::null();
             let mut symbol = nsCSSValue::null();
             weight.set_integer(tuple.weight.value());
-            symbol.set_from(tuple.symbol);
+            symbol.set_from(&tuple.symbol);
             (weight, symbol)
         }));
     }
 }
 
-impl ToNsCssValue for counter_style::SpeakAs {
+impl<'a> ToNsCssValue for &'a counter_style::SpeakAs {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         use counter_style::SpeakAs::*;
-        match self {
+        match *self {
             Auto => nscssvalue.set_auto(),
             Bullets => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SPEAKAS_BULLETS as i32),
             Numbers => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SPEAKAS_NUMBERS as i32),
             Words => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SPEAKAS_WORDS as i32),
-            Other(other) => nscssvalue.set_atom_ident(other.0),
+            Other(ref other) => nscssvalue.set_atom_ident(other.0.clone()),
         }
     }
 }
--- a/servo/components/style/gecko_bindings/sugar/refptr.rs
+++ b/servo/components/style/gecko_bindings/sugar/refptr.rs
@@ -255,19 +255,16 @@ macro_rules! impl_refcount {
             }
             unsafe fn release(&self) {
                 ::gecko_bindings::bindings::$release(self as *const _ as *mut _)
             }
         }
     );
 }
 
-impl_refcount!(::gecko_bindings::structs::nsCSSCounterStyleRule,
-               Gecko_CSSCounterStyleRule_AddRef, Gecko_CSSCounterStyleRule_Release);
-
 // Companion of NS_DECL_THREADSAFE_FFI_REFCOUNTING.
 //
 // Gets you a free RefCounted impl implemented via FFI.
 macro_rules! impl_threadsafe_refcount {
     ($t:ty, $addref:ident, $release:ident) => (
         impl_refcount!($t, $addref, $release);
         unsafe impl ThreadSafeRefCounted for $t {}
     );
--- a/servo/components/style/stylesheets/counter_style_rule.rs
+++ b/servo/components/style/stylesheets/counter_style_rule.rs
@@ -1,24 +1,7 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-// TODO(emilio): unify this, components/style/counter_style.rs, and
-// components/style/gecko/rules.rs
 #![allow(missing_docs)]
 
-#[cfg(feature = "servo")]
 pub use counter_style::CounterStyleRuleData as CounterStyleRule;
-#[cfg(feature = "gecko")]
-pub use gecko::rules::CounterStyleRule;
-
-impl CounterStyleRule {
-    #[cfg(feature = "servo")]
-    pub fn clone_conditionally_gecko_or_servo(&self) -> CounterStyleRule {
-        self.clone()
-    }
-
-    #[cfg(feature = "gecko")]
-    pub fn clone_conditionally_gecko_or_servo(&self) -> CounterStyleRule {
-        self.deep_clone_from_gecko()
-    }
-}
--- a/servo/components/style/stylesheets/mod.rs
+++ b/servo/components/style/stylesheets/mod.rs
@@ -309,18 +309,17 @@ impl DeepCloneWithLock for CssRule {
                 CssRule::FontFace(Arc::new(lock.wrap(rule.clone())))
             },
             CssRule::FontFeatureValues(ref arc) => {
                 let rule = arc.read_with(guard);
                 CssRule::FontFeatureValues(Arc::new(lock.wrap(rule.clone())))
             },
             CssRule::CounterStyle(ref arc) => {
                 let rule = arc.read_with(guard);
-                CssRule::CounterStyle(Arc::new(lock.wrap(
-                    rule.clone_conditionally_gecko_or_servo())))
+                CssRule::CounterStyle(Arc::new(lock.wrap(rule.clone())))
             },
             CssRule::Viewport(ref arc) => {
                 let rule = arc.read_with(guard);
                 CssRule::Viewport(Arc::new(lock.wrap(rule.clone())))
             },
             CssRule::Keyframes(ref arc) => {
                 let rule = arc.read_with(guard);
                 CssRule::Keyframes(Arc::new(
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -1606,18 +1606,16 @@ pub struct ExtraStyleData {
     #[cfg(feature = "gecko")]
     pub counter_styles: PrecomputedHashMap<Atom, Arc<Locked<CounterStyleRule>>>,
 
     /// A map of effective page rules.
     #[cfg(feature = "gecko")]
     pub pages: Vec<Arc<Locked<PageRule>>>,
 }
 
-// FIXME(emilio): This is kind of a lie, and relies on us not cloning
-// nsCSSCounterStyleRules OMT (which we don't).
 #[cfg(feature = "gecko")]
 unsafe impl Sync for ExtraStyleData {}
 #[cfg(feature = "gecko")]
 unsafe impl Send for ExtraStyleData {}
 
 #[cfg(feature = "gecko")]
 impl ExtraStyleData {
     /// Add the given @font-face rule.
@@ -1631,17 +1629,17 @@ impl ExtraStyleData {
     }
 
     /// Add the given @counter-style rule.
     fn add_counter_style(
         &mut self,
         guard: &SharedRwLockReadGuard,
         rule: &Arc<Locked<CounterStyleRule>>,
     ) {
-        let name = unsafe { Atom::from_raw(rule.read_with(guard).mName.mRawPtr) };
+        let name = rule.read_with(guard).name().0.clone();
         self.counter_styles.insert(name, rule.clone());
     }
 
     /// Add the given @page rule.
     fn add_page(&mut self, rule: &Arc<Locked<PageRule>>) {
         self.pages.push(rule.clone());
     }
 }
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -34,16 +34,17 @@ use style::gecko::restyle_damage::GeckoR
 use style::gecko::selector_parser::{NonTSPseudoClass, PseudoElement};
 use style::gecko::traversal::RecalcStyleOnly;
 use style::gecko::wrapper::{GeckoElement, GeckoNode};
 use style::gecko_bindings::bindings;
 use style::gecko_bindings::bindings::{RawGeckoElementBorrowed, RawGeckoElementBorrowedOrNull, RawGeckoNodeBorrowed};
 use style::gecko_bindings::bindings::{RawGeckoKeyframeListBorrowed, RawGeckoKeyframeListBorrowedMut};
 use style::gecko_bindings::bindings::{RawServoAuthorStyles, RawServoAuthorStylesBorrowed};
 use style::gecko_bindings::bindings::{RawServoAuthorStylesBorrowedMut, RawServoAuthorStylesOwned};
+use style::gecko_bindings::bindings::{RawServoCounterStyleRule, RawServoCounterStyleRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoDeclarationBlockBorrowed, RawServoDeclarationBlockStrong};
 use style::gecko_bindings::bindings::{RawServoDocumentRule, RawServoDocumentRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoFontFaceRuleBorrowed, RawServoFontFaceRuleStrong};
 use style::gecko_bindings::bindings::{RawServoFontFeatureValuesRule, RawServoFontFeatureValuesRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoImportRule, RawServoImportRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoKeyframe, RawServoKeyframeBorrowed, RawServoKeyframeStrong};
 use style::gecko_bindings::bindings::{RawServoKeyframesRule, RawServoKeyframesRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoMediaListBorrowed, RawServoMediaListStrong};
@@ -87,17 +88,17 @@ use style::gecko_bindings::bindings::nsC
 use style::gecko_bindings::bindings::nsTArrayBorrowed_uintptr_t;
 use style::gecko_bindings::bindings::nsTimingFunctionBorrowed;
 use style::gecko_bindings::bindings::nsTimingFunctionBorrowedMut;
 use style::gecko_bindings::structs;
 use style::gecko_bindings::structs::{CallerType, CSSPseudoElementType, CompositeOperation};
 use style::gecko_bindings::structs::{Loader, LoaderReusableStyleSheets};
 use style::gecko_bindings::structs::{RawServoStyleRule, ComputedStyleStrong, RustString};
 use style::gecko_bindings::structs::{ServoStyleSheet, SheetLoadData, SheetParsingMode, nsAtom, nsCSSPropertyID};
-use style::gecko_bindings::structs::{nsCSSFontDesc, nsCSSCounterStyleRule};
+use style::gecko_bindings::structs::{nsCSSFontDesc, nsCSSCounterDesc};
 use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, PropertyValuePair};
 use style::gecko_bindings::structs::AtomArray;
 use style::gecko_bindings::structs::IterationCompositeOperation;
 use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf;
 use style::gecko_bindings::structs::OriginFlags;
 use style::gecko_bindings::structs::OriginFlags_Author;
 use style::gecko_bindings::structs::OriginFlags_User;
 use style::gecko_bindings::structs::OriginFlags_UserAgent;
@@ -108,18 +109,16 @@ use style::gecko_bindings::structs::RawS
 use style::gecko_bindings::structs::RawServoSourceSizeList;
 use style::gecko_bindings::structs::SeenPtrs;
 use style::gecko_bindings::structs::ServoElementSnapshotTable;
 use style::gecko_bindings::structs::ServoStyleSetSizes;
 use style::gecko_bindings::structs::ServoTraversalFlags;
 use style::gecko_bindings::structs::StyleRuleInclusion;
 use style::gecko_bindings::structs::URLExtraData;
 use style::gecko_bindings::structs::gfxFontFeatureValueSet;
-use style::gecko_bindings::structs::nsCSSCounterDesc;
-use style::gecko_bindings::structs::nsCSSValue;
 use style::gecko_bindings::structs::nsCSSValueSharedList;
 use style::gecko_bindings::structs::nsCompatibility;
 use style::gecko_bindings::structs::nsIDocument;
 use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
 use style::gecko_bindings::structs::nsTArray;
 use style::gecko_bindings::structs::nsresult;
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
@@ -136,20 +135,20 @@ use style::properties::{parse_one_declar
 use style::properties::animated_properties::AnimationValue;
 use style::properties::animated_properties::compare_property_priority;
 use style::rule_cache::RuleCacheConditions;
 use style::rule_tree::{CascadeLevel, StrongRuleNode, StyleSource};
 use style::selector_parser::{PseudoElementCascadeType, SelectorImpl};
 use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
 use style::string_cache::{Atom, WeakAtom};
 use style::style_adjuster::StyleAdjuster;
-use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule};
-use style::stylesheets::{FontFaceRule, FontFeatureValuesRule, ImportRule, KeyframesRule};
-use style::stylesheets::{MediaRule, NamespaceRule, Origin, OriginSet, PageRule, StyleRule};
-use style::stylesheets::{StylesheetContents, SupportsRule};
+use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, CounterStyleRule};
+use style::stylesheets::{DocumentRule, FontFaceRule, FontFeatureValuesRule, ImportRule};
+use style::stylesheets::{KeyframesRule, MediaRule, NamespaceRule, Origin, OriginSet, PageRule};
+use style::stylesheets::{StyleRule, StylesheetContents, SupportsRule};
 use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
 use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
 use style::stylesheets::supports_rule::parse_condition_or_declaration;
 use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
 use style::thread_state;
 use style::timer::Timer;
 use style::traversal::DomTraversal;
 use style::traversal::resolve_style;
@@ -1749,35 +1748,21 @@ impl_basic_rule_funcs! { (FontFeatureVal
 }
 
 impl_basic_rule_funcs! { (FontFace, FontFaceRule, RawServoFontFaceRule),
     getter: Servo_CssRules_GetFontFaceRuleAt,
     debug: Servo_FontFaceRule_Debug,
     to_css: Servo_FontFaceRule_GetCssText,
 }
 
-macro_rules! impl_getter_for_embedded_rule {
-    ($getter:ident: $name:ident -> $ty:ty) => {
-        #[no_mangle]
-        pub extern "C" fn $getter(rules: ServoCssRulesBorrowed, index: u32) -> *mut $ty
-        {
-            let global_style_data = &*GLOBAL_STYLE_DATA;
-            let guard = global_style_data.shared_lock.read();
-            let rules = Locked::<CssRules>::as_arc(&rules).read_with(&guard);
-            match rules.0[index as usize] {
-                CssRule::$name(ref rule) => rule.read_with(&guard).get(),
-                _ => unreachable!(concat!(stringify!($getter), " should only be called on a ",
-                                          stringify!($name), " rule")),
-            }
-        }
-    }
-}
-
-impl_getter_for_embedded_rule!(Servo_CssRules_GetCounterStyleRuleAt:
-                               CounterStyle -> nsCSSCounterStyleRule);
+impl_basic_rule_funcs! { (CounterStyle, CounterStyleRule, RawServoCounterStyleRule),
+    getter: Servo_CssRules_GetCounterStyleRuleAt,
+    debug: Servo_CounterStyleRule_Debug,
+    to_css: Servo_CounterStyleRule_GetCssText,
+}
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleRule_GetStyle(rule: RawServoStyleRuleBorrowed) -> RawServoDeclarationBlockStrong {
     read_locked_arc(rule, |rule: &StyleRule| {
         rule.block.clone().into_strong()
     })
 }
 
@@ -2434,16 +2419,208 @@ pub unsafe extern "C" fn Servo_FontFaceR
                 }
             }
         }
         apply_font_desc_list!(reset_desc)
     })
 }
 
 #[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetName(
+    rule: RawServoCounterStyleRuleBorrowed,
+) -> *mut nsAtom {
+    read_locked_arc(rule, |rule: &CounterStyleRule| {
+        rule.name().0.as_ptr()
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_SetName(
+    rule: RawServoCounterStyleRuleBorrowed,
+    value: *const nsACString,
+) -> bool {
+    let value = value.as_ref().unwrap().as_str_unchecked();
+    let mut input = ParserInput::new(&value);
+    let mut parser = Parser::new(&mut input);
+    match parser.parse_entirely(counter_style::parse_counter_style_name_definition) {
+        Ok(name) => {
+            write_locked_arc(rule, |rule: &mut CounterStyleRule| rule.set_name(name));
+            true
+        }
+        Err(_) => false,
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetGeneration(
+    rule: RawServoCounterStyleRuleBorrowed,
+) -> u32 {
+    read_locked_arc(rule, |rule: &CounterStyleRule| {
+        rule.generation()
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetSystem(
+    rule: RawServoCounterStyleRuleBorrowed,
+) -> u8 {
+    use style::counter_style::System;
+    read_locked_arc(rule, |rule: &CounterStyleRule| {
+        match *rule.get_system() {
+            System::Cyclic => structs::NS_STYLE_COUNTER_SYSTEM_CYCLIC,
+            System::Numeric => structs::NS_STYLE_COUNTER_SYSTEM_NUMERIC,
+            System::Alphabetic => structs::NS_STYLE_COUNTER_SYSTEM_ALPHABETIC,
+            System::Symbolic => structs::NS_STYLE_COUNTER_SYSTEM_SYMBOLIC,
+            System::Additive => structs::NS_STYLE_COUNTER_SYSTEM_ADDITIVE,
+            System::Fixed { .. } => structs::NS_STYLE_COUNTER_SYSTEM_FIXED,
+            System::Extends(_) => structs::NS_STYLE_COUNTER_SYSTEM_EXTENDS,
+        }
+    }) as u8
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetExtended(
+    rule: RawServoCounterStyleRuleBorrowed,
+) -> *mut nsAtom {
+    read_locked_arc(rule, |rule: &CounterStyleRule| {
+        match *rule.get_system() {
+            counter_style::System::Extends(ref name) => name.0.as_ptr(),
+            _ => {
+                debug_assert!(false, "Not extends system");
+                ptr::null_mut()
+            }
+        }
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetFixedFirstValue(
+    rule: RawServoCounterStyleRuleBorrowed,
+) -> i32 {
+    read_locked_arc(rule, |rule: &CounterStyleRule| {
+        match *rule.get_system() {
+            counter_style::System::Fixed { first_symbol_value } => {
+                first_symbol_value.map_or(1, |v| v.value())
+            }
+            _ => {
+                debug_assert!(false, "Not fixed system");
+                0
+            }
+        }
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetFallback(
+    rule: RawServoCounterStyleRuleBorrowed,
+) -> *mut nsAtom {
+    read_locked_arc(rule, |rule: &CounterStyleRule| {
+        rule.fallback().map_or(ptr::null_mut(), |i| i.0 .0.as_ptr())
+    })
+}
+
+macro_rules! counter_style_descriptors {
+    {
+        valid: [
+            $($desc:ident => $getter:ident / $setter:ident,)+
+        ]
+        invalid: [
+            $($i_desc:ident,)+
+        ]
+    } => {
+        #[no_mangle]
+        pub unsafe extern "C" fn Servo_CounterStyleRule_GetDescriptor(
+            rule: RawServoCounterStyleRuleBorrowed,
+            desc: nsCSSCounterDesc,
+            result: nsCSSValueBorrowedMut,
+        ) {
+            read_locked_arc(rule, |rule: &CounterStyleRule| {
+                match desc {
+                    $(nsCSSCounterDesc::$desc => {
+                        if let Some(value) = rule.$getter() {
+                            result.set_from(value);
+                        }
+                    })+
+                    $(nsCSSCounterDesc::$i_desc => unreachable!(),)+
+                }
+            });
+        }
+
+        #[no_mangle]
+        pub unsafe extern "C" fn Servo_CounterStyleRule_GetDescriptorCssText(
+            rule: RawServoCounterStyleRuleBorrowed,
+            desc: nsCSSCounterDesc,
+            result: *mut nsAString,
+        ) {
+            let mut writer = CssWriter::new(result.as_mut().unwrap());
+            read_locked_arc(rule, |rule: &CounterStyleRule| {
+                match desc {
+                    $(nsCSSCounterDesc::$desc => {
+                        if let Some(value) = rule.$getter() {
+                            value.to_css(&mut writer).unwrap();
+                        }
+                    })+
+                    $(nsCSSCounterDesc::$i_desc => unreachable!(),)+
+                }
+            });
+        }
+
+        #[no_mangle]
+        pub unsafe extern "C" fn Servo_CounterStyleRule_SetDescriptor(
+            rule: RawServoCounterStyleRuleBorrowed,
+            desc: nsCSSCounterDesc,
+            value: *mut nsACString,
+        ) -> bool {
+            let value = value.as_ref().unwrap().as_str_unchecked();
+            let mut input = ParserInput::new(&value);
+            let mut parser = Parser::new(&mut input);
+            let url_data = dummy_url_data();
+            let context = ParserContext::new(
+                Origin::Author,
+                url_data,
+                Some(CssRuleType::CounterStyle),
+                ParsingMode::DEFAULT,
+                QuirksMode::NoQuirks,
+            );
+
+            write_locked_arc(rule, |rule: &mut CounterStyleRule| {
+                match desc {
+                    $(nsCSSCounterDesc::$desc => {
+                        match parser.parse_entirely(|i| Parse::parse(&context, i)) {
+                            Ok(value) => rule.$setter(value),
+                            Err(_) => false,
+                        }
+                    })+
+                    $(nsCSSCounterDesc::$i_desc => unreachable!(),)+
+                }
+            })
+        }
+    }
+}
+
+counter_style_descriptors! {
+    valid: [
+        eCSSCounterDesc_System => system / set_system,
+        eCSSCounterDesc_Symbols => symbols / set_symbols,
+        eCSSCounterDesc_AdditiveSymbols => additive_symbols / set_additive_symbols,
+        eCSSCounterDesc_Negative => negative / set_negative,
+        eCSSCounterDesc_Prefix => prefix / set_prefix,
+        eCSSCounterDesc_Suffix => suffix / set_suffix,
+        eCSSCounterDesc_Range => range / set_range,
+        eCSSCounterDesc_Pad => pad / set_pad,
+        eCSSCounterDesc_Fallback => fallback / set_fallback,
+        eCSSCounterDesc_SpeakAs => speak_as / set_speak_as,
+    ]
+    invalid: [
+        eCSSCounterDesc_UNKNOWN,
+        eCSSCounterDesc_COUNT,
+    ]
+}
+
+#[no_mangle]
 pub unsafe extern "C" fn Servo_ComputedValues_GetForAnonymousBox(
     parent_style_or_null: ComputedStyleBorrowedOrNull,
     pseudo_tag: *mut nsAtom,
     raw_data: RawServoStyleSetBorrowed,
 ) -> ComputedStyleStrong {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let guard = global_style_data.shared_lock.read();
     let guards = StylesheetGuards::same(&guard);
@@ -4600,35 +4777,33 @@ pub extern "C" fn Servo_StyleSet_GetFont
 
     unsafe { rules.set_len(len) };
     for (src, dest) in font_face_iter.zip(rules.iter_mut()) {
         dest.mRule.set_arc_leaky(src.0.clone());
         dest.mSheetType = src.1.into();
     }
 }
 
-#[no_mangle]
-pub extern "C" fn Servo_StyleSet_GetCounterStyleRule(
+// XXX Ideally this should return a RawServoCounterStyleRuleBorrowedOrNull,
+// but we cannot, because the value from AtomicRefCell::borrow() can only
+// live in this function, and thus anything derived from it cannot get the
+// same lifetime as raw_data in parameter.
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleSet_GetCounterStyleRule(
     raw_data: RawServoStyleSetBorrowed,
     name: *mut nsAtom,
-) -> *mut nsCSSCounterStyleRule {
+) -> *const RawServoCounterStyleRule {
     let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
-
-    unsafe {
-        Atom::with(name, |name| {
-            data.stylist
-                .iter_extra_data_origins()
-                .filter_map(|(d, _)| d.counter_styles.get(name))
-                .next()
-        })
-    }.map(|rule| {
-        let global_style_data = &*GLOBAL_STYLE_DATA;
-        let guard = global_style_data.shared_lock.read();
-        rule.read_with(&guard).get()
-    }).unwrap_or(ptr::null_mut())
+    Atom::with(name, |name| {
+        data.stylist
+            .iter_extra_data_origins()
+            .filter_map(|(d, _)| d.counter_styles.get(name))
+            .next()
+            .map_or(ptr::null(), |rule| rule.as_borrowed())
+    })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_BuildFontFeatureValueSet(
     raw_data: RawServoStyleSetBorrowed,
 ) -> *mut gfxFontFeatureValueSet {
     let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
 
@@ -5201,29 +5376,16 @@ pub unsafe extern "C" fn Servo_SourceSiz
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_SourceSizeList_Drop(list: RawServoSourceSizeListOwned) {
     let _ = list.into_box::<SourceSizeList>();
 }
 
 #[no_mangle]
-pub extern "C" fn Servo_ParseCounterStyleName(
-    value: *const nsACString,
-) -> *mut nsAtom {
-    let value = unsafe { value.as_ref().unwrap().as_str_unchecked() };
-    let mut input = ParserInput::new(&value);
-    let mut parser = Parser::new(&mut input);
-    match parser.parse_entirely(counter_style::parse_counter_style_name_definition) {
-        Ok(name) => name.0.into_addrefed(),
-        Err(_) => ptr::null_mut(),
-    }
-}
-
-#[no_mangle]
 pub unsafe extern "C" fn Servo_InvalidateStyleForDocStateChanges(
     root: RawGeckoElementBorrowed,
     document_style: RawServoStyleSetBorrowed,
     non_document_styles: *const nsTArray<RawServoAuthorStylesBorrowed>,
     states_changed: u64,
 ) {
     use style::invalidation::element::document_state::DocumentStateInvalidationProcessor;
     use style::invalidation::element::invalidator::TreeStyleInvalidator;
@@ -5254,49 +5416,16 @@ pub unsafe extern "C" fn Servo_Invalidat
     if result.has_invalidated_descendants() {
         bindings::Gecko_NoteDirtySubtreeForInvalidation(root.0);
     } else if result.has_invalidated_self() {
         bindings::Gecko_NoteDirtyElement(root.0);
     }
 }
 
 #[no_mangle]
-pub extern "C" fn Servo_ParseCounterStyleDescriptor(
-    descriptor: nsCSSCounterDesc,
-    value: *const nsACString,
-    raw_extra_data: *mut URLExtraData,
-    result: *mut nsCSSValue,
-) -> bool {
-    let value = unsafe { value.as_ref().unwrap().as_str_unchecked() };
-    let url_data = unsafe {
-        if raw_extra_data.is_null() {
-            dummy_url_data()
-        } else {
-            RefPtr::from_ptr_ref(&raw_extra_data)
-        }
-    };
-    let result = unsafe { result.as_mut().unwrap() };
-    let mut input = ParserInput::new(&value);
-    let mut parser = Parser::new(&mut input);
-    let context = ParserContext::new(
-        Origin::Author,
-        url_data,
-        Some(CssRuleType::CounterStyle),
-        ParsingMode::DEFAULT,
-        QuirksMode::NoQuirks,
-    );
-    counter_style::parse_counter_style_descriptor(
-        &context,
-        &mut parser,
-        descriptor,
-        result,
-    ).is_ok()
-}
-
-#[no_mangle]
 pub unsafe extern "C" fn Servo_PseudoClass_GetStates(name: *const nsACString) -> u64 {
     let name = name.as_ref().unwrap().as_str_unchecked();
     match NonTSPseudoClass::parse_non_functional(name) {
         None => 0,
         // Ignore :any-link since it contains both visited and unvisited state.
         Some(NonTSPseudoClass::AnyLink) => 0,
         Some(pseudo_class) => pseudo_class.state_flag().bits(),
     }