Bug 966166 - Part 1: Parse @counter-style rule. r=dbaron, r=bz
authorXidorn Quan <quanxunzhen@gmail.com>
Wed, 11 Jun 2014 21:10:00 -0400
changeset 188451 d21febcb08a3
parent 188450 dffaea7f1b9a
child 188452 001216bdc605
push id26956
push userkwierso@gmail.com
push dateFri, 13 Jun 2014 00:23:52 +0000
treeherdermozilla-central@adcf3f05f813 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron, bz
bugs966166
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 966166 - Part 1: Parse @counter-style rule. r=dbaron, r=bz
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/interfaces/css/moz.build
dom/interfaces/css/nsIDOMCSSCounterStyleRule.idl
dom/interfaces/css/nsIDOMCSSRule.idl
dom/locales/en-US/chrome/layout/css.properties
dom/tests/mochitest/general/test_interfaces.html
layout/style/Rule.h
layout/style/moz.build
layout/style/nsCSSCounterDescList.h
layout/style/nsCSSKeywordList.h
layout/style/nsCSSParser.cpp
layout/style/nsCSSParser.h
layout/style/nsCSSProperty.h
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsCSSRuleProcessor.cpp
layout/style/nsCSSRuleProcessor.h
layout/style/nsCSSRules.cpp
layout/style/nsCSSRules.h
layout/style/nsCSSStyleSheet.cpp
layout/style/nsStyleConsts.h
layout/style/nsStyleSet.cpp
layout/style/nsStyleSet.h
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -93,16 +93,17 @@
 #include "nsIDOMCSSCharsetRule.h"
 #include "nsIDOMCSSImportRule.h"
 #include "nsIDOMCSSMediaRule.h"
 #include "nsIDOMCSSFontFaceRule.h"
 #include "nsIDOMCSSMozDocumentRule.h"
 #include "nsIDOMCSSSupportsRule.h"
 #include "nsIDOMMozCSSKeyframeRule.h"
 #include "nsIDOMMozCSSKeyframesRule.h"
+#include "nsIDOMCSSCounterStyleRule.h"
 #include "nsIDOMCSSPageRule.h"
 #include "nsIDOMCSSStyleRule.h"
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIControllers.h"
 #include "nsIBoxObject.h"
 #ifdef MOZ_XUL
 #include "nsITreeSelection.h"
 #include "nsITreeContentView.h"
@@ -406,16 +407,19 @@ static nsDOMClassInfoData sClassInfoData
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
 
   NS_DEFINE_CLASSINFO_DATA(MozCSSKeyframeRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MozCSSKeyframesRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
+  NS_DEFINE_CLASSINFO_DATA(CSSCounterStyleRule, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
   NS_DEFINE_CLASSINFO_DATA(CSSPageRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(CSSFontFeatureValuesRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(UserDataHandler, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -1031,16 +1035,20 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_BEGIN(MozCSSKeyframeRule, nsIDOMMozCSSKeyframeRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozCSSKeyframeRule)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(MozCSSKeyframesRule, nsIDOMMozCSSKeyframesRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozCSSKeyframesRule)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(CSSCounterStyleRule, nsIDOMCSSCounterStyleRule)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSCounterStyleRule)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(CSSPageRule, nsIDOMCSSPageRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSPageRule)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(CSSFontFeatureValuesRule, nsIDOMCSSFontFeatureValuesRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSFontFeatureValuesRule)
   DOM_CLASSINFO_MAP_END
 
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -71,16 +71,19 @@ DOMCI_CLASS(CSSFontFaceRule)
 
 DOMCI_CLASS(ContentFrameMessageManager)
 DOMCI_CLASS(ChromeMessageBroadcaster)
 DOMCI_CLASS(ChromeMessageSender)
 
 DOMCI_CLASS(MozCSSKeyframeRule)
 DOMCI_CLASS(MozCSSKeyframesRule)
 
+// @counter-style in CSS
+DOMCI_CLASS(CSSCounterStyleRule)
+
 DOMCI_CLASS(CSSPageRule)
 
 DOMCI_CLASS(CSSFontFeatureValuesRule)
 
 DOMCI_CLASS(UserDataHandler)
 DOMCI_CLASS(XULControlElement)
 DOMCI_CLASS(XULLabeledControlElement)
 DOMCI_CLASS(XULButtonElement)
--- a/dom/interfaces/css/moz.build
+++ b/dom/interfaces/css/moz.build
@@ -3,16 +3,17 @@
 # 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/.
 
 XPIDL_SOURCES += [
     'nsIDOMCounter.idl',
     'nsIDOMCSSCharsetRule.idl',
     'nsIDOMCSSConditionRule.idl',
+    'nsIDOMCSSCounterStyleRule.idl',
     'nsIDOMCSSFontFaceRule.idl',
     'nsIDOMCSSFontFeatureValuesRule.idl',
     'nsIDOMCSSGroupingRule.idl',
     'nsIDOMCSSImportRule.idl',
     'nsIDOMCSSMediaRule.idl',
     'nsIDOMCSSMozDocumentRule.idl',
     'nsIDOMCSSPageRule.idl',
     'nsIDOMCSSPrimitiveValue.idl',
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/css/nsIDOMCSSCounterStyleRule.idl
@@ -0,0 +1,23 @@
+/* -*- Mode: IDL; tab-width: 2; 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 "nsIDOMCSSRule.idl"
+
+[scriptable, uuid(5f9f2068-743b-42e3-becb-10ffa994d1e3)]
+interface nsIDOMCSSCounterStyleRule : nsIDOMCSSRule
+{
+  attribute DOMString name;
+  attribute DOMString system;
+  attribute DOMString symbols;
+  attribute DOMString additiveSymbols;
+  attribute DOMString negative;
+  attribute DOMString prefix;
+  attribute DOMString suffix;
+  attribute DOMString range;
+  attribute DOMString pad;
+  attribute DOMString speakAs;
+  attribute DOMString fallback;
+};
--- a/dom/interfaces/css/nsIDOMCSSRule.idl
+++ b/dom/interfaces/css/nsIDOMCSSRule.idl
@@ -26,16 +26,17 @@ interface nsIDOMCSSRule : nsISupports
   const unsigned short      PAGE_RULE                      = 6;
   const unsigned short      KEYFRAMES_RULE                 = 7;
   const unsigned short      KEYFRAME_RULE                  = 8;
   // When layout.css.prefixes.animations is disabled/removed,
   // we should remove these two MOZ_* constants.
   const unsigned short      MOZ_KEYFRAMES_RULE             = 7;
   const unsigned short      MOZ_KEYFRAME_RULE              = 8;
   const unsigned short      NAMESPACE_RULE                 = 10;
+  const unsigned short      COUNTER_STYLE_RULE             = 11;
   const unsigned short      SUPPORTS_RULE                  = 12;
   const unsigned short      FONT_FEATURE_VALUES_RULE       = 14;
 
   readonly attribute unsigned short      type;
            attribute DOMString           cssText;
                                         // raises(DOMException) on setting
 
   readonly attribute nsIDOMCSSStyleSheet parentStyleSheet;
--- a/dom/locales/en-US/chrome/layout/css.properties
+++ b/dom/locales/en-US/chrome/layout/css.properties
@@ -39,16 +39,24 @@ PEKeyframeBrace=Expected opening { of @k
 PESkipDeclBraceEOF=closing } of declaration block
 PESkipRSBraceEOF=closing } of invalid rule set
 PEBadSelectorRSIgnored=Ruleset ignored due to bad selector.
 PEBadSelectorKeyframeRuleIgnored=Keyframe rule ignored due to bad selector.
 PESelectorListExtraEOF=',' or '{'
 PESelectorListExtra=Expected ',' or '{' but found '%1$S'.
 PESelectorGroupNoSelector=Selector expected.
 PESelectorGroupExtraCombinator=Dangling combinator.
+PECounterStyleNotIdent=Expected identifier for name of @counter-style rule.
+PECounterStyleBadName=Name of @counter-style rule can't be '%1$S'.
+PECounterStyleBadBlockStart=Expected '{' to begin @counter-style rule but found '%1$S'.
+PECounterStyleEOF=closing '}' of @counter-style block
+PECounterDescExpected=Expected counter descriptor but found '%1$S'.
+PEUnknownCounterDesc=Unknown descriptor '%1$S' in @counter-style rule.
+PECounterExtendsNotIdent=Expected identifier for extends system but found '%1$S'.
+PECounterASWeight=Each weight in the additive-symbols descriptor must be smaller than the previous weight.
 PEClassSelEOF=class name
 PEClassSelNotIdent=Expected identifier for class selector but found '%1$S'.
 PETypeSelEOF=element type
 PETypeSelNotType=Expected element name or '*' but found '%1$S'.
 PEUnknownNamespacePrefix=Unknown namespace prefix '%1$S'.
 PEAttributeNameEOF=attribute name
 PEAttributeNameExpected=Expected identifier for attribute name but found '%1$S'.
 PEAttributeNameOrNamespaceExpected=Expected attribute name or namespace but found '%1$S'.
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -220,16 +220,18 @@ var interfaceNamesInGlobalScope =
     "CSS",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSS2Properties",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSCharsetRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSConditionRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "CSSCounterStyleRule",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSFontFaceRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CSSFontFeatureValuesRule", release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSGroupingRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSImportRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/layout/style/Rule.h
+++ b/layout/style/Rule.h
@@ -62,17 +62,18 @@ public:
     STYLE_RULE,
     MEDIA_RULE,
     FONT_FACE_RULE,
     PAGE_RULE,
     KEYFRAME_RULE,
     KEYFRAMES_RULE,
     DOCUMENT_RULE,
     SUPPORTS_RULE,
-    FONT_FEATURE_VALUES_RULE
+    FONT_FEATURE_VALUES_RULE,
+    COUNTER_STYLE_RULE
   };
 
   virtual int32_t GetType() const = 0;
 
   nsCSSStyleSheet* GetStyleSheet() const;
   nsHTMLCSSStyleSheet* GetHTMLCSSStyleSheet() const;
 
   // Return the document the rule lives in, if any
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -8,16 +8,17 @@ DIRS += ['xbl-marquee']
 TEST_TOOL_DIRS += ['test']
 
 EXPORTS += [
     'AnimationCommon.h',
     'nsAnimationManager.h',
     'nsComputedDOMStylePropertyList.h',
     'nsCSSAnonBoxes.h',
     'nsCSSAnonBoxList.h',
+    'nsCSSCounterDescList.h',
     'nsCSSFontDescList.h',
     'nsCSSKeywordList.h',
     'nsCSSKeywords.h',
     'nsCSSParser.h',
     'nsCSSPropAliasList.h',
     'nsCSSProperty.h',
     'nsCSSPropList.h',
     'nsCSSProps.h',
new file mode 100644
--- /dev/null
+++ b/layout/style/nsCSSCounterDescList.h
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+CSS_COUNTER_DESC(system, System)
+CSS_COUNTER_DESC(symbols, Symbols)
+CSS_COUNTER_DESC(additive-symbols, AdditiveSymbols)
+CSS_COUNTER_DESC(negative, Negative)
+CSS_COUNTER_DESC(prefix, Prefix)
+CSS_COUNTER_DESC(suffix, Suffix)
+CSS_COUNTER_DESC(range, Range)
+CSS_COUNTER_DESC(pad, Pad)
+CSS_COUNTER_DESC(fallback, Fallback)
+CSS_COUNTER_DESC(speak-as, SpeakAs)
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -151,16 +151,17 @@ CSS_KEY(-moz-visitedhyperlinktext, _moz_
 CSS_KEY(-moz-window, _moz_window)
 CSS_KEY(-moz-workspace, _moz_workspace)
 CSS_KEY(-moz-zoom-in, _moz_zoom_in)
 CSS_KEY(-moz-zoom-out, _moz_zoom_out)
 CSS_KEY(absolute, absolute)
 CSS_KEY(active, active)
 CSS_KEY(activeborder, activeborder)
 CSS_KEY(activecaption, activecaption)
+CSS_KEY(additive, additive)
 CSS_KEY(alias, alias)
 CSS_KEY(all, all)
 CSS_KEY(all-petite-caps, all_petite_caps)
 CSS_KEY(all-scroll, all_scroll)
 CSS_KEY(all-small-caps, all_small_caps)
 CSS_KEY(alpha, alpha)
 CSS_KEY(alternate, alternate)
 CSS_KEY(alternate-reverse, alternate_reverse)
@@ -187,16 +188,17 @@ CSS_KEY(bold-script, bold_script)
 CSS_KEY(bolder, bolder)
 CSS_KEY(border-box, border_box)
 CSS_KEY(both, both)
 CSS_KEY(bottom, bottom)
 CSS_KEY(bottom-outside, bottom_outside)
 CSS_KEY(break-all, break_all)
 CSS_KEY(break-word, break_word)
 CSS_KEY(brightness, brightness)
+CSS_KEY(bullets, bullets)
 CSS_KEY(button, button)
 CSS_KEY(buttonface, buttonface)
 CSS_KEY(buttonhighlight, buttonhighlight)
 CSS_KEY(buttonshadow, buttonshadow)
 CSS_KEY(buttontext, buttontext)
 CSS_KEY(capitalize, capitalize)
 CSS_KEY(caption, caption)
 CSS_KEY(captiontext, captiontext)
@@ -235,16 +237,17 @@ CSS_KEY(contrast, contrast)
 CSS_KEY(copy, copy)
 CSS_KEY(contextual, contextual)
 CSS_KEY(cover, cover)
 CSS_KEY(crop, crop)
 CSS_KEY(cross, cross)
 CSS_KEY(crosshair, crosshair)
 CSS_KEY(currentcolor, currentcolor)
 CSS_KEY(cursive, cursive)
+CSS_KEY(cyclic, cyclic)
 CSS_KEY(darken, darken)
 CSS_KEY(dashed, dashed)
 CSS_KEY(dense, dense)
 CSS_KEY(decimal, decimal)
 CSS_KEY(decimal-leading-zero, decimal_leading_zero)
 CSS_KEY(default, default)
 CSS_KEY(deg, deg)
 CSS_KEY(diagonal-fractions, diagonal_fractions)
@@ -269,16 +272,17 @@ CSS_KEY(ellipse, ellipse)
 CSS_KEY(ellipsis, ellipsis)
 CSS_KEY(em, em)
 CSS_KEY(embed, embed)
 CSS_KEY(enabled, enabled)
 CSS_KEY(end, end)
 CSS_KEY(ex, ex)
 CSS_KEY(exclusion, exclusion)
 CSS_KEY(expanded, expanded)
+CSS_KEY(extends, extends)
 CSS_KEY(extra-condensed, extra_condensed)
 CSS_KEY(extra-expanded, extra_expanded)
 CSS_KEY(ew-resize, ew_resize)
 CSS_KEY(fantasy, fantasy)
 CSS_KEY(farthest-side, farthest_side)
 CSS_KEY(farthest-corner, farthest_corner)
 CSS_KEY(fill, fill)
 CSS_KEY(fixed, fixed)
@@ -405,16 +409,17 @@ CSS_KEY(no-discretionary-ligatures, no_d
 CSS_KEY(no-drop, no_drop)
 CSS_KEY(no-historical-ligatures, no_historical_ligatures)
 CSS_KEY(no-open-quote, no_open_quote)
 CSS_KEY(no-repeat, no_repeat)
 CSS_KEY(none, none)
 CSS_KEY(normal, normal)
 CSS_KEY(not-allowed, not_allowed)
 CSS_KEY(nowrap, nowrap)
+CSS_KEY(numeric, numeric)
 CSS_KEY(ns-resize, ns_resize)
 CSS_KEY(nw-resize, nw_resize)
 CSS_KEY(nwse-resize, nwse_resize)
 CSS_KEY(oblique, oblique)
 CSS_KEY(oldstyle-nums, oldstyle_nums)
 CSS_KEY(opacity, opacity)
 CSS_KEY(open-quote, open_quote)
 CSS_KEY(ordinal, ordinal)
@@ -506,16 +511,17 @@ CSS_KEY(small-caps, small_caps)
 CSS_KEY(small-caption, small_caption)
 CSS_KEY(smaller, smaller)
 CSS_KEY(soft, soft)
 CSS_KEY(soft-light, soft_light)
 CSS_KEY(solid, solid)
 CSS_KEY(space-around, space_around)
 CSS_KEY(space-between, space_between)
 CSS_KEY(span, span)
+CSS_KEY(spell-out, spell_out)
 CSS_KEY(square, square)
 CSS_KEY(stack, stack)
 CSS_KEY(stacked-fractions, stacked_fractions)
 CSS_KEY(start, start)
 CSS_KEY(static, static)
 CSS_KEY(status-bar, status_bar)
 CSS_KEY(step-end, step_end)
 CSS_KEY(step-start, step_start)
@@ -589,16 +595,17 @@ CSS_KEY(visiblestroke, visiblestroke)
 CSS_KEY(w-resize, w_resize)
 CSS_KEY(wait, wait)
 CSS_KEY(wavy, wavy)
 CSS_KEY(weight, weight)
 CSS_KEY(wider, wider)
 CSS_KEY(window, window)
 CSS_KEY(windowframe, windowframe)
 CSS_KEY(windowtext, windowtext)
+CSS_KEY(words, words)
 CSS_KEY(wrap, wrap)
 CSS_KEY(wrap-reverse, wrap_reverse)
 CSS_KEY(write-only, write_only)
 CSS_KEY(x-large, x_large)
 CSS_KEY(x-small, x_small)
 CSS_KEY(xx-large, xx_large)
 CSS_KEY(xx-small, xx_small)
 CSS_KEY(zoom-in, zoom_in)
@@ -621,16 +628,17 @@ CSS_KEY(button-arrow-previous, button_ar
 CSS_KEY(separator, separator)
 CSS_KEY(splitter, splitter)
 CSS_KEY(statusbar, statusbar)
 CSS_KEY(statusbarpanel, statusbarpanel)
 CSS_KEY(resizerpanel, resizerpanel)
 CSS_KEY(resizer, resizer)
 CSS_KEY(listbox, listbox)
 CSS_KEY(listitem, listitem)
+CSS_KEY(numbers, numbers)
 CSS_KEY(number-input, number_input)
 CSS_KEY(treeview, treeview)
 CSS_KEY(treeitem, treeitem)
 CSS_KEY(treetwisty, treetwisty)
 CSS_KEY(treetwistyopen, treetwistyopen)
 CSS_KEY(treeline, treeline)
 CSS_KEY(treeheader, treeheader)
 CSS_KEY(treeheadercell, treeheadercell)
@@ -737,12 +745,13 @@ CSS_KEY(non-scaling-stroke, non_scaling_
 CSS_KEY(nonzero, nonzero)
 CSS_KEY(optimizelegibility, optimizelegibility)
 CSS_KEY(optimizequality, optimizequality)
 CSS_KEY(optimizespeed, optimizespeed)
 CSS_KEY(reset-size, reset_size)
 //CSS_KEY(square, square)
 //CSS_KEY(start, start)
 CSS_KEY(srgb, srgb)
+CSS_KEY(symbolic, symbolic)
 CSS_KEY(text-after-edge, text_after_edge)
 CSS_KEY(text-before-edge, text_before_edge)
 CSS_KEY(use-script, use_script)
 CSS_KEY(-moz-crisp-edges, _moz_crisp_edges)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -192,16 +192,28 @@ public:
                                    nsIURI* aBaseURL,
                                    nsIPrincipal* aDocPrincipal);
 
   bool EvaluateSupportsCondition(const nsAString& aCondition,
                                  nsIURI* aDocURL,
                                  nsIURI* aBaseURL,
                                  nsIPrincipal* aDocPrincipal);
 
+  bool ParseCounterStyleName(const nsAString& aBuffer,
+                             nsIURI* aURL,
+                             nsAString& aName);
+
+  bool ParseCounterDescriptor(nsCSSCounterDesc aDescID,
+                              const nsAString& aBuffer,
+                              nsIURI* aSheetURL,
+                              nsIURI* aBaseURL,
+                              nsIPrincipal* aSheetPrincipal,
+                              nsCSSValue& aValue);
+
+
   typedef nsCSSParser::VariableEnumFunc VariableEnumFunc;
 
   /**
    * Parses a CSS token stream value and invokes a callback function for each
    * variable reference that is encountered.
    *
    * @param aPropertyValue The CSS token stream value.
    * @param aFunc The callback function to invoke; its parameters are the
@@ -465,16 +477,23 @@ protected:
   bool ParseSupportsConditionInParens(bool& aConditionMet);
   bool ParseSupportsConditionInParensInsideParens(bool& aConditionMet);
   bool ParseSupportsConditionTerms(bool& aConditionMet);
   enum SupportsConditionTermOperator { eAnd, eOr };
   bool ParseSupportsConditionTermsAfterOperator(
                                        bool& aConditionMet,
                                        SupportsConditionTermOperator aOperator);
 
+  bool ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aProcessData);
+  bool ParseCounterStyleName(nsAString& aName, bool aForDefinition);
+  bool ParseCounterDescriptor(nsCSSCounterStyleRule *aRule);
+  bool ParseCounterDescriptorValue(nsCSSCounterDesc aDescID,
+                                   nsCSSValue& aValue);
+  bool ParseCounterRange(nsCSSValuePair& aPair);
+
   /**
    * Parses the current input stream for a CSS token stream value and resolves
    * any variable references using the variables in aVariables.
    *
    * @param aVariables The set of variable values to use when resolving variable
    *   references.
    * @param aResult Out parameter that, if the function returns true, will be
    *   replaced with the resolved value.
@@ -851,16 +870,20 @@ protected:
                     int32_t aVariantMask,
                     const KTableValue aKeywordTable[]);
   bool ParseNonNegativeVariant(nsCSSValue& aValue,
                                int32_t aVariantMask,
                                const KTableValue aKeywordTable[]);
   bool ParseOneOrLargerVariant(nsCSSValue& aValue,
                                int32_t aVariantMask,
                                const KTableValue aKeywordTable[]);
+  bool ParseNonNegativeInteger(nsCSSValue& aValue)
+  {
+    return ParseNonNegativeVariant(aValue, VARIANT_INTEGER, nullptr);
+  }
 
   // http://dev.w3.org/csswg/css-values/#custom-idents
   // Parse an identifier that is none of:
   // * a CSS-wide keyword
   // * "default"
   // * a keyword in |aExcludedKeywords|
   // * a keyword in |aPropertyKTable|
   //
@@ -2363,16 +2386,54 @@ CSSParserImpl::ParsePropertyWithVariable
 
   // Copy the property value into the rule data.
   mTempData.MapRuleInfoInto(aPropertyID, aRuleData);
 
   mTempData.ClearProperty(propertyToParse);
   mTempData.AssertInitialState();
 }
 
+bool
+CSSParserImpl::ParseCounterStyleName(const nsAString& aBuffer,
+                                     nsIURI* aURL,
+                                     nsAString& aName)
+{
+  nsCSSScanner scanner(aBuffer, 0);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURL);
+  InitScanner(scanner, reporter, aURL, aURL, nullptr);
+
+  bool success = ParseCounterStyleName(aName, true) && !GetToken(true);
+
+  OUTPUT_ERROR();
+  ReleaseScanner();
+
+  return success;
+}
+
+bool
+CSSParserImpl::ParseCounterDescriptor(nsCSSCounterDesc aDescID,
+                                      const nsAString& aBuffer,
+                                      nsIURI* aSheetURL,
+                                      nsIURI* aBaseURL,
+                                      nsIPrincipal* aSheetPrincipal,
+                                      nsCSSValue& aValue)
+{
+  nsCSSScanner scanner(aBuffer, 0);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURL);
+  InitScanner(scanner, reporter, aSheetURL, aBaseURL, aSheetPrincipal);
+
+  bool success = ParseCounterDescriptorValue(aDescID, aValue) &&
+                 !GetToken(true);
+
+  OUTPUT_ERROR();
+  ReleaseScanner();
+
+  return success;
+}
+
 //----------------------------------------------------------------------
 
 bool
 CSSParserImpl::GetToken(bool aSkipWS)
 {
   if (mHavePushBack) {
     mHavePushBack = false;
     if (!aSkipWS || mToken.mType != eCSSToken_Whitespace) {
@@ -2589,16 +2650,20 @@ CSSParserImpl::ParseAtRule(RuleAppendFun
              mToken.mIdent.LowerCaseEqualsLiteral("keyframes")) {
     parseFunc = &CSSParserImpl::ParseKeyframesRule;
     newSection = eCSSSection_General;
 
   } else if (mToken.mIdent.LowerCaseEqualsLiteral("supports")) {
     parseFunc = &CSSParserImpl::ParseSupportsRule;
     newSection = eCSSSection_General;
 
+  } else if (mToken.mIdent.LowerCaseEqualsLiteral("counter-style")) {
+    parseFunc = &CSSParserImpl::ParseCounterStyleRule;
+    newSection = eCSSSection_General;
+
   } else {
     if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
       REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
       OUTPUT_ERROR();
     }
     // Skip over unsupported at rule, don't advance section
     return SkipAtRule(aInAtRule);
   }
@@ -3955,16 +4020,358 @@ CSSParserImpl::ParseSupportsConditionTer
         !mToken.mIdent.LowerCaseEqualsASCII(token)) {
       UngetToken();
       return true;
     }
   }
 }
 
 bool
+CSSParserImpl::ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aData)
+{
+  nsAutoString name;
+  if (!ParseCounterStyleName(name, true)) {
+    REPORT_UNEXPECTED_TOKEN(PECounterStyleNotIdent);
+    return false;
+  }
+
+  if (!ExpectSymbol('{', true)) {
+    REPORT_UNEXPECTED_TOKEN(PECounterStyleBadBlockStart);
+    return false;
+  }
+
+  nsRefPtr<nsCSSCounterStyleRule> rule = new nsCSSCounterStyleRule(name);
+  for (;;) {
+    if (!GetToken(true)) {
+      REPORT_UNEXPECTED_EOF(PECounterStyleEOF);
+      break;
+    }
+    if (mToken.IsSymbol('}')) {
+      break;
+    }
+    if (mToken.IsSymbol(';')) {
+      continue;
+    }
+
+    if (!ParseCounterDescriptor(rule)) {
+      REPORT_UNEXPECTED(PEDeclSkipped);
+      OUTPUT_ERROR();
+      if (!SkipDeclaration(true)) {
+        REPORT_UNEXPECTED_EOF(PECounterStyleEOF);
+        break;
+      }
+    }
+  }
+
+  int32_t system = rule->GetSystem();
+  bool isCorrect = false;
+  switch (system) {
+    case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
+    case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
+    case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
+    case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC:
+    case NS_STYLE_COUNTER_SYSTEM_FIXED: {
+      // check whether symbols is set and the length is sufficient
+      const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols);
+      if (symbols.GetUnit() == eCSSUnit_List &&
+          nsCSSCounterStyleRule::CheckDescValue(
+              system, eCSSCounterDesc_Symbols, symbols)) {
+        isCorrect = true;
+      }
+      break;
+    }
+    case NS_STYLE_COUNTER_SYSTEM_ADDITIVE: {
+      // for additive system, additive-symbols must be set
+      const nsCSSValue& symbols =
+        rule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
+      if (symbols.GetUnit() == eCSSUnit_PairList) {
+        isCorrect = true;
+      }
+      break;
+    }
+    case NS_STYLE_COUNTER_SYSTEM_EXTENDS: {
+      // for extends system, symbols & additive-symbols must not be set
+      const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols);
+      const nsCSSValue& additiveSymbols =
+        rule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
+      if (symbols.GetUnit() == eCSSUnit_Null &&
+          additiveSymbols.GetUnit() == eCSSUnit_Null) {
+        isCorrect = true;
+      }
+      break;
+    }
+    default:
+      NS_NOTREACHED("unknown system");
+  }
+
+  if (isCorrect) {
+    (*aAppendFunc)(rule, aData);
+  }
+  return true;
+}
+
+bool
+CSSParserImpl::ParseCounterStyleName(nsAString& aName, bool aForDefinition)
+{
+  if (!GetToken(true)) {
+    return false;
+  }
+
+  if (mToken.mType != eCSSToken_Ident) {
+    UngetToken();
+    return false;
+  }
+
+  static const nsCSSKeyword kReservedNames[] = {
+    eCSSKeyword_none,
+    eCSSKeyword_decimal,
+    eCSSKeyword_UNKNOWN
+  };
+
+  nsCSSValue value; // we don't actually care about the value
+  if (!ParseCustomIdent(value, mToken.mIdent,
+                        aForDefinition ? kReservedNames : nullptr)) {
+    REPORT_UNEXPECTED_TOKEN(PECounterStyleBadName);
+    UngetToken();
+    return false;
+  }
+
+  aName = mToken.mIdent;
+  if (nsCSSProps::IsPredefinedCounterStyle(aName)) {
+    ToLowerCase(aName);
+  }
+  return true;
+}
+
+bool
+CSSParserImpl::ParseCounterDescriptor(nsCSSCounterStyleRule* aRule)
+{
+  if (eCSSToken_Ident != mToken.mType) {
+    REPORT_UNEXPECTED_TOKEN(PECounterDescExpected);
+    return false;
+  }
+
+  nsString descName = mToken.mIdent;
+  if (!ExpectSymbol(':', true)) {
+    REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
+    OUTPUT_ERROR();
+    return false;
+  }
+
+  nsCSSCounterDesc descID = nsCSSProps::LookupCounterDesc(descName);
+  nsCSSValue value;
+
+  if (descID == eCSSCounterDesc_UNKNOWN) {
+    REPORT_UNEXPECTED_P(PEUnknownCounterDesc, descName);
+    return false;
+  }
+
+  if (!ParseCounterDescriptorValue(descID, value)) {
+    REPORT_UNEXPECTED_P(PEValueParsingError, descName);
+    return false;
+  }
+
+  if (!ExpectEndProperty()) {
+    return false;
+  }
+
+  aRule->SetDesc(descID, value);
+  return true;
+}
+
+bool
+CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID,
+                                           nsCSSValue& aValue)
+{
+  // Should also include VARIANT_IMAGE, but it is not supported currently.
+  // See bug 1024179.
+  static const int32_t VARIANT_COUNTER_SYMBOL =
+    VARIANT_STRING | VARIANT_IDENTIFIER;
+
+  switch (aDescID) {
+    case eCSSCounterDesc_System: {
+      nsCSSValue system;
+      if (!ParseEnum(system, nsCSSProps::kCounterSystemKTable)) {
+        return false;
+      }
+      switch (system.GetIntValue()) {
+        case NS_STYLE_COUNTER_SYSTEM_FIXED: {
+          nsCSSValue start;
+          if (!ParseVariant(start, VARIANT_INTEGER, nullptr)) {
+            start.SetIntValue(1, eCSSUnit_Integer);
+          }
+          aValue.SetPairValue(system, start);
+          return true;
+        }
+        case NS_STYLE_COUNTER_SYSTEM_EXTENDS: {
+          nsString name;
+          if (!ParseCounterStyleName(name, false)) {
+            REPORT_UNEXPECTED_TOKEN(PECounterExtendsNotIdent);
+            return false;
+          }
+          aValue.SetPairValue(system, nsCSSValue(name, eCSSUnit_Ident));
+          return true;
+        }
+        default:
+          aValue = system;
+          return true;
+      }
+    }
+
+    case eCSSCounterDesc_Negative: {
+      nsCSSValue first, second;
+      if (!ParseVariant(first, VARIANT_COUNTER_SYMBOL, nullptr)) {
+        return false;
+      }
+      if (!ParseVariant(second, VARIANT_COUNTER_SYMBOL, nullptr)) {
+        aValue = first;
+      } else {
+        aValue.SetPairValue(first, second);
+      }
+      return true;
+    }
+
+    case eCSSCounterDesc_Prefix:
+    case eCSSCounterDesc_Suffix:
+      return ParseVariant(aValue, VARIANT_COUNTER_SYMBOL, nullptr);
+
+    case eCSSCounterDesc_Range: {
+      if (ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
+        return true;
+      }
+      nsCSSValuePairList* item = aValue.SetPairListValue();
+      for (;;) {
+        nsCSSValuePair pair;
+        if (!ParseCounterRange(pair)) {
+          return false;
+        }
+        item->mXValue = pair.mXValue;
+        item->mYValue = pair.mYValue;
+        if (!ExpectSymbol(',', true)) {
+          return true;
+        }
+        item->mNext = new nsCSSValuePairList;
+        item = item->mNext;
+      }
+      // should always return in the loop
+    }
+
+    case eCSSCounterDesc_Pad: {
+      nsCSSValue width, symbol;
+      bool hasWidth = ParseNonNegativeInteger(width);
+      if (!ParseVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) ||
+          (!hasWidth && !ParseNonNegativeInteger(width))) {
+        return false;
+      }
+      aValue.SetPairValue(width, symbol);
+      return true;
+    }
+
+    case eCSSCounterDesc_Fallback: {
+      nsString name;
+      if (!ParseCounterStyleName(name, false)) {
+        return false;
+      }
+      aValue.SetStringValue(name, eCSSUnit_Ident);
+      return true;
+    }
+
+    case eCSSCounterDesc_Symbols: {
+      nsCSSValueList* item = nullptr;
+      for (;;) {
+        nsCSSValue value;
+        if (!ParseVariant(value, VARIANT_COUNTER_SYMBOL, nullptr)) {
+          return !!item;
+        }
+        if (!item) {
+          item = aValue.SetListValue();
+        } else {
+          item->mNext = new nsCSSValueList;
+          item = item->mNext;
+        }
+        item->mValue = value;
+      }
+      // should always return in the loop
+    }
+
+    case eCSSCounterDesc_AdditiveSymbols: {
+      nsCSSValuePairList* item = nullptr;
+      int32_t lastWeight = -1;
+      for (;;) {
+        nsCSSValue weight, symbol;
+        bool hasWeight = ParseNonNegativeInteger(weight);
+        if (!ParseVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) ||
+            (!hasWeight && !ParseNonNegativeInteger(weight))) {
+          return false;
+        }
+        if (lastWeight != -1 && weight.GetIntValue() >= lastWeight) {
+          REPORT_UNEXPECTED(PECounterASWeight);
+          return false;
+        }
+        lastWeight = weight.GetIntValue();
+        if (!item) {
+          item = aValue.SetPairListValue();
+        } else {
+          item->mNext = new nsCSSValuePairList;
+          item = item->mNext;
+        }
+        item->mXValue = weight;
+        item->mYValue = symbol;
+        if (!ExpectSymbol(',', true)) {
+          return true;
+        }
+      }
+      // should always return in the loop
+    }
+
+    case eCSSCounterDesc_SpeakAs: {
+      if (ParseVariant(aValue, VARIANT_AUTO | VARIANT_KEYWORD,
+                      nsCSSProps::kCounterSpeakAsKTable)) {
+        if (aValue.GetUnit() == eCSSUnit_Enumerated &&
+            aValue.GetIntValue() == NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT) {
+          // Currently spell-out is not supported, so it is explicitly
+          // rejected here rather than parsed as a custom identifier.
+          // See bug 1024178.
+          return false;
+        }
+        return true;
+      }
+      nsString name;
+      if (ParseCounterStyleName(name, false)) {
+        aValue.SetStringValue(name, eCSSUnit_Ident);
+        return true;
+      }
+      return false;
+    }
+
+    default:
+      NS_NOTREACHED("unknown descriptor");
+      return false;
+  }
+}
+
+bool
+CSSParserImpl::ParseCounterRange(nsCSSValuePair& aPair)
+{
+  static const int32_t VARIANT_BOUND = VARIANT_INTEGER | VARIANT_KEYWORD;
+  nsCSSValue lower, upper;
+  if (!ParseVariant(lower, VARIANT_BOUND, nsCSSProps::kCounterRangeKTable) ||
+      !ParseVariant(upper, VARIANT_BOUND, nsCSSProps::kCounterRangeKTable)) {
+    return false;
+  }
+  if (lower.GetUnit() != eCSSUnit_Enumerated &&
+      upper.GetUnit() != eCSSUnit_Enumerated &&
+      lower.GetIntValue() > upper.GetIntValue()) {
+    return false;
+  }
+  aPair = nsCSSValuePair(lower, upper);
+  return true;
+}
+
+bool
 CSSParserImpl::SkipUntil(char16_t aStopSymbol)
 {
   nsCSSToken* tk = &mToken;
   nsAutoTArray<char16_t, 16> stack;
   stack.AppendElement(aStopSymbol);
   for (;;) {
     if (!GetToken(true)) {
       return false;
@@ -14354,8 +14761,30 @@ nsCSSParser::ParsePropertyWithVariableRe
                                             uint32_t aLineOffset)
 {
   static_cast<CSSParserImpl*>(mImpl)->
     ParsePropertyWithVariableReferences(aPropertyID, aShorthandPropertyID,
                                         aValue, aVariables, aRuleData, aDocURL,
                                         aBaseURL, aDocPrincipal, aSheet,
                                         aLineNumber, aLineOffset);
 }
+
+bool
+nsCSSParser::ParseCounterStyleName(const nsAString& aBuffer,
+                                   nsIURI* aURL,
+                                   nsAString& aName)
+{
+  return static_cast<CSSParserImpl*>(mImpl)->
+    ParseCounterStyleName(aBuffer, aURL, aName);
+}
+
+bool
+nsCSSParser::ParseCounterDescriptor(nsCSSCounterDesc aDescID,
+                                    const nsAString& aBuffer,
+                                    nsIURI* aSheetURL,
+                                    nsIURI* aBaseURL,
+                                    nsIPrincipal* aSheetPrincipal,
+                                    nsCSSValue& aValue)
+{
+  return static_cast<CSSParserImpl*>(mImpl)->
+    ParseCounterDescriptor(aDescID, aBuffer,
+                           aSheetURL, aBaseURL, aSheetPrincipal, aValue);
+}
--- a/layout/style/nsCSSParser.h
+++ b/layout/style/nsCSSParser.h
@@ -259,16 +259,27 @@ public:
                                    nsRuleData* aRuleData,
                                    nsIURI* aDocURL,
                                    nsIURI* aBaseURL,
                                    nsIPrincipal* aDocPrincipal,
                                    nsCSSStyleSheet* aSheet,
                                    uint32_t aLineNumber,
                                    uint32_t aLineOffset);
 
+  bool ParseCounterStyleName(const nsAString& aBuffer,
+                             nsIURI* aURL,
+                             nsAString& aName);
+
+  bool ParseCounterDescriptor(nsCSSCounterDesc aDescID,
+                              const nsAString& aBuffer,
+                              nsIURI* aSheetURL,
+                              nsIURI* aBaseURL,
+                              nsIPrincipal* aSheetPrincipal,
+                              nsCSSValue& aValue);
+
 protected:
   // This is a CSSParserImpl*, but if we expose that type name in this
   // header, we can't put the type definition (in nsCSSParser.cpp) in
   // the anonymous namespace.
   void* mImpl;
 };
 
 #endif /* nsCSSParser_h___ */
--- a/layout/style/nsCSSProperty.h
+++ b/layout/style/nsCSSProperty.h
@@ -67,9 +67,19 @@ enum nsCSSProperty {
 enum nsCSSFontDesc {
   eCSSFontDesc_UNKNOWN = -1,
 #define CSS_FONT_DESC(name_, method_) eCSSFontDesc_##method_,
 #include "nsCSSFontDescList.h"
 #undef CSS_FONT_DESC
   eCSSFontDesc_COUNT
 };
 
+// The "descriptors" that can appear in a @counter-style rule.
+// They have the syntax of properties but different value rules.
+enum nsCSSCounterDesc {
+  eCSSCounterDesc_UNKNOWN = -1,
+#define CSS_COUNTER_DESC(name_, method_) eCSSCounterDesc_##method_,
+#include "nsCSSCounterDescList.h"
+#undef CSS_COUNTER_DESC
+  eCSSCounterDesc_COUNT
+};
+
 #endif /* nsCSSProperty_h___ */
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -46,27 +46,48 @@ const char* const kCSSRawProperties[eCSS
 #undef CSS_PROP_ALIAS
 };
 
 using namespace mozilla;
 
 static int32_t gPropertyTableRefCount;
 static nsStaticCaseInsensitiveNameTable* gPropertyTable;
 static nsStaticCaseInsensitiveNameTable* gFontDescTable;
+static nsStaticCaseInsensitiveNameTable* gCounterDescTable;
+static nsStaticCaseInsensitiveNameTable* gPredefinedCounterStyleTable;
 
 /* static */ nsCSSProperty *
   nsCSSProps::gShorthandsContainingTable[eCSSProperty_COUNT_no_shorthands];
 /* static */ nsCSSProperty* nsCSSProps::gShorthandsContainingPool = nullptr;
 
 static const char* const kCSSRawFontDescs[] = {
 #define CSS_FONT_DESC(name_, method_) #name_,
 #include "nsCSSFontDescList.h"
 #undef CSS_FONT_DESC
 };
 
+static const char* const kCSSRawCounterDescs[] = {
+#define CSS_COUNTER_DESC(name_, method_) #name_,
+#include "nsCSSCounterDescList.h"
+#undef CSS_COUNTER_DESC
+};
+
+static const char* const kCSSRawPredefinedCounterStyles[] = {
+  "none", "decimal", "decimal-leading-zero", "cjk-decimal",
+  "lower-roman", "upper-roman", "armenian", "georgian", "hebrew",
+  "lower-alpha", "lower-latin", "upper-alpha", "upper-latin",
+  "lower-greek", "hiragana", "hiragana-iroha", "katakana", "katakana-iroha",
+  "disc", "circle", "square", "disclosure-open", "disclosure-closed",
+  "japanese-informal", "japanese-formal",
+  "korean-hangul-formal", "korean-hanja-informal", "korean-hanja-formal",
+  "simp-chinese-informal", "simp-chinese-formal",
+  "trad-chinese-informal", "trad-chinese-formal", "cjk-ideographic",
+  "ethiopic-numeric"
+};
+
 struct PropertyAndCount {
   nsCSSProperty property;
   uint32_t count;
 };
 
 static int
 SortPropertyAndCount(const void* s1, const void* s2, void *closure)
 {
@@ -116,20 +137,27 @@ CreateStaticTable(const char* const aRaw
 }
 
 void
 nsCSSProps::AddRefTable(void)
 {
   if (0 == gPropertyTableRefCount++) {
     NS_ABORT_IF_FALSE(!gPropertyTable, "pre existing array!");
     NS_ABORT_IF_FALSE(!gFontDescTable, "pre existing array!");
+    NS_ABORT_IF_FALSE(!gCounterDescTable, "pre existing array!");
+    NS_ABORT_IF_FALSE(!gPredefinedCounterStyleTable, "pre existing array!");
 
     gPropertyTable = CreateStaticTable(
         kCSSRawProperties, eCSSProperty_COUNT_with_aliases);
     gFontDescTable = CreateStaticTable(kCSSRawFontDescs, eCSSFontDesc_COUNT);
+    gCounterDescTable = CreateStaticTable(
+        kCSSRawCounterDescs, eCSSCounterDesc_COUNT);
+    gPredefinedCounterStyleTable = CreateStaticTable(
+        kCSSRawPredefinedCounterStyles,
+        ArrayLength(kCSSRawPredefinedCounterStyles));
 
     BuildShorthandsContainingTable();
 
     static bool prefObserversInited = false;
     if (!prefObserversInited) {
       prefObserversInited = true;
       
       #define OBSERVE_PROP(pref_, id_)                                        \
@@ -328,16 +356,22 @@ nsCSSProps::ReleaseTable(void)
 {
   if (0 == --gPropertyTableRefCount) {
     delete gPropertyTable;
     gPropertyTable = nullptr;
 
     delete gFontDescTable;
     gFontDescTable = nullptr;
 
+    delete gCounterDescTable;
+    gCounterDescTable = nullptr;
+
+    delete gPredefinedCounterStyleTable;
+    gPredefinedCounterStyleTable = nullptr;
+
     delete [] gShorthandsContainingPool;
     gShorthandsContainingPool = nullptr;
   }
 }
 
 /* static */ bool
 nsCSSProps::IsInherited(nsCSSProperty aProperty)
 {
@@ -451,16 +485,48 @@ nsCSSProps::LookupFontDesc(const nsAStri
     nsAutoString prefixedProp;
     prefixedProp.AppendLiteral("-moz-");
     prefixedProp.Append(aFontDesc);
     which = nsCSSFontDesc(gFontDescTable->Lookup(prefixedProp));
   }
   return which;
 }
 
+nsCSSCounterDesc
+nsCSSProps::LookupCounterDesc(const nsAString& aProperty)
+{
+  NS_ABORT_IF_FALSE(gCounterDescTable, "no lookup table, needs addref");
+  return nsCSSCounterDesc(gCounterDescTable->Lookup(aProperty));
+}
+
+nsCSSCounterDesc
+nsCSSProps::LookupCounterDesc(const nsACString& aProperty)
+{
+  NS_ABORT_IF_FALSE(gCounterDescTable, "no lookup table, needs addref");
+  return nsCSSCounterDesc(gCounterDescTable->Lookup(aProperty));
+}
+
+bool
+nsCSSProps::IsPredefinedCounterStyle(const nsAString& aStyle)
+{
+  NS_ABORT_IF_FALSE(gPredefinedCounterStyleTable,
+                    "no lookup table, needs addref");
+  return gPredefinedCounterStyleTable->Lookup(aStyle) !=
+    nsStaticCaseInsensitiveNameTable::NOT_FOUND;
+}
+
+bool
+nsCSSProps::IsPredefinedCounterStyle(const nsACString& aStyle)
+{
+  NS_ABORT_IF_FALSE(gPredefinedCounterStyleTable,
+                    "no lookup table, needs addref");
+  return gPredefinedCounterStyleTable->Lookup(aStyle) !=
+    nsStaticCaseInsensitiveNameTable::NOT_FOUND;
+}
+
 const nsAFlatCString&
 nsCSSProps::GetStringValue(nsCSSProperty aProperty)
 {
   NS_ABORT_IF_FALSE(gPropertyTable, "no lookup table, needs addref");
   if (gPropertyTable) {
     return gPropertyTable->GetStringValue(int32_t(aProperty));
   } else {
     static nsDependentCString sNullStr("");
@@ -475,16 +541,28 @@ nsCSSProps::GetStringValue(nsCSSFontDesc
   if (gFontDescTable) {
     return gFontDescTable->GetStringValue(int32_t(aFontDescID));
   } else {
     static nsDependentCString sNullStr("");
     return sNullStr;
   }
 }
 
+const nsAFlatCString&
+nsCSSProps::GetStringValue(nsCSSCounterDesc aCounterDesc)
+{
+  NS_ABORT_IF_FALSE(gCounterDescTable, "no lookup table, needs addref");
+  if (gCounterDescTable) {
+    return gCounterDescTable->GetStringValue(int32_t(aCounterDesc));
+  } else {
+    static nsDependentCString sNullStr("");
+    return sNullStr;
+  }
+}
+
 nsCSSProperty
 nsCSSProps::OtherNameFor(nsCSSProperty aProperty)
 {
   switch (aProperty) {
     case eCSSProperty_border_left_color_value:
       return eCSSProperty_border_left_color;
     case eCSSProperty_border_left_style_value:
       return eCSSProperty_border_left_style;
@@ -1905,16 +1983,40 @@ const KTableValue nsCSSProps::kColorInte
 };
 
 const KTableValue nsCSSProps::kColumnFillKTable[] = {
   eCSSKeyword_auto, NS_STYLE_COLUMN_FILL_AUTO,
   eCSSKeyword_balance, NS_STYLE_COLUMN_FILL_BALANCE,
   eCSSKeyword_UNKNOWN, -1
 };
 
+const KTableValue nsCSSProps::kCounterSystemKTable[] = {
+  eCSSKeyword_cyclic, NS_STYLE_COUNTER_SYSTEM_CYCLIC,
+  eCSSKeyword_numeric, NS_STYLE_COUNTER_SYSTEM_NUMERIC,
+  eCSSKeyword_alphabetic, NS_STYLE_COUNTER_SYSTEM_ALPHABETIC,
+  eCSSKeyword_symbolic, NS_STYLE_COUNTER_SYSTEM_SYMBOLIC,
+  eCSSKeyword_additive, NS_STYLE_COUNTER_SYSTEM_ADDITIVE,
+  eCSSKeyword_fixed, NS_STYLE_COUNTER_SYSTEM_FIXED,
+  eCSSKeyword_extends, NS_STYLE_COUNTER_SYSTEM_EXTENDS,
+  eCSSKeyword_UNKNOWN, -1
+};
+
+const KTableValue nsCSSProps::kCounterRangeKTable[] = {
+  eCSSKeyword_infinite, NS_STYLE_COUNTER_RANGE_INFINITE,
+  eCSSKeyword_UNKNOWN, -1
+};
+
+const KTableValue nsCSSProps::kCounterSpeakAsKTable[] = {
+  eCSSKeyword_bullets, NS_STYLE_COUNTER_SPEAKAS_BULLETS,
+  eCSSKeyword_numbers, NS_STYLE_COUNTER_SPEAKAS_NUMBERS,
+  eCSSKeyword_words, NS_STYLE_COUNTER_SPEAKAS_WORDS,
+  eCSSKeyword_spell_out, NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT,
+  eCSSKeyword_UNKNOWN, -1
+};
+
 static bool IsKeyValSentinel(nsCSSKeyword aKey, KTableValue aValue)
 {
   return aKey == eCSSKeyword_UNKNOWN && aValue == -1;
 }
 
 int32_t
 nsCSSProps::FindIndexOfKeyword(nsCSSKeyword aKeyword,
                                const KTableValue aTable[])
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -305,19 +305,28 @@ public:
 
   // Must be given a longhand property.
   static bool IsInherited(nsCSSProperty aProperty);
 
   // Same but for @font-face descriptors
   static nsCSSFontDesc LookupFontDesc(const nsAString& aProperty);
   static nsCSSFontDesc LookupFontDesc(const nsACString& aProperty);
 
+  // For @counter-style descriptors
+  static nsCSSCounterDesc LookupCounterDesc(const nsAString& aProperty);
+  static nsCSSCounterDesc LookupCounterDesc(const nsACString& aProperty);
+
+  // For predefined counter styles which need to be lower-cased during parse
+  static bool IsPredefinedCounterStyle(const nsAString& aStyle);
+  static bool IsPredefinedCounterStyle(const nsACString& aStyle);
+
   // Given a property enum, get the string value
   static const nsAFlatCString& GetStringValue(nsCSSProperty aProperty);
   static const nsAFlatCString& GetStringValue(nsCSSFontDesc aFontDesc);
+  static const nsAFlatCString& GetStringValue(nsCSSCounterDesc aCounterDesc);
 
   // Get the property to report the computed value of aProperty as being
   // the computed value of.  aProperty must have the
   // CSS_PROPERTY_REPORT_OTHER_NAME bit set.
   static nsCSSProperty OtherNameFor(nsCSSProperty aProperty);
 
   // Given a CSS Property and a Property Enum Value
   // Return back a const nsString& representation of the
@@ -636,16 +645,19 @@ public:
   static const KTableValue kVolumeKTable[];
   static const KTableValue kWhitespaceKTable[];
   static const KTableValue kWidthKTable[]; // also min-width, max-width
   static const KTableValue kWindowShadowKTable[];
   static const KTableValue kWordBreakKTable[];
   static const KTableValue kWordWrapKTable[];
   static const KTableValue kWritingModeKTable[];
   static const KTableValue kHyphensKTable[];
+  static const KTableValue kCounterSystemKTable[];
+  static const KTableValue kCounterRangeKTable[];
+  static const KTableValue kCounterSpeakAsKTable[];
 };
 
 inline nsCSSProps::EnabledState operator|(nsCSSProps::EnabledState a,
                                           nsCSSProps::EnabledState b)
 {
   return nsCSSProps::EnabledState(int(a) | int(b));
 }
 
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -923,16 +923,17 @@ static const RuleHashTableOps AtomSelect
 //--------------------------------
 
 struct RuleCascadeData {
   RuleCascadeData(nsIAtom *aMedium, bool aQuirksMode)
     : mRuleHash(aQuirksMode),
       mStateSelectors(),
       mSelectorDocumentStates(0),
       mKeyframesRuleTable(16),
+      mCounterStyleRuleTable(16),
       mCacheKey(aMedium),
       mNext(nullptr),
       mQuirksMode(aQuirksMode)
   {
     // mAttributeSelectors is matching on the attribute _name_, not the value,
     // and we case-fold names at parse-time, so this is a case-sensitive match.
     PL_DHashTableInit(&mAttributeSelectors, &AtomSelector_CSOps, nullptr,
                       sizeof(AtomSelectorEntry), 16);
@@ -983,18 +984,20 @@ struct RuleCascadeData {
 #ifdef MOZ_XUL
   PLDHashTable             mXULTreeRules;
 #endif
 
   nsTArray<nsFontFaceRuleContainer> mFontFaceRules;
   nsTArray<nsCSSKeyframesRule*> mKeyframesRules;
   nsTArray<nsCSSFontFeatureValuesRule*> mFontFeatureValuesRules;
   nsTArray<nsCSSPageRule*> mPageRules;
+  nsTArray<nsCSSCounterStyleRule*> mCounterStyleRules;
 
   nsDataHashtable<nsStringHashKey, nsCSSKeyframesRule*> mKeyframesRuleTable;
+  nsDataHashtable<nsStringHashKey, nsCSSCounterStyleRule*> mCounterStyleRuleTable;
 
   // Looks up or creates the appropriate list in |mAttributeSelectors|.
   // Returns null only on allocation failure.
   nsTArray<nsCSSSelector*>* AttributeListFor(nsIAtom* aAttribute);
 
   nsMediaQueryResultCacheKey mCacheKey;
   RuleCascadeData*  mNext; // for a different medium
 
@@ -1053,16 +1056,17 @@ RuleCascadeData::SizeOfIncludingThis(Mal
   n += PL_DHashTableSizeOfExcludingThis(&mXULTreeRules,
                                         SizeOfRuleHashTableEntry, aMallocSizeOf);
 #endif
 
   n += mFontFaceRules.SizeOfExcludingThis(aMallocSizeOf);
   n += mKeyframesRules.SizeOfExcludingThis(aMallocSizeOf);
   n += mFontFeatureValuesRules.SizeOfExcludingThis(aMallocSizeOf);
   n += mPageRules.SizeOfExcludingThis(aMallocSizeOf);
+  n += mCounterStyleRules.SizeOfExcludingThis(aMallocSizeOf);
   n += mKeyframesRuleTable.SizeOfExcludingThis(SizeOfKeyframesRuleEntryExcludingThis,
                                                aMallocSizeOf,
                                                nullptr);
 
   return n;
 }
 
 nsTArray<nsCSSSelector*>*
@@ -2879,16 +2883,29 @@ nsCSSRuleProcessor::KeyframesRuleForName
 
   if (cascade) {
     return cascade->mKeyframesRuleTable.Get(aName);
   }
 
   return nullptr;
 }
 
+nsCSSCounterStyleRule*
+nsCSSRuleProcessor::CounterStyleRuleForName(nsPresContext* aPresContext,
+                                            const nsAString& aName)
+{
+  RuleCascadeData* cascade = GetRuleCascade(aPresContext);
+
+  if (cascade) {
+    return cascade->mCounterStyleRuleTable.Get(aName);
+  }
+
+  return nullptr;
+}
+
 // Append all the currently-active page rules to aArray.  Return
 // true for success and false for failure.
 bool
 nsCSSRuleProcessor::AppendPageRules(
                               nsPresContext* aPresContext,
                               nsTArray<nsCSSPageRule*>& aArray)
 {
   RuleCascadeData* cascade = GetRuleCascade(aPresContext);
@@ -3219,23 +3236,25 @@ static const PLDHashTableOps gRulesByWei
 };
 
 struct CascadeEnumData {
   CascadeEnumData(nsPresContext* aPresContext,
                   nsTArray<nsFontFaceRuleContainer>& aFontFaceRules,
                   nsTArray<nsCSSKeyframesRule*>& aKeyframesRules,
                   nsTArray<nsCSSFontFeatureValuesRule*>& aFontFeatureValuesRules,
                   nsTArray<nsCSSPageRule*>& aPageRules,
+                  nsTArray<nsCSSCounterStyleRule*>& aCounterStyleRules,
                   nsMediaQueryResultCacheKey& aKey,
                   uint8_t aSheetType)
     : mPresContext(aPresContext),
       mFontFaceRules(aFontFaceRules),
       mKeyframesRules(aKeyframesRules),
       mFontFeatureValuesRules(aFontFeatureValuesRules),
       mPageRules(aPageRules),
+      mCounterStyleRules(aCounterStyleRules),
       mCacheKey(aKey),
       mSheetType(aSheetType)
   {
     if (!PL_DHashTableInit(&mRulesByWeight, &gRulesByWeightOps, nullptr,
                            sizeof(RuleByWeightEntry), 64, fallible_t()))
       mRulesByWeight.ops = nullptr;
 
     // Initialize our arena
@@ -3250,16 +3269,17 @@ struct CascadeEnumData {
     PL_FinishArenaPool(&mArena);
   }
 
   nsPresContext* mPresContext;
   nsTArray<nsFontFaceRuleContainer>& mFontFaceRules;
   nsTArray<nsCSSKeyframesRule*>& mKeyframesRules;
   nsTArray<nsCSSFontFeatureValuesRule*>& mFontFeatureValuesRules;
   nsTArray<nsCSSPageRule*>& mPageRules;
+  nsTArray<nsCSSCounterStyleRule*>& mCounterStyleRules;
   nsMediaQueryResultCacheKey& mCacheKey;
   PLArenaPool mArena;
   // Hooray, a manual PLDHashTable since nsClassHashtable doesn't
   // provide a getter that gives me a *reference* to the value.
   PLDHashTable mRulesByWeight; // of PerWeightDataListItem linked lists
   uint8_t mSheetType;
 };
 
@@ -3269,16 +3289,17 @@ struct CascadeEnumData {
  *  (1) add any style rules, in order, into data->mRulesByWeight (for
  *      the primary CSS cascade), where they are separated by weight
  *      but kept in order per-weight, and
  *  (2) add any @font-face rules, in order, into data->mFontFaceRules.
  *  (3) add any @keyframes rules, in order, into data->mKeyframesRules.
  *  (4) add any @font-feature-value rules, in order,
  *      into data->mFontFeatureValuesRules.
  *  (5) add any @page rules, in order, into data->mPageRules.
+ *  (6) add any @counter-style rules, in order, into data->mCounterStyleRules.
  */
 static bool
 CascadeRuleEnumFunc(css::Rule* aRule, void* aData)
 {
   CascadeEnumData* data = (CascadeEnumData*)aData;
   int32_t type = aRule->GetType();
 
   if (css::Rule::STYLE_RULE == type) {
@@ -3334,16 +3355,23 @@ CascadeRuleEnumFunc(css::Rule* aRule, vo
     }
   }
   else if (css::Rule::PAGE_RULE == type) {
     nsCSSPageRule* pageRule = static_cast<nsCSSPageRule*>(aRule);
     if (!data->mPageRules.AppendElement(pageRule)) {
       return false;
     }
   }
+  else if (css::Rule::COUNTER_STYLE_RULE == type) {
+    nsCSSCounterStyleRule* counterStyleRule =
+      static_cast<nsCSSCounterStyleRule*>(aRule);
+    if (!data->mCounterStyleRules.AppendElement(counterStyleRule)) {
+      return false;
+    }
+  }
   return true;
 }
 
 /* static */ bool
 nsCSSRuleProcessor::CascadeSheet(nsCSSStyleSheet* aSheet, CascadeEnumData* aData)
 {
   if (aSheet->IsApplicable() &&
       aSheet->UseForPresentation(aData->mPresContext, aData->mCacheKey) &&
@@ -3438,16 +3466,17 @@ nsCSSRuleProcessor::RefreshRuleCascade(n
     nsAutoPtr<RuleCascadeData> newCascade(
       new RuleCascadeData(aPresContext->Medium(),
                           eCompatibility_NavQuirks == aPresContext->CompatibilityMode()));
     if (newCascade) {
       CascadeEnumData data(aPresContext, newCascade->mFontFaceRules,
                            newCascade->mKeyframesRules,
                            newCascade->mFontFeatureValuesRules,
                            newCascade->mPageRules,
+                           newCascade->mCounterStyleRules,
                            newCascade->mCacheKey,
                            mSheetType);
       if (!data.mRulesByWeight.ops)
         return; /* out of memory */
 
       for (uint32_t i = 0; i < mSheets.Length(); ++i) {
         if (!CascadeSheet(mSheets.ElementAt(i), &data))
           return; /* out of memory */
@@ -3476,16 +3505,23 @@ nsCSSRuleProcessor::RefreshRuleCascade(n
 
       // Build mKeyframesRuleTable.
       for (nsTArray<nsCSSKeyframesRule*>::size_type i = 0,
              iEnd = newCascade->mKeyframesRules.Length(); i < iEnd; ++i) {
         nsCSSKeyframesRule* rule = newCascade->mKeyframesRules[i];
         newCascade->mKeyframesRuleTable.Put(rule->GetName(), rule);
       }
 
+      // Build mCounterStyleRuleTable
+      for (nsTArray<nsCSSCounterStyleRule*>::size_type i = 0,
+           iEnd = newCascade->mCounterStyleRules.Length(); i < iEnd; ++i) {
+        nsCSSCounterStyleRule* rule = newCascade->mCounterStyleRules[i];
+        newCascade->mCounterStyleRuleTable.Put(rule->GetName(), rule);
+      }
+
       // Ensure that the current one is always mRuleCascades.
       newCascade->mNext = mRuleCascades;
       mRuleCascades = newCascade.forget();
     }
   }
   return;
 }
 
--- a/layout/style/nsCSSRuleProcessor.h
+++ b/layout/style/nsCSSRuleProcessor.h
@@ -24,16 +24,17 @@ struct CascadeEnumData;
 struct nsCSSSelector;
 struct nsCSSSelectorList;
 struct RuleCascadeData;
 struct TreeMatchContext;
 class nsCSSKeyframesRule;
 class nsCSSPageRule;
 class nsCSSFontFeatureValuesRule;
 class nsCSSStyleSheet;
+class nsCSSCounterStyleRule;
 
 /**
  * The CSS style rule processor provides a mechanism for sibling style
  * sheets to combine their rule processing in order to allow proper
  * cascading to happen.
  *
  * CSS style rule processors keep a live reference on all style sheets
  * bound to them.  The CSS style sheets keep a weak reference to all the
@@ -124,16 +125,19 @@ public:
   // Append all the currently-active font face rules to aArray.  Return
   // true for success and false for failure.
   bool AppendFontFaceRules(nsPresContext* aPresContext,
                            nsTArray<nsFontFaceRuleContainer>& aArray);
 
   nsCSSKeyframesRule* KeyframesRuleForName(nsPresContext* aPresContext,
                                            const nsString& aName);
 
+  nsCSSCounterStyleRule* CounterStyleRuleForName(nsPresContext* aPresContext,
+                                                 const nsAString& aName);
+
   bool AppendPageRules(nsPresContext* aPresContext,
                        nsTArray<nsCSSPageRule*>& aArray);
 
   bool AppendFontFeatureValuesRules(nsPresContext* aPresContext,
                               nsTArray<nsCSSFontFeatureValuesRule*>& aArray);
 
   /**
    * Returns the scope element for the scoped style sheets this rule
--- a/layout/style/nsCSSRules.cpp
+++ b/layout/style/nsCSSRules.cpp
@@ -3037,8 +3037,462 @@ CSSSupportsRule::SizeOfIncludingThis(Mal
   n += mCondition.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   return n;
 }
 
 } // namespace mozilla
 
 // Must be outside namespace
 DOMCI_DATA(CSSSupportsRule, mozilla::CSSSupportsRule)
+
+// -------------------------------------------
+// nsCSSCounterStyleRule
+//
+
+nsCSSCounterStyleRule::nsCSSCounterStyleRule(const nsCSSCounterStyleRule& aCopy)
+  : Rule(aCopy),
+    mName(aCopy.mName)
+{
+}
+
+nsCSSCounterStyleRule::~nsCSSCounterStyleRule()
+{
+}
+
+/* virtual */ already_AddRefed<css::Rule>
+nsCSSCounterStyleRule::Clone() const
+{
+  nsRefPtr<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
+};
+
+NS_IMPL_ADDREF(nsCSSCounterStyleRule)
+NS_IMPL_RELEASE(nsCSSCounterStyleRule)
+
+DOMCI_DATA(CSSCounterStyleRule, nsCSSCounterStyleRule)
+
+// QueryInterface implementation for nsCSSCounterStyleRule
+NS_INTERFACE_MAP_BEGIN(nsCSSCounterStyleRule)
+  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMCSSCounterStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSCounterStyleRule)
+NS_INTERFACE_MAP_END
+
+IMPL_STYLE_RULE_INHERIT(nsCSSCounterStyleRule, css::Rule)
+
+#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("  ");
+
+  fprintf(out, "%s@counter-style %s (rev.%u) {\n",
+          baseInd.get(), NS_ConvertUTF16toUTF8(mName).get(), mGeneration);
+  // TODO
+  fprintf(out, "%s}\n", baseInd.get());
+}
+#endif
+
+/* virtual */ int32_t
+nsCSSCounterStyleRule::GetType() const
+{
+  return Rule::COUNTER_STYLE_RULE;
+}
+
+// nsIDOMCSSRule methods
+NS_IMETHODIMP
+nsCSSCounterStyleRule::GetType(uint16_t* aType)
+{
+  *aType = nsIDOMCSSRule::COUNTER_STYLE_RULE;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCSSCounterStyleRule::GetCssText(nsAString& aCssText)
+{
+  aCssText.AssignLiteral(MOZ_UTF16("@counter-style "));
+  nsStyleUtil::AppendEscapedCSSIdent(mName, aCssText);
+  aCssText.AppendLiteral(MOZ_UTF16(" {\n"));
+  for (nsCSSCounterDesc id = nsCSSCounterDesc(0);
+       id < eCSSCounterDesc_COUNT;
+       id = nsCSSCounterDesc(id + 1)) {
+    if (mValues[id].GetUnit() != eCSSUnit_Null) {
+      nsAutoString tmp;
+      (this->*kGetters[id])(tmp);
+      aCssText.AppendLiteral(MOZ_UTF16("  "));
+      AppendASCIItoUTF16(nsCSSProps::GetStringValue(id), aCssText);
+      aCssText.AppendLiteral(MOZ_UTF16(": "));
+      aCssText.Append(tmp);
+      aCssText.AppendLiteral(MOZ_UTF16(";\n"));
+    }
+  }
+  aCssText.AppendLiteral(MOZ_UTF16("}"));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCSSCounterStyleRule::SetCssText(const nsAString& aCssText)
+{
+  // FIXME: implement???
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsCSSCounterStyleRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
+{
+  return Rule::GetParentStyleSheet(aSheet);
+}
+
+NS_IMETHODIMP
+nsCSSCounterStyleRule::GetParentRule(nsIDOMCSSRule** aParentRule)
+{
+  return Rule::GetParentRule(aParentRule);
+}
+
+// nsIDOMCSSCounterStyleRule methods
+NS_IMETHODIMP
+nsCSSCounterStyleRule::GetName(nsAString& aName)
+{
+  aName.Truncate();
+  nsStyleUtil::AppendEscapedCSSIdent(mName, aName);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCSSCounterStyleRule::SetName(const nsAString& aName)
+{
+  nsCSSParser parser;
+  nsAutoString name;
+  if (parser.ParseCounterStyleName(aName, nullptr, name)) {
+    nsIDocument* doc = GetDocument();
+    MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
+
+    mName = name;
+
+    nsCSSStyleSheet* sheet = GetStyleSheet();
+    if (sheet) {
+      sheet->SetModifiedByChildRule();
+      if (doc) {
+        doc->StyleRuleChanged(sheet, this, this);
+      }
+    }
+  }
+  return NS_OK;
+}
+
+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);
+  NS_ABORT_IF_FALSE(system.GetUnit() == eCSSUnit_Pair,
+                    "Invalid system value");
+  return system.GetPairValue().mYValue;
+}
+
+void
+nsCSSCounterStyleRule::SetDesc(nsCSSCounterDesc aDescID, const nsCSSValue& aValue)
+{
+  NS_ABORT_IF_FALSE(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++;
+
+  nsCSSStyleSheet* sheet = GetStyleSheet();
+  if (sheet) {
+    sheet->SetModifiedByChildRule();
+    if (doc) {
+      doc->StyleRuleChanged(sheet, this, this);
+    }
+  }
+}
+
+NS_IMETHODIMP
+nsCSSCounterStyleRule::GetSystem(nsAString& aSystem)
+{
+  const nsCSSValue& value = GetDesc(eCSSCounterDesc_System);
+  if (value.GetUnit() == eCSSUnit_Null) {
+    aSystem.Truncate();
+    return NS_OK;
+  }
+
+  aSystem = NS_ConvertASCIItoUTF16(nsCSSProps::ValueToKeyword(
+          GetSystem(), nsCSSProps::kCounterSystemKTable));
+  if (value.GetUnit() == eCSSUnit_Pair) {
+    aSystem.Append(' ');
+    GetSystemArgument().AppendToString(
+        eCSSProperty_UNKNOWN, aSystem, nsCSSValue::eNormalized);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+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,
+                                  nsCSSValue::eNormalized);
+      if (item->mNext) {
+        aSymbols.Append(' ');
+      }
+    }
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+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, nsCSSValue::eNormalized);
+      aSymbols.Append(' ');
+      item->mYValue.AppendToString(eCSSProperty_UNKNOWN,
+                                   aSymbols, nsCSSValue::eNormalized);
+      if (item->mNext) {
+        aSymbols.AppendLiteral(", ");
+      }
+    }
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCSSCounterStyleRule::GetRange(nsAString& aRange)
+{
+  const nsCSSValue& value = GetDesc(eCSSCounterDesc_Range);
+
+  switch (value.GetUnit()) {
+    case eCSSUnit_Auto:
+      aRange.AssignLiteral(MOZ_UTF16("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();
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+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(MOZ_UTF16("bullets"));
+          break;
+        case NS_STYLE_COUNTER_SPEAKAS_NUMBERS:
+          aSpeakAs.AssignLiteral(MOZ_UTF16("numbers"));
+          break;
+        case NS_STYLE_COUNTER_SPEAKAS_WORDS:
+          aSpeakAs.AssignLiteral(MOZ_UTF16("words"));
+          break;
+        case NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT:
+          aSpeakAs.AssignLiteral(MOZ_UTF16("spell-out"));
+          break;
+        default:
+          NS_NOTREACHED("Unknown speech synthesis");
+      }
+      break;
+
+    case eCSSUnit_Auto:
+    case eCSSUnit_Ident:
+      aSpeakAs.Truncate();
+      value.AppendToString(eCSSProperty_UNKNOWN,
+                           aSpeakAs, nsCSSValue::eNormalized);
+      break;
+
+    default:
+      aSpeakAs.Truncate();
+  }
+  return NS_OK;
+}
+
+nsresult
+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, nsCSSValue::eNormalized);
+  }
+  return NS_OK;
+}
+
+#define CSS_COUNTER_DESC_GETTER(name_)                    \
+NS_IMETHODIMP                                             \
+nsCSSCounterStyleRule::Get##name_(nsAString& a##name_)    \
+{                                                         \
+  return 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;
+  }
+}
+
+nsresult
+nsCSSCounterStyleRule::SetDescriptor(nsCSSCounterDesc aDescID,
+                                     const nsAString& aValue)
+{
+  nsCSSParser parser;
+  nsCSSValue value;
+  nsCSSStyleSheet* sheet = GetStyleSheet();
+  nsIURI* baseURL = nullptr;
+  nsIPrincipal* principal = nullptr;
+  if (sheet) {
+    baseURL = sheet->GetBaseURI();
+    principal = sheet->Principal();
+  }
+  if (parser.ParseCounterDescriptor(aDescID, aValue, nullptr,
+                                    baseURL, principal, value)) {
+    if (CheckDescValue(GetSystem(), aDescID, value)) {
+      SetDesc(aDescID, value);
+    }
+  }
+  return NS_OK;
+}
+
+#define CSS_COUNTER_DESC_SETTER(name_)                        \
+NS_IMETHODIMP                                                 \
+nsCSSCounterStyleRule::Set##name_(const nsAString& a##name_)  \
+{                                                             \
+  return 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);
+}
--- a/layout/style/nsCSSRules.h
+++ b/layout/style/nsCSSRules.h
@@ -10,16 +10,17 @@
 #define nsCSSRules_h_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/css/GroupRule.h"
 #include "nsIDOMCSSConditionRule.h"
+#include "nsIDOMCSSCounterStyleRule.h"
 #include "nsIDOMCSSFontFaceRule.h"
 #include "nsIDOMCSSFontFeatureValuesRule.h"
 #include "nsIDOMCSSGroupingRule.h"
 #include "nsIDOMCSSMediaRule.h"
 #include "nsIDOMCSSMozDocumentRule.h"
 #include "nsIDOMCSSSupportsRule.h"
 #include "nsIDOMMozCSSKeyframeRule.h"
 #include "nsIDOMMozCSSKeyframesRule.h"
@@ -636,9 +637,79 @@ public:
 
 protected:
   bool mUseGroup;
   nsString mCondition;
 };
 
 } // namespace mozilla
 
+class nsCSSCounterStyleRule MOZ_FINAL : public mozilla::css::Rule,
+                                        public nsIDOMCSSCounterStyleRule
+{
+public:
+  explicit nsCSSCounterStyleRule(const nsAString& aName)
+    : mName(aName),
+      mGeneration(0)
+  {
+  }
+
+private:
+  nsCSSCounterStyleRule(const nsCSSCounterStyleRule& aCopy);
+  ~nsCSSCounterStyleRule();
+
+public:
+  NS_DECL_ISUPPORTS
+
+  // nsIStyleRule methods
+#ifdef DEBUG
+  virtual void List(FILE* out = stdout, int32_t aIndent = 0) const MOZ_OVERRIDE;
+#endif
+
+  // Rule methods
+  DECL_STYLE_RULE_INHERIT
+  virtual int32_t GetType() const MOZ_OVERRIDE;
+  virtual already_AddRefed<mozilla::css::Rule> Clone() const;
+
+  // nsIDOMCSSRule interface
+  NS_DECL_NSIDOMCSSRULE
+
+  // nsIDOMCSSCounterStyleRule
+  NS_DECL_NSIDOMCSSCOUNTERSTYLERULE
+
+  // 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);
+
+  const nsString& GetName() const { return mName; }
+
+  uint32_t GetGeneration() const { return mGeneration; }
+
+  int32_t GetSystem() const;
+  const nsCSSValue& GetSystemArgument() const;
+
+  const nsCSSValue& GetDesc(nsCSSCounterDesc aDescID) const
+  {
+    NS_ABORT_IF_FALSE(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 MOZ_OVERRIDE;
+
+private:
+  typedef NS_STDCALL_FUNCPROTO(nsresult, Getter, nsCSSCounterStyleRule,
+                               GetSymbols, (nsAString&));
+  static const Getter kGetters[];
+
+  nsresult GetDescriptor(nsCSSCounterDesc aDescID, nsAString& aValue);
+  nsresult SetDescriptor(nsCSSCounterDesc aDescID, const nsAString& aValue);
+
+  nsString   mName;
+  nsCSSValue mValues[eCSSCounterDesc_COUNT];
+  uint32_t   mGeneration;
+};
+
 #endif /* !defined(nsCSSRules_h_) */
--- a/layout/style/nsCSSStyleSheet.cpp
+++ b/layout/style/nsCSSStyleSheet.cpp
@@ -2037,16 +2037,17 @@ nsCSSStyleSheet::InsertRuleIntoGroup(con
     return result;
 
   switch (rule->GetType()) {
     case css::Rule::STYLE_RULE:
     case css::Rule::MEDIA_RULE:
     case css::Rule::FONT_FACE_RULE:
     case css::Rule::PAGE_RULE:
     case css::Rule::KEYFRAMES_RULE:
+    case css::Rule::COUNTER_STYLE_RULE:
     case css::Rule::DOCUMENT_RULE:
     case css::Rule::SUPPORTS_RULE:
       // these types are OK to insert into a group
       break;
     case css::Rule::CHARSET_RULE:
     case css::Rule::IMPORT_RULE:
     case css::Rule::NAMESPACE_RULE:
       // these aren't
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -1052,16 +1052,32 @@ static inline mozilla::css::Side operato
 #define NS_STYLE_BLEND_SATURATION                   13
 #define NS_STYLE_BLEND_COLOR                        14
 #define NS_STYLE_BLEND_LUMINOSITY                   15
 
 // See nsStyleText::mControlCharacterVisibility
 #define NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN  0
 #define NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE 1
 
+// counter system
+#define NS_STYLE_COUNTER_SYSTEM_CYCLIC      0
+#define NS_STYLE_COUNTER_SYSTEM_NUMERIC     1
+#define NS_STYLE_COUNTER_SYSTEM_ALPHABETIC  2
+#define NS_STYLE_COUNTER_SYSTEM_SYMBOLIC    3
+#define NS_STYLE_COUNTER_SYSTEM_ADDITIVE    4
+#define NS_STYLE_COUNTER_SYSTEM_FIXED       5
+#define NS_STYLE_COUNTER_SYSTEM_EXTENDS     6
+
+#define NS_STYLE_COUNTER_RANGE_INFINITE     0
+
+#define NS_STYLE_COUNTER_SPEAKAS_BULLETS    0
+#define NS_STYLE_COUNTER_SPEAKAS_NUMBERS    1
+#define NS_STYLE_COUNTER_SPEAKAS_WORDS      2
+#define NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT  3
+
 /*****************************************************************************
  * Constants for media features.                                             *
  *****************************************************************************/
 
 // orientation
 #define NS_STYLE_ORIENTATION_PORTRAIT           0
 #define NS_STYLE_ORIENTATION_LANDSCAPE          1
 
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -1608,16 +1608,37 @@ nsStyleSet::KeyframesRuleForName(nsPresC
     nsCSSKeyframesRule* result =
       ruleProc->KeyframesRuleForName(aPresContext, aName);
     if (result)
       return result;
   }
   return nullptr;
 }
 
+nsCSSCounterStyleRule*
+nsStyleSet::CounterStyleRuleForName(nsPresContext* aPresContext,
+                                    const nsAString& aName)
+{
+  NS_ENSURE_FALSE(mInShutdown, nullptr);
+
+  for (uint32_t i = ArrayLength(gCSSSheetTypes); i-- != 0; ) {
+    if (gCSSSheetTypes[i] == eScopedDocSheet)
+      continue;
+    nsCSSRuleProcessor *ruleProc = static_cast<nsCSSRuleProcessor*>
+                                    (mRuleProcessors[gCSSSheetTypes[i]].get());
+    if (!ruleProc)
+      continue;
+    nsCSSCounterStyleRule *result =
+      ruleProc->CounterStyleRuleForName(aPresContext, aName);
+    if (result)
+      return result;
+  }
+  return nullptr;
+}
+
 bool
 nsStyleSet::AppendFontFeatureValuesRules(nsPresContext* aPresContext,
                                  nsTArray<nsCSSFontFeatureValuesRule*>& aArray)
 {
   NS_ENSURE_FALSE(mInShutdown, false);
 
   for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) {
     nsCSSRuleProcessor *ruleProc = static_cast<nsCSSRuleProcessor*>
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -25,16 +25,17 @@
 #include "nsIStyleRule.h"
 #include "nsCSSPseudoElements.h"
 
 class gfxFontFeatureValueSet;
 class nsCSSFontFaceRule;
 class nsCSSKeyframesRule;
 class nsCSSFontFeatureValuesRule;
 class nsCSSPageRule;
+class nsCSSCounterStyleRule;
 class nsRuleWalker;
 struct ElementDependentRuleProcessorData;
 struct TreeMatchContext;
 
 namespace mozilla {
 class EventStates;
 } // namespace mozilla
 
@@ -176,16 +177,20 @@ class nsStyleSet
   // true for success and false for failure.
   bool AppendFontFaceRules(nsPresContext* aPresContext,
                              nsTArray<nsFontFaceRuleContainer>& aArray);
 
   // Return the winning (in the cascade) @keyframes rule for the given name.
   nsCSSKeyframesRule* KeyframesRuleForName(nsPresContext* aPresContext,
                                            const nsString& aName);
 
+  // Return the winning (in the cascade) @counter-style rule for the given name.
+  nsCSSCounterStyleRule* CounterStyleRuleForName(nsPresContext* aPresContext,
+                                                 const nsAString& aName);
+
   // Fetch object for looking up font feature values
   already_AddRefed<gfxFontFeatureValueSet> GetFontFeatureValuesLookup();
 
   // Append all the currently-active font feature values rules to aArray.
   // Return true for success and false for failure.
   bool AppendFontFeatureValuesRules(nsPresContext* aPresContext,
                               nsTArray<nsCSSFontFeatureValuesRule*>& aArray);