Bug 966166 - Part 3: Link to other parts. r=dbaron
authorXidorn Quan <quanxunzhen@gmail.com>
Wed, 11 Jun 2014 21:12:00 -0400
changeset 210371 fffcb4bbc8b17a34f5fa5013418a8956d0fdcc7a
parent 210370 001216bdc6058cafa9e0ab57efc4c20d252dd625
child 210372 bffb0b2f7c2f279ba10d407fb302e00c2051364f
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
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 3: Link to other parts. r=dbaron
accessible/src/html/HTMLListAccessible.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/base/nsCounterManager.cpp
layout/base/nsCounterManager.h
layout/base/nsIPresShell.h
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsBulletFrame.cpp
layout/generic/nsBulletFrame.h
layout/generic/nsHTMLReflowState.cpp
layout/reftests/counters/counter-reset-integer-range-ref.html
layout/reftests/counters/counter-ua-limits-02-ref.html
layout/reftests/counters/reftest.list
layout/reftests/counters/t1202-counter-16-f-test.html
layout/reftests/counters/t1202-counters-18-f-test.html
layout/style/Declaration.cpp
layout/style/moz.build
layout/style/nsCSSParser.cpp
layout/style/nsCSSPropList.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
layout/style/test/test_garbage_at_end_of_declarations.html
--- a/accessible/src/html/HTMLListAccessible.cpp
+++ b/accessible/src/html/HTMLListAccessible.cpp
@@ -160,17 +160,17 @@ HTMLListBulletAccessible::GetFrame() con
 ENameValueFlag
 HTMLListBulletAccessible::Name(nsString &aName)
 {
   aName.Truncate();
 
   // Native anonymous content, ARIA can't be used. Get list bullet text.
   nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
   if (blockFrame) {
-    blockFrame->GetBulletText(aName);
+    blockFrame->GetSpokenBulletText(aName);
   }
 
   return eNameOK;
 }
 
 role
 HTMLListBulletAccessible::NativeRole()
 {
@@ -185,17 +185,17 @@ HTMLListBulletAccessible::NativeState()
 
 void
 HTMLListBulletAccessible::AppendTextTo(nsAString& aText, uint32_t aStartOffset,
                                        uint32_t aLength)
 {
   nsAutoString bulletText;
   nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
   if (blockFrame)
-    blockFrame->GetBulletText(bulletText);
+    blockFrame->GetSpokenBulletText(bulletText);
 
   aText.Append(Substring(bulletText, aStartOffset, aLength));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLListBulletAccessible: public
 
 bool
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -1669,17 +1669,18 @@ nsCSSFrameConstructor::CreateGeneratedCo
     {
       nsCSSValue::Array* counters = data.mContent.mCounters;
       nsCounterList* counterList = mCounterManager.CounterListFor(
           nsDependentString(counters->Item(0).GetStringBufferValue()));
       if (!counterList)
         return nullptr;
 
       nsCounterUseNode* node =
-        new nsCounterUseNode(counters, aContentIndex,
+        new nsCounterUseNode(mPresShell->GetPresContext(),
+                             counters, aContentIndex,
                              type == eStyleContentType_Counters);
 
       nsGenConInitializer* initializer =
         new nsGenConInitializer(node, counterList,
                                 &nsCSSFrameConstructor::CountersDirty);
       return CreateGenConTextNode(aState, EmptyString(), &node->mText,
                                   initializer);
     }
@@ -8004,16 +8005,24 @@ nsCSSFrameConstructor::RecalcQuotesAndCo
     mCounterManager.RecalcAll();
   }
 
   NS_ASSERTION(!mQuotesDirty, "Quotes updates will be lost");
   NS_ASSERTION(!mCountersDirty, "Counter updates will be lost");
 }
 
 void
+nsCSSFrameConstructor::NotifyCounterStylesAreDirty()
+{
+  NS_PRECONDITION(mUpdateCount != 0, "Should be in an update");
+  mCounterManager.SetAllCounterStylesDirty();
+  CountersDirty();
+}
+
+void
 nsCSSFrameConstructor::WillDestroyFrameTree()
 {
 #if defined(DEBUG_dbaron_off)
   mCounterManager.Dump();
 #endif
 
   mIsDestroyingFrameTree = true;
 
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -215,16 +215,19 @@ public:
   // Should be called when a frame is going to be destroyed and
   // WillDestroyFrameTree hasn't been called yet.
   void NotifyDestroyingFrame(nsIFrame* aFrame);
 
   void BeginUpdate();
   void EndUpdate();
   void RecalcQuotesAndCounters();
 
+  // Called when any counter style is changed.
+  void NotifyCounterStylesAreDirty();
+
   // Gets called when the presshell is destroying itself and also
   // when we tear down our frame tree to reconstruct it
   void WillDestroyFrameTree();
 
   // Request to create a continuing frame.  This method never returns null.
   nsIFrame* CreateContinuingFrame(nsPresContext*    aPresContext,
                                   nsIFrame*         aFrame,
                                   nsContainerFrame* aParentFrame,
--- a/layout/base/nsCounterManager.cpp
+++ b/layout/base/nsCounterManager.cpp
@@ -8,16 +8,18 @@
 
 #include "nsCounterManager.h"
 #include "nsBulletFrame.h" // legacy location for list style type to text code
 #include "nsContentUtils.h"
 #include "nsTArray.h"
 #include "mozilla/Likely.h"
 #include "nsIContent.h"
 
+using namespace mozilla;
+
 bool
 nsCounterUseNode::InitTextFrame(nsGenConList* aList,
         nsIFrame* aPseudoFrame, nsIFrame* aTextFrame)
 {
   nsCounterNode::InitTextFrame(aList, aPseudoFrame, aTextFrame);
 
   nsCounterList *counterList = static_cast<nsCounterList*>(aList);
   counterList->Insert(this);
@@ -35,16 +37,27 @@ nsCounterUseNode::InitTextFrame(nsGenCon
       counterList->SetDirty();
       return true;
     }
   }
   
   return false;
 }
 
+CounterStyle*
+nsCounterUseNode::GetCounterStyle()
+{
+    if (!mCounterStyle) {
+        const nsCSSValue& style = mCounterFunction->Item(mAllCounters ? 2 : 1);
+        mCounterStyle = mPresContext->CounterStyleManager()->
+            BuildCounterStyle(nsDependentString(style.GetStringBufferValue()));
+    }
+    return mCounterStyle;
+}
+
 // assign the correct |mValueAfter| value to a node that has been inserted
 // Should be called immediately after calling |Insert|.
 void nsCounterUseNode::Calc(nsCounterList *aList)
 {
     NS_ASSERTION(!aList->IsDirty(),
                  "Why are we calculating with a dirty list?");
     mValueAfter = aList->ValueBefore(this);
 }
@@ -72,27 +85,27 @@ nsCounterUseNode::GetText(nsString& aRes
 
     nsAutoTArray<nsCounterNode*, 8> stack;
     stack.AppendElement(static_cast<nsCounterNode*>(this));
 
     if (mAllCounters && mScopeStart)
         for (nsCounterNode *n = mScopeStart; n->mScopePrev; n = n->mScopeStart)
             stack.AppendElement(n->mScopePrev);
 
-    const nsCSSValue& styleItem = mCounterStyle->Item(mAllCounters ? 2 : 1);
-    int32_t style = styleItem.GetIntValue();
     const char16_t* separator;
     if (mAllCounters)
-        separator = mCounterStyle->Item(1).GetStringBufferValue();
+        separator = mCounterFunction->Item(1).GetStringBufferValue();
 
+    CounterStyle* style = GetCounterStyle();
     for (uint32_t i = stack.Length() - 1;; --i) {
         nsCounterNode *n = stack[i];
+        nsAutoString text;
         bool isTextRTL;
-        nsBulletFrame::AppendCounterText(
-                style, n->mValueAfter, aResult, isTextRTL);
+        style->GetCounterText(n->mValueAfter, text, isTextRTL);
+        aResult.Append(text);
         if (i == 0)
             break;
         NS_ASSERTION(mAllCounters, "yikes, separator is uninitialized");
         aResult.Append(separator);
     }
 }
 
 void
@@ -262,16 +275,44 @@ RecalcDirtyLists(const nsAString& aKey, 
 }
 
 void
 nsCounterManager::RecalcAll()
 {
     mNames.EnumerateRead(RecalcDirtyLists, nullptr);
 }
 
+static PLDHashOperator
+SetCounterStylesDirty(const nsAString& aKey,
+                      nsCounterList* aList,
+                      void* aClosure)
+{
+    nsCounterNode* first = aList->First();
+    if (first) {
+        bool changed = false;
+        nsCounterNode* node = first;
+        do {
+            if (node->mType == nsCounterNode::USE) {
+                node->UseNode()->SetCounterStyleDirty();
+                changed = true;
+            }
+        } while ((node = aList->Next(node)) != first);
+        if (changed) {
+            aList->SetDirty();
+        }
+    }
+    return PL_DHASH_NEXT;
+}
+
+void
+nsCounterManager::SetAllCounterStylesDirty()
+{
+    mNames.EnumerateRead(SetCounterStylesDirty, nullptr);
+}
+
 struct DestroyNodesData {
     DestroyNodesData(nsIFrame *aFrame)
         : mFrame(aFrame)
         , mDestroyedAny(false)
     {
     }
 
     nsIFrame *mFrame;
--- a/layout/base/nsCounterManager.h
+++ b/layout/base/nsCounterManager.h
@@ -9,16 +9,17 @@
 #ifndef nsCounterManager_h_
 #define nsCounterManager_h_
 
 #include "mozilla/Attributes.h"
 #include "nsGenConList.h"
 #include "nsAutoPtr.h"
 #include "nsClassHashtable.h"
 #include "mozilla/Likely.h"
+#include "CounterStyleManager.h"
 
 class nsCounterList;
 struct nsCounterUseNode;
 struct nsCounterChangeNode;
 
 struct nsCounterNode : public nsGenConNode {
     enum Type {
         RESET,     // a "counter number" pair in 'counter-reset'
@@ -74,34 +75,46 @@ struct nsCounterNode : public nsGenConNo
     // to avoid virtual function calls in the common case
     inline void Calc(nsCounterList* aList);
 };
 
 struct nsCounterUseNode : public nsCounterNode {
     // The same structure passed through the style system:  an array
     // containing the values in the counter() or counters() in the order
     // given in the CSS spec.
-    nsRefPtr<nsCSSValue::Array> mCounterStyle;
+    nsRefPtr<nsCSSValue::Array> mCounterFunction;
+
+    nsPresContext* mPresContext;
+    nsRefPtr<mozilla::CounterStyle> mCounterStyle;
 
     // false for counter(), true for counters()
     bool mAllCounters;
 
     // args go directly to member variables here and of nsGenConNode
-    nsCounterUseNode(nsCSSValue::Array* aCounterStyle,
+    nsCounterUseNode(nsPresContext* aPresContext,
+                     nsCSSValue::Array* aCounterFunction,
                      uint32_t aContentIndex, bool aAllCounters)
         : nsCounterNode(aContentIndex, USE)
-        , mCounterStyle(aCounterStyle)
+        , mCounterFunction(aCounterFunction)
+        , mPresContext(aPresContext)
+        , mCounterStyle(nullptr)
         , mAllCounters(aAllCounters)
     {
         NS_ASSERTION(aContentIndex <= INT32_MAX, "out of range");
     }
     
     virtual bool InitTextFrame(nsGenConList* aList,
             nsIFrame* aPseudoFrame, nsIFrame* aTextFrame) MOZ_OVERRIDE;
 
+    mozilla::CounterStyle* GetCounterStyle();
+    void SetCounterStyleDirty()
+    {
+        mCounterStyle = nullptr;
+    }
+
     // assign the correct |mValueAfter| value to a node that has been inserted
     // Should be called immediately after calling |Insert|.
     void Calc(nsCounterList* aList);
 
     // The text that should be displayed for this counter.
     void GetText(nsString& aResult);
 };
 
@@ -214,16 +227,19 @@ public:
 
     // Gets the appropriate counter list, creating it if necessary.
     // Returns null only on out-of-memory.
     nsCounterList* CounterListFor(const nsSubstring& aCounterName);
 
     // Clean up data in any dirty counter lists.
     void RecalcAll();
 
+    // Set all counter styles dirty
+    void SetAllCounterStylesDirty();
+
     // Destroy nodes for the frame in any lists, and return whether any
     // nodes were destroyed.
     bool DestroyNodesFor(nsIFrame *aFrame);
 
     // Clear all data.
     void Clear() { mNames.Clear(); }
 
 #ifdef DEBUG
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -525,16 +525,18 @@ public:
    * called on must be in the process of being reflowed when it's called.  This
    * method doesn't mark any intrinsic widths dirty and doesn't add any bits
    * other than NS_FRAME_HAS_DIRTY_CHILDREN.
    */
   virtual void FrameNeedsToContinueReflow(nsIFrame *aFrame) = 0;
 
   virtual void CancelAllPendingReflows() = 0;
 
+  virtual void NotifyCounterStylesAreDirty() = 0;
+
   /**
    * Recreates the frames for a node
    */
   virtual nsresult RecreateFramesFor(nsIContent* aContent) = 0;
 
   void PostRecreateFramesFor(mozilla::dom::Element* aElement);
   void RestyleForAnimation(mozilla::dom::Element* aElement,
                            nsRestyleHint aHint);
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -235,16 +235,18 @@ nsPresContext::nsPresContext(nsIDocument
   } else {
     mImageAnimationMode = imgIContainer::kNormalAnimMode;
     mNeverAnimate = false;
   }
   NS_ASSERTION(mDocument, "Null document");
   mUserFontSet = nullptr;
   mUserFontSetDirty = true;
 
+  mCounterStylesDirty = true;
+
   // if text perf logging enabled, init stats struct
   PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textperf);
   if (log && log->level >= PR_LOG_WARNING) {
     mTextPerf = new gfxTextPerfMetrics();
   }
 
   PR_INIT_CLIST(&mDOMMediaQueryLists);
 }
@@ -1870,16 +1872,17 @@ nsPresContext::RebuildAllStyleData(nsCha
   if (!mShell) {
     // We must have been torn down. Nothing to do here.
     return;
   }
 
   mUsesRootEMUnits = false;
   mUsesViewportUnits = false;
   RebuildUserFontSet();
+  RebuildCounterStyles();
 
   RestyleManager()->RebuildAllStyleData(aExtraHint);
 }
 
 void
 nsPresContext::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint)
 {
   if (!mShell) {
@@ -2205,16 +2208,56 @@ nsPresContext::UserFontSetUpdated()
   //      which also depend on font metrics.  Updating this information
   //      requires rebuilding the rule tree from the top, avoiding the
   //      reuse of cached data even when no style rules have changed.
 
   PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW);
 }
 
 void
+nsPresContext::FlushCounterStyles()
+{
+  if (!mShell) {
+    return; // we've been torn down
+  }
+  if (mCounterStyleManager->IsInitial()) {
+    // Still in its initial state, no need to clean.
+    return;
+  }
+
+  if (mCounterStylesDirty) {
+    bool changed = mCounterStyleManager->NotifyRuleChanged();
+    if (changed) {
+      PresShell()->NotifyCounterStylesAreDirty();
+      PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW);
+    }
+    mCounterStylesDirty = false;
+  }
+}
+
+void
+nsPresContext::RebuildCounterStyles()
+{
+  if (mCounterStyleManager->IsInitial()) {
+    // Still in its initial state, no need to reset.
+    return;
+  }
+
+  mCounterStylesDirty = true;
+  mDocument->SetNeedStyleFlush();
+  if (!mPostedFlushCounterStyles) {
+    nsCOMPtr<nsIRunnable> ev =
+      NS_NewRunnableMethod(this, &nsPresContext::HandleRebuildCounterStyles);
+    if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
+      mPostedFlushCounterStyles = true;
+    }
+  }
+}
+
+void
 nsPresContext::EnsureSafeToHandOutCSSRules()
 {
   nsCSSStyleSheet::EnsureUniqueInnerResult res =
     mShell->StyleSet()->EnsureUniqueInnerOnCSSSheets();
   if (res == nsCSSStyleSheet::eUniqueInner_AlreadyUnique) {
     // Nothing to do.
     return;
   }
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -248,17 +248,17 @@ public:
     return mCounterStyleManager;
   }
 #endif
 
   /**
    * Rebuilds all style data by throwing out the old rule tree and
    * building a new one, and additionally applying aExtraHint (which
    * must not contain nsChangeHint_ReconstructFrame) to the root frame.
-   * Also rebuild the user font set.
+   * Also rebuild the user font set and counter style manager.
    */
   void RebuildAllStyleData(nsChangeHint aExtraHint);
   /**
    * Just like RebuildAllStyleData, except (1) asynchronous and (2) it
    * doesn't rebuild the user font set.
    */
   void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint);
 
@@ -869,16 +869,19 @@ public:
   void FlushUserFontSet();
   void RebuildUserFontSet(); // asynchronously
 
   // Should be called whenever the set of fonts available in the user
   // font set changes (e.g., because a new font loads, or because the
   // user font set is changed and fonts become unavailable).
   void UserFontSetUpdated();
 
+  void FlushCounterStyles();
+  void RebuildCounterStyles(); // asynchronously
+
   // Ensure that it is safe to hand out CSS rules outside the layout
   // engine by ensuring that all CSS style sheets have unique inners
   // and, if necessary, synchronously rebuilding all style data.
   void EnsureSafeToHandOutCSSRules();
 
   void NotifyInvalidation(uint32_t aFlags);
   void NotifyInvalidation(const nsRect& aRect, uint32_t aFlags);
   // aRect is in device pixels
@@ -1158,16 +1161,21 @@ protected:
   void InvalidateThebesLayers();
   void AppUnitsPerDevPixelChanged();
 
   void HandleRebuildUserFontSet() {
     mPostedFlushUserFontSet = false;
     FlushUserFontSet();
   }
 
+  void HandleRebuildCounterStyles() {
+    mPostedFlushCounterStyles = false;
+    FlushCounterStyles();
+  }
+
   bool HavePendingInputEvent();
 
   // Can't be inline because we can't include nsStyleSet.h.
   bool HasCachedStyleData();
 
   bool IsChromeSlow() const;
 
   // IMPORTANT: The ownership implicit in the following member variables
@@ -1323,16 +1331,21 @@ protected:
 
   // Is the current mUserFontSet valid?
   unsigned              mUserFontSetDirty : 1;
   // Has GetUserFontSet() been called?
   unsigned              mGetUserFontSetCalled : 1;
   // Do we currently have an event posted to call FlushUserFontSet?
   unsigned              mPostedFlushUserFontSet : 1;
 
+  // Is the current mCounterStyleManager valid?
+  unsigned              mCounterStylesDirty : 1;
+  // Do we currently have an event posted to call FlushCounterStyles?
+  unsigned              mPostedFlushCounterStyles: 1;
+
   // resize reflow is suppressed when the only change has been to zoom
   // the document rather than to change the document's dimensions
   unsigned              mSupressResizeReflow : 1;
 
   unsigned              mIsVisual : 1;
 
   unsigned              mProcessingRestyles : 1;
   unsigned              mProcessingAnimationStyleChange : 1;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -4166,16 +4166,18 @@ PresShell::FlushPendingNotifications(moz
       mViewManager->FlushDelayedResize(false);
       mPresContext->FlushPendingMediaFeatureValuesChanged();
 
       // Flush any pending update of the user font set, since that could
       // cause style changes (for updating ex/ch units, and to cause a
       // reflow).
       mPresContext->FlushUserFontSet();
 
+      mPresContext->FlushCounterStyles();
+
       // Flush any requested SMIL samples.
       if (mDocument->HasAnimationController()) {
         mDocument->GetAnimationController()->FlushResampleRequests();
       }
 
       if (aFlush.mFlushAnimations &&
           !mPresContext->StyleUpdateForAllAnimationsIsUpToDate()) {
         if (mPresContext->AnimationManager()) {
@@ -4512,16 +4514,25 @@ PresShell::ContentRemoved(nsIDocument *a
       static_cast<nsINode*>(aContainer) == static_cast<nsINode*>(aDocument)) ||
       aDocument) && aChild->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
     NotifyFontSizeInflationEnabledIsDirty();
   }
 
   VERIFY_STYLE_TREE;
 }
 
+void
+PresShell::NotifyCounterStylesAreDirty()
+{
+  nsAutoCauseReflowNotifier reflowNotifier(this);
+  mFrameConstructor->BeginUpdate();
+  mFrameConstructor->NotifyCounterStylesAreDirty();
+  mFrameConstructor->EndUpdate();
+}
+
 nsresult
 PresShell::ReconstructFrames(void)
 {
   NS_PRECONDITION(!mFrameConstructor->GetRootFrame() || mDidInitialize,
                   "Must not have root frame before initial reflow");
   if (!mDidInitialize) {
     // Nothing to do here
     return NS_OK;
@@ -4558,16 +4569,17 @@ nsIPresShell::ReconstructStyleDataIntern
 
   if (mIsDestroying) {
     // We don't want to mess with restyles at this point
     return;
   }
 
   if (mPresContext) {
     mPresContext->RebuildUserFontSet();
+    mPresContext->RebuildCounterStyles();
   }
 
   Element* root = mDocument->GetRootElement();
   if (!mDidInitialize) {
     // Nothing to do here, since we have no frames yet
     return;
   }
 
@@ -8551,16 +8563,18 @@ PresShell::WillDoReflow()
   // We just reflowed, tell the caret that its frame might have moved.
   // XXXbz that comment makes no sense
   if (mCaret) {
     mCaret->InvalidateOutsideCaret();
   }
 
   mPresContext->FlushUserFontSet();
 
+  mPresContext->FlushCounterStyles();
+
   mFrameConstructor->BeginUpdate();
 
   mLastReflowStart = GetPerformanceNow();
 }
 
 void
 PresShell::DidDoReflow(bool aInterruptible, bool aWasInterrupted)
 {
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -154,16 +154,17 @@ public:
                                  mozilla::WidgetEvent* aEvent,
                                  nsIFrame* aFrame,
                                  nsIContent* aContent,
                                  nsEventStatus* aStatus) MOZ_OVERRIDE;
   virtual nsIFrame* GetEventTargetFrame() MOZ_OVERRIDE;
   virtual already_AddRefed<nsIContent> GetEventTargetContent(
                                                      mozilla::WidgetEvent* aEvent) MOZ_OVERRIDE;
 
+  virtual void NotifyCounterStylesAreDirty();
 
   virtual nsresult ReconstructFrames(void) MOZ_OVERRIDE;
   virtual void Freeze() MOZ_OVERRIDE;
   virtual void Thaw() MOZ_OVERRIDE;
   virtual void FireOrClearDelayedEvents(bool aFireEvents) MOZ_OVERRIDE;
 
   virtual nsresult RenderDocument(const nsRect& aRect, uint32_t aFlags,
                                               nscolor aBackgroundColor,
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -43,16 +43,17 @@
 #endif
 #include "nsLayoutUtils.h"
 #include "nsDisplayList.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsRenderingContext.h"
 #include "TextOverflow.h"
 #include "nsIFrameInlines.h"
+#include "CounterStyleManager.h"
 
 #include "nsBidiPresUtils.h"
 
 static const int MIN_LINES_NEEDING_CURSOR = 20;
 
 static const char16_t kDiscCharacter = 0x2022;
 static const char16_t kCircleCharacter = 0x25e6;
 static const char16_t kSquareCharacter = 0x25aa;
@@ -6481,27 +6482,20 @@ nsBlockFrame::SetInitialChildList(ChildL
       }
       possibleListItem = parent;
     }
     if (NS_STYLE_DISPLAY_LIST_ITEM ==
           possibleListItem->StyleDisplay()->mDisplay &&
         !GetPrevInFlow()) {
       // Resolve style for the bullet frame
       const nsStyleList* styleList = StyleList();
-      nsCSSPseudoElements::Type pseudoType;
-      switch (styleList->mListStyleType) {
-        case NS_STYLE_LIST_STYLE_DISC:
-        case NS_STYLE_LIST_STYLE_CIRCLE:
-        case NS_STYLE_LIST_STYLE_SQUARE:
-          pseudoType = nsCSSPseudoElements::ePseudo_mozListBullet;
-          break;
-        default:
-          pseudoType = nsCSSPseudoElements::ePseudo_mozListNumber;
-          break;
-      }
+      CounterStyle* style = styleList->GetCounterStyle();
+      nsCSSPseudoElements::Type pseudoType = style->IsBullet() ?
+        nsCSSPseudoElements::ePseudo_mozListBullet :
+        nsCSSPseudoElements::ePseudo_mozListNumber;
 
       nsIPresShell *shell = presContext->PresShell();
 
       nsStyleContext* parentStyle =
         CorrectStyleParentFrame(this,
           nsCSSPseudoElements::GetPseudoAtom(pseudoType))->StyleContext();
       nsRefPtr<nsStyleContext> kidSC = shell->StyleSet()->
         ResolvePseudoElementStyle(mContent->AsElement(), pseudoType,
@@ -6530,45 +6524,33 @@ nsBlockFrame::SetInitialChildList(ChildL
 
 bool
 nsBlockFrame::BulletIsEmpty() const
 {
   NS_ASSERTION(mContent->GetPrimaryFrame()->StyleDisplay()->mDisplay ==
                  NS_STYLE_DISPLAY_LIST_ITEM && HasOutsideBullet(),
                "should only care when we have an outside bullet");
   const nsStyleList* list = StyleList();
-  return list->mListStyleType == NS_STYLE_LIST_STYLE_NONE &&
+  return list->GetCounterStyle()->IsNone() &&
          !list->GetListStyleImage();
 }
 
 void
-nsBlockFrame::GetBulletText(nsAString& aText) const
-{
-  aText.Truncate();
-
+nsBlockFrame::GetSpokenBulletText(nsAString& aText) const
+{
   const nsStyleList* myList = StyleList();
-  if (myList->GetListStyleImage() ||
-      myList->mListStyleType == NS_STYLE_LIST_STYLE_DISC) {
+  if (myList->GetListStyleImage()) {
     aText.Assign(kDiscCharacter);
     aText.Append(' ');
-  }
-  else if (myList->mListStyleType == NS_STYLE_LIST_STYLE_CIRCLE) {
-    aText.Assign(kCircleCharacter);
-    aText.Append(' ');
-  }
-  else if (myList->mListStyleType == NS_STYLE_LIST_STYLE_SQUARE) {
-    aText.Assign(kSquareCharacter);
-    aText.Append(' ');
-  }
-  else if (myList->mListStyleType != NS_STYLE_LIST_STYLE_NONE) {
+  } else {
     nsBulletFrame* bullet = GetBullet();
     if (bullet) {
-      nsAutoString text;
-      bullet->GetListItemText(*myList, text);
-      aText = text;
+      bullet->GetSpokenText(aText);
+    } else {
+      aText.Truncate();
     }
   }
 }
 
 // static
 bool
 nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame)
 {
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -188,17 +188,17 @@ public:
   // Given that we have a bullet, does it actually draw something, i.e.,
   // do we have either a 'list-style-type' or 'list-style-image' that is
   // not 'none'?
   bool BulletIsEmpty() const;
 
   /**
    * Return the bullet text equivalent.
    */
-  void GetBulletText(nsAString& aText) const;
+  void GetSpokenBulletText(nsAString& aText) const;
 
   /**
    * Return true if there's a bullet.
    */
   bool HasBullet() const {
     return HasOutsideBullet() || HasInsideBullet();
   }
 
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -15,16 +15,17 @@
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsIDocument.h"
 #include "nsRenderingContext.h"
 #include "prprf.h"
 #include "nsDisplayList.h"
 #include "nsCounterManager.h"
 #include "nsBidiUtils.h"
+#include "CounterStyleManager.h"
 
 #include "imgIContainer.h"
 #include "imgRequestProxy.h"
 #include "nsIURI.h"
 
 #include <algorithm>
 
 #ifdef ACCESSIBILITY
@@ -86,17 +87,17 @@ bool
 nsBulletFrame::IsEmpty()
 {
   return IsSelfEmpty();
 }
 
 bool
 nsBulletFrame::IsSelfEmpty() 
 {
-  return StyleList()->mListStyleType == NS_STYLE_LIST_STYLE_NONE;
+  return StyleList()->GetCounterStyle()->IsNone();
 }
 
 /* virtual */ void
 nsBulletFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   nsFrame::DidSetStyleContext(aOldStyleContext);
 
   imgRequestProxy *newRequest = StyleList()->GetListStyleImage();
@@ -161,21 +162,21 @@ nsBulletFrame::DidSetStyleContext(nsStyl
   // Update the list bullet accessible. If old style list isn't available then
   // no need to update the accessible tree because it's not created yet.
   if (aOldStyleContext) {
     nsAccessibilityService* accService = nsIPresShell::AccService();
     if (accService) {
       const nsStyleList* oldStyleList = aOldStyleContext->PeekStyleList();
       if (oldStyleList) {
         bool hadBullet = oldStyleList->GetListStyleImage() ||
-            oldStyleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE;
+          !oldStyleList->GetCounterStyle()->IsNone();
 
         const nsStyleList* newStyleList = StyleList();
         bool hasBullet = newStyleList->GetListStyleImage() ||
-            newStyleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE;
+          !newStyleList->GetCounterStyle()->IsNone();
 
         if (hadBullet != hasBullet) {
           accService->UpdateListBullet(PresContext()->GetPresShell(), mContent,
                                        hasBullet);
         }
       }
     }
   }
@@ -284,17 +285,17 @@ nsBulletFrame::BuildDisplayList(nsDispla
     new (aBuilder) nsDisplayBullet(aBuilder, this));
 }
 
 void
 nsBulletFrame::PaintBullet(nsRenderingContext& aRenderingContext, nsPoint aPt,
                            const nsRect& aDirtyRect, uint32_t aFlags)
 {
   const nsStyleList* myList = StyleList();
-  uint8_t listStyleType = myList->mListStyleType;
+  CounterStyle* listStyleType = myList->GetCounterStyle();
 
   if (myList->GetListStyleImage() && mImageRequest) {
     uint32_t status;
     mImageRequest->GetImageStatus(&status);
     if (status & imgIRequest::STATUS_LOAD_COMPLETE &&
         !(status & imgIRequest::STATUS_ERROR)) {
       nsCOMPtr<imgIContainer> imageCon;
       mImageRequest->GetImage(getter_AddRefs(imageCon));
@@ -309,21 +310,20 @@ nsBulletFrame::PaintBullet(nsRenderingCo
       }
     }
   }
 
   nsRefPtr<nsFontMetrics> fm;
   aRenderingContext.SetColor(nsLayoutUtils::GetColor(this, eCSSProperty_color));
 
   nsAutoString text;
-  switch (listStyleType) {
+  switch (listStyleType->GetStyle()) {
   case NS_STYLE_LIST_STYLE_NONE:
     break;
 
-  default:
   case NS_STYLE_LIST_STYLE_DISC:
     aRenderingContext.FillEllipse(mPadding.left + aPt.x, mPadding.top + aPt.y,
                                   mRect.width - (mPadding.left + mPadding.right),
                                   mRect.height - (mPadding.top + mPadding.bottom));
     break;
 
   case NS_STYLE_LIST_STYLE_CIRCLE:
     aRenderingContext.DrawEllipse(mPadding.left + aPt.x, mPadding.top + aPt.y,
@@ -348,75 +348,20 @@ nsBulletFrame::PaintBullet(nsRenderingCo
                       pc->RoundAppUnitsToNearestDevPixels(rect.height));
       snapRect.MoveBy((rect.width - snapRect.width) / 2,
                       (rect.height - snapRect.height) / 2);
       aRenderingContext.FillRect(snapRect.x, snapRect.y,
                                  snapRect.width, snapRect.height);
     }
     break;
 
-  case NS_STYLE_LIST_STYLE_DECIMAL:
-  case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO:
-  case NS_STYLE_LIST_STYLE_CJK_DECIMAL:
-  case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
-  case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
-  case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
-  case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
-  case NS_STYLE_LIST_STYLE_LOWER_GREEK:
-  case NS_STYLE_LIST_STYLE_HEBREW:
-  case NS_STYLE_LIST_STYLE_ARMENIAN:
-  case NS_STYLE_LIST_STYLE_GEORGIAN:
-  case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC:
-  case NS_STYLE_LIST_STYLE_HIRAGANA:
-  case NS_STYLE_LIST_STYLE_KATAKANA:
-  case NS_STYLE_LIST_STYLE_HIRAGANA_IROHA:
-  case NS_STYLE_LIST_STYLE_KATAKANA_IROHA:
-  case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
-  case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
-  case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
-  case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
-  case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
-  case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
-  case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
-  case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
-  case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
-  case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL: 
-  case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL: 
-  case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL: 
-  case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL: 
-  case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL: 
-  case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL: 
-  case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM:
-  case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH:
-  case NS_STYLE_LIST_STYLE_MOZ_ARABIC_INDIC:
-  case NS_STYLE_LIST_STYLE_MOZ_PERSIAN:
-  case NS_STYLE_LIST_STYLE_MOZ_URDU:
-  case NS_STYLE_LIST_STYLE_MOZ_DEVANAGARI:
-  case NS_STYLE_LIST_STYLE_MOZ_GURMUKHI:
-  case NS_STYLE_LIST_STYLE_MOZ_GUJARATI:
-  case NS_STYLE_LIST_STYLE_MOZ_ORIYA:
-  case NS_STYLE_LIST_STYLE_MOZ_KANNADA:
-  case NS_STYLE_LIST_STYLE_MOZ_MALAYALAM:
-  case NS_STYLE_LIST_STYLE_MOZ_BENGALI:
-  case NS_STYLE_LIST_STYLE_MOZ_TAMIL:
-  case NS_STYLE_LIST_STYLE_MOZ_TELUGU:
-  case NS_STYLE_LIST_STYLE_MOZ_THAI:
-  case NS_STYLE_LIST_STYLE_MOZ_LAO:
-  case NS_STYLE_LIST_STYLE_MOZ_MYANMAR:
-  case NS_STYLE_LIST_STYLE_MOZ_KHMER:
-  case NS_STYLE_LIST_STYLE_MOZ_HANGUL:
-  case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT:
-  case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME:
-  case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_NUMERIC:
-  case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_AM:
-  case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ER:
-  case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ET:
+  default:
     nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
                                           GetFontSizeInflation());
-    GetListItemText(*myList, text);
+    GetListItemText(text);
     aRenderingContext.SetFont(fm);
     nscoord ascent = fm->MaxAscent();
     aPt.MoveBy(mPadding.left, mPadding.top);
     aPt.y = NSToCoordRound(nsLayoutUtils::GetSnappedBaselineY(
             this, aRenderingContext.ThebesContext(), aPt.y, ascent));
     nsPresContext* presContext = PresContext();
     if (!presContext->BidiEnabled() && HasRTLChars(text)) {
       presContext->SetBidiEnabled();
@@ -1493,43 +1438,43 @@ nsBulletFrame::GetListItemSuffix(int32_t
     case NS_STYLE_LIST_STYLE_MOZ_HANGUL:
     case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT:
       aResult.AssignLiteral(MOZ_UTF16(", "));
       break;
   }
 }
 
 void
-nsBulletFrame::GetListItemText(const nsStyleList& aListStyle,
-                               nsString& result)
+nsBulletFrame::GetListItemText(nsAString& aResult)
 {
-  NS_ASSERTION(aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_NONE &&
-               aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_DISC &&
-               aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_CIRCLE &&
-               aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_SQUARE,
+  CounterStyle* style = StyleList()->GetCounterStyle();
+  NS_ASSERTION(style->GetStyle() != NS_STYLE_LIST_STYLE_NONE &&
+               style->GetStyle() != NS_STYLE_LIST_STYLE_DISC &&
+               style->GetStyle() != NS_STYLE_LIST_STYLE_CIRCLE &&
+               style->GetStyle() != NS_STYLE_LIST_STYLE_SQUARE,
                "we should be using specialized code for these types");
 
   bool isRTL;
-  nsAutoString number;
-  AppendCounterText(aListStyle.mListStyleType, mOrdinal, number, isRTL);
+  nsAutoString counter, prefix, suffix;
+  style->GetPrefix(prefix);
+  style->GetSuffix(suffix);
+  style->GetCounterText(mOrdinal, counter, isRTL);
 
-  nsAutoString suffix;
-  GetListItemSuffix(aListStyle.mListStyleType, suffix);
-
-  result.Truncate();
+  aResult.Truncate();
+  aResult.Append(prefix);
   if (GetWritingMode().IsBidiLTR() != isRTL) {
-    result.Append(number);
+    aResult.Append(counter);
   } else {
     // RLM = 0x200f, LRM = 0x200e
     char16_t mark = isRTL ? 0x200f : 0x200e;
-    result.Append(mark);
-    result.Append(number);
-    result.Append(mark);
+    aResult.Append(mark);
+    aResult.Append(counter);
+    aResult.Append(mark);
   }
-  result.Append(suffix);
+  aResult.Append(suffix);
 }
 
 #define MIN_BULLET_SIZE 1
 
 
 void
 nsBulletFrame::GetDesiredSize(nsPresContext*  aCX,
                               nsRenderingContext *aRenderingContext,
@@ -1579,17 +1524,17 @@ nsBulletFrame::GetDesiredSize(nsPresCont
   // fully there, we'll end up with mIntrinsicSize not matching our size, but
   // won't trigger a reflow in OnStartContainer (because mIntrinsicSize will
   // match the image size).
   mIntrinsicSize.SizeTo(0, 0);
 
   nscoord bulletSize;
 
   nsAutoString text;
-  switch (myList->mListStyleType) {
+  switch (myList->GetCounterStyle()->GetStyle()) {
     case NS_STYLE_LIST_STYLE_NONE:
       aMetrics.Width() = aMetrics.Height() = 0;
       aMetrics.SetBlockStartAscent(0);
       break;
 
     case NS_STYLE_LIST_STYLE_DISC:
     case NS_STYLE_LIST_STYLE_CIRCLE:
     case NS_STYLE_LIST_STYLE_SQUARE: {
@@ -1609,73 +1554,17 @@ nsBulletFrame::GetDesiredSize(nsPresCont
         mPadding.right += halfEm;
       } else {
         mPadding.left += halfEm;
       }
       break;
     }
 
     default:
-    case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO:
-    case NS_STYLE_LIST_STYLE_DECIMAL:
-    case NS_STYLE_LIST_STYLE_CJK_DECIMAL:
-    case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
-    case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
-    case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
-    case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
-    case NS_STYLE_LIST_STYLE_KATAKANA:
-    case NS_STYLE_LIST_STYLE_HIRAGANA:
-    case NS_STYLE_LIST_STYLE_KATAKANA_IROHA:
-    case NS_STYLE_LIST_STYLE_HIRAGANA_IROHA:
-    case NS_STYLE_LIST_STYLE_LOWER_GREEK:
-    case NS_STYLE_LIST_STYLE_HEBREW: 
-    case NS_STYLE_LIST_STYLE_ARMENIAN: 
-    case NS_STYLE_LIST_STYLE_GEORGIAN: 
-    case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC: 
-    case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
-    case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
-    case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
-    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
-    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
-    case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
-    case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
-    case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
-    case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
-    case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL: 
-    case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL: 
-    case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL: 
-    case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL: 
-    case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL: 
-    case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL: 
-    case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM:
-    case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH:
-    case NS_STYLE_LIST_STYLE_MOZ_ARABIC_INDIC:
-    case NS_STYLE_LIST_STYLE_MOZ_PERSIAN:
-    case NS_STYLE_LIST_STYLE_MOZ_URDU:
-    case NS_STYLE_LIST_STYLE_MOZ_DEVANAGARI:
-    case NS_STYLE_LIST_STYLE_MOZ_GURMUKHI:
-    case NS_STYLE_LIST_STYLE_MOZ_GUJARATI:
-    case NS_STYLE_LIST_STYLE_MOZ_ORIYA:
-    case NS_STYLE_LIST_STYLE_MOZ_KANNADA:
-    case NS_STYLE_LIST_STYLE_MOZ_MALAYALAM:
-    case NS_STYLE_LIST_STYLE_MOZ_BENGALI:
-    case NS_STYLE_LIST_STYLE_MOZ_TAMIL:
-    case NS_STYLE_LIST_STYLE_MOZ_TELUGU:
-    case NS_STYLE_LIST_STYLE_MOZ_THAI:
-    case NS_STYLE_LIST_STYLE_MOZ_LAO:
-    case NS_STYLE_LIST_STYLE_MOZ_MYANMAR:
-    case NS_STYLE_LIST_STYLE_MOZ_KHMER:
-    case NS_STYLE_LIST_STYLE_MOZ_HANGUL:
-    case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT:
-    case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME:
-    case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_NUMERIC:
-    case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_AM:
-    case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ER:
-    case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ET:
-      GetListItemText(*myList, text);
+      GetListItemText(text);
       aMetrics.Height() = fm->MaxHeight();
       aRenderingContext->SetFont(fm);
       aMetrics.Width() =
         nsLayoutUtils::GetStringWidth(this, aRenderingContext,
                                       text.get(), text.Length());
       aMetrics.SetBlockStartAscent(fm->MaxAscent());
       break;
   }
@@ -1878,18 +1767,18 @@ nsBulletFrame::GetBaseline() const
 {
   nscoord ascent = 0, bottomPadding;
   if (GetStateBits() & BULLET_FRAME_IMAGE_LOADING) {
     ascent = GetRect().height;
   } else {
     nsRefPtr<nsFontMetrics> fm;
     nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
                                           GetFontSizeInflation());
-    const nsStyleList* myList = StyleList();
-    switch (myList->mListStyleType) {
+    CounterStyle* listStyleType = StyleList()->GetCounterStyle();
+    switch (listStyleType->GetStyle()) {
       case NS_STYLE_LIST_STYLE_NONE:
         break;
 
       case NS_STYLE_LIST_STYLE_DISC:
       case NS_STYLE_LIST_STYLE_CIRCLE:
       case NS_STYLE_LIST_STYLE_SQUARE:
         ascent = fm->MaxAscent();
         bottomPadding = NSToCoordRound(float(ascent) / 8.0f);
@@ -1901,16 +1790,32 @@ nsBulletFrame::GetBaseline() const
       default:
         ascent = fm->MaxAscent();
         break;
     }
   }
   return ascent + GetUsedBorderAndPadding().top;
 }
 
+void
+nsBulletFrame::GetSpokenText(nsAString& aText)
+{
+  CounterStyle* style = StyleList()->GetCounterStyle();
+  bool isBullet;
+  style->GetSpokenCounterText(mOrdinal, aText, isBullet);
+  if (isBullet) {
+    aText.Append(' ');
+  } else {
+    nsAutoString prefix, suffix;
+    style->GetPrefix(prefix);
+    style->GetSuffix(suffix);
+    aText = prefix + aText + suffix;
+  }
+}
+
 
 
 
 
 
 
 
 NS_IMPL_ISUPPORTS(nsBulletListener, imgINotificationObserver)
--- a/layout/generic/nsBulletFrame.h
+++ b/layout/generic/nsBulletFrame.h
@@ -83,17 +83,19 @@ public:
                                 nsString& aResult,
                                 bool& aIsRTL);
 
   /* get suffix of list item */
   static void GetListItemSuffix(int32_t aListStyleType,
                                 nsString& aResult);
 
   /* get list item text, with '.' */
-  void GetListItemText(const nsStyleList& aStyleList, nsString& aResult);
+  void GetListItemText(nsAString& aResult);
+
+  void GetSpokenText(nsAString& aText);
                          
   void PaintBullet(nsRenderingContext& aRenderingContext, nsPoint aPt,
                    const nsRect& aDirtyRect, uint32_t aFlags);
   
   virtual bool IsEmpty() MOZ_OVERRIDE;
   virtual bool IsSelfEmpty() MOZ_OVERRIDE;
   virtual nscoord GetBaseline() const MOZ_OVERRIDE;
 
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -22,16 +22,17 @@
 #include "nsTableFrame.h"
 #include "nsTableCellFrame.h"
 #include "nsIPercentHeightObserver.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsFontInflationData.h"
 #include "StickyScrollContainer.h"
 #include "nsIFrameInlines.h"
+#include "CounterStyleManager.h"
 #include <algorithm>
 #include "mozilla/dom/HTMLInputElement.h"
 
 #ifdef DEBUG
 #undef NOISY_VERTICAL_ALIGN
 #else
 #undef NOISY_VERTICAL_ALIGN
 #endif
@@ -102,33 +103,36 @@ static bool CheckNextInFlowParenthood(ns
  * bullets to be rendered with font inflation enabled.
  */
 static  nscoord
 FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame)
 {
   float inflation = nsLayoutUtils::FontSizeInflationFor(aFrame);
   if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
     const nsBlockFrame* blockFrame = static_cast<const nsBlockFrame*>(aFrame);
-    const nsStyleList* styleList = aFrame->StyleList();
 
     // We only want to adjust the margins if we're dealing with an ordered
     // list.
     if (inflation > 1.0f &&
         blockFrame->HasBullet() &&
-        styleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE &&
-        styleList->mListStyleType != NS_STYLE_LIST_STYLE_DISC &&
-        styleList->mListStyleType != NS_STYLE_LIST_STYLE_CIRCLE &&
-        styleList->mListStyleType != NS_STYLE_LIST_STYLE_SQUARE &&
         inflation > 1.0f) {
 
-      // The HTML spec states that the default padding for ordered lists begins
-      // at 40px, indicating that we have 40px of space to place a bullet. When
-      // performing font inflation calculations, we add space equivalent to this,
-      // but simply inflated at the same amount as the text, in app units.
-      return nsPresContext::CSSPixelsToAppUnits(40) * (inflation - 1);
+      auto listStyleType = aFrame->StyleList()->GetCounterStyle()->GetStyle();
+      if (listStyleType != NS_STYLE_LIST_STYLE_NONE &&
+          listStyleType != NS_STYLE_LIST_STYLE_DISC &&
+          listStyleType != NS_STYLE_LIST_STYLE_CIRCLE &&
+          listStyleType != NS_STYLE_LIST_STYLE_SQUARE) {
+        // The HTML spec states that the default padding for ordered lists
+        // begins at 40px, indicating that we have 40px of space to place a
+        // bullet. When performing font inflation calculations, we add space
+        // equivalent to this, but simply inflated at the same amount as the
+        // text, in app units.
+        return nsPresContext::CSSPixelsToAppUnits(40) * (inflation - 1);
+      }
+
     }
   }
 
   return 0;
 }
 
 // NOTE: If we ever want to use nsCSSOffsetState for a flex item or a grid
 // item, we need to make it take the containing-block height as well as the
--- a/layout/reftests/counters/counter-reset-integer-range-ref.html
+++ b/layout/reftests/counters/counter-reset-integer-range-ref.html
@@ -1,9 +1,9 @@
 <!DOCTYPE HTML>
 <title>Expected integer range</title>
 0
 2147483647
 2147483647
 2147483647
 -2147483647
--2147483648
--2147483648
+-2147483647
+-2147483647
--- a/layout/reftests/counters/counter-ua-limits-02-ref.html
+++ b/layout/reftests/counters/counter-ua-limits-02-ref.html
@@ -13,14 +13,14 @@
   <link rel="help" href="http://dev.w3.org/csswg/css-counter-styles-3/#counter-style-range"/>
   <link rel="help" href="http://krijnhoetmer.nl/irc-logs/css/20130205#l-1590"/>
  </head>
  <body>
 
  <div id="test">
    <span>-2147483646</span>
    <span>-2147483647</span>
-   <span>-2147483648</span>
-   <span>-2147483648</span>
+   <span>-2147483647</span>
+   <span>-2147483647</span>
  </div>
 
  </body>
 </html>
--- a/layout/reftests/counters/reftest.list
+++ b/layout/reftests/counters/reftest.list
@@ -55,20 +55,20 @@ fails-if(B2G&&browserIsRemote) == t1204-
 == t120401-scope-03-c-test.html t120401-scope-03-c-reference.html
 == t120401-scope-04-d-test.html t120401-scope-04-d-reference.html
 == t120403-content-none-00-c-test.html t120403-content-none-00-c-reference.html
 == t120403-display-none-00-c-test.html t120403-display-none-00-c-reference.html
 == t120403-visibility-00-c-test.html t120403-visibility-00-c-reference.html
 == text-boundaries-subpixel.html text-boundaries-subpixel-ref.html
 == counter-hebrew-test.html counter-hebrew-reference.html
 == counters-hebrew-test.html counters-hebrew-reference.html
-== counter-reset-integer-range.html counter-reset-integer-range-ref.html
+fails-if(xulRuntime.XPCOMABI.match(/arm/)) == counter-reset-integer-range.html counter-reset-integer-range-ref.html # bug 989718
 == counter-ua-limits-00.html counter-ua-limits-00-ref.html
 == counter-ua-limits-01.html counter-ua-limits-01-ref.html
-== counter-ua-limits-02.html counter-ua-limits-02-ref.html
+fails-if(xulRuntime.XPCOMABI.match(/arm/)) == counter-ua-limits-02.html counter-ua-limits-02-ref.html # bug 989718
 == counter-ua-limits-03.html counter-ua-limits-03-ref.html
 == counter-ua-limits-list-00.html counter-ua-limits-list-00-ref.html
 == counter-ua-limits-list-01.html counter-ua-limits-list-01-ref.html
 == multiple-thai-counters.html multiple-thai-counters-ref.html
 == counter-suffix.html counter-suffix-ref.html
 == counter-cjk-decimal.html counter-cjk-decimal-ref.html
 == counter-japanese-informal.html counter-japanese-informal-ref.html
 == counter-japanese-formal.html counter-japanese-formal-ref.html
--- a/layout/reftests/counters/t1202-counter-16-f-test.html
+++ b/layout/reftests/counters/t1202-counter-16-f-test.html
@@ -10,17 +10,16 @@
   #test { counter-reset: c 0 f 1000; }
   #test span { counter-increment: c; }
   #test span:before {
     content: counter(c);
     content: counter(f, ".");
     content: counter(f, ".", decimal);
     content: counter(f, decimal, ".");
     content: counter(f, decimal, decimal);
-    content: counter(f, unknowntype);
   }
 
   </style>
  </head>
  <body>
 
  <div id="test">
    <span></span>
--- a/layout/reftests/counters/t1202-counters-18-f-test.html
+++ b/layout/reftests/counters/t1202-counters-18-f-test.html
@@ -11,17 +11,16 @@
   p, #test span { counter-increment: c; }
   #test span:before {
     content: counters(c, ".");
     content: counters(f);
     content: counters(f, decimal);
     content: counters(f, decimal, ".");
     content: counters(f, ".", decimal, decimal);
     content: counters(f, ".", decimal, ".");
-    content: counters(f, ".", unknowntype);
   }
 
   </style>
  </head>
  <body>
 
  <p></p>
 
--- a/layout/style/Declaration.cpp
+++ b/layout/style/Declaration.cpp
@@ -710,25 +710,25 @@ Declaration::GetValue(nsCSSProperty aPro
         }
         aValue.Append(char16_t(' '));
         family->AppendToString(eCSSProperty_font_family, aValue,
                                aSerialization);
       }
       break;
     }
     case eCSSProperty_list_style:
-      if (AppendValueToString(eCSSProperty_list_style_type, aValue,
-                              aSerialization)) {
-        aValue.Append(char16_t(' '));
-      }
       if (AppendValueToString(eCSSProperty_list_style_position, aValue,
                               aSerialization)) {
         aValue.Append(char16_t(' '));
       }
-      AppendValueToString(eCSSProperty_list_style_image, aValue,
+      if (AppendValueToString(eCSSProperty_list_style_image, aValue,
+                              aSerialization)) {
+        aValue.Append(char16_t(' '));
+      }
+      AppendValueToString(eCSSProperty_list_style_type, aValue,
                           aSerialization);
       break;
     case eCSSProperty_overflow: {
       const nsCSSValue &xValue =
         *data->ValueFor(eCSSProperty_overflow_x);
       const nsCSSValue &yValue =
         *data->ValueFor(eCSSProperty_overflow_y);
       if (xValue == yValue)
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -4,16 +4,17 @@
 # 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/.
 
 DIRS += ['xbl-marquee']
 TEST_TOOL_DIRS += ['test']
 
 EXPORTS += [
     'AnimationCommon.h',
+    'CounterStyleManager.h',
     'nsAnimationManager.h',
     'nsComputedDOMStylePropertyList.h',
     'nsCSSAnonBoxes.h',
     'nsCSSAnonBoxList.h',
     'nsCSSCounterDescList.h',
     'nsCSSFontDescList.h',
     'nsCSSKeywordList.h',
     'nsCSSKeywords.h',
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -4,16 +4,17 @@
  * 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/. */
 
 /* parsing of CSS stylesheets, based on a token stream from the CSS scanner */
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Move.h"
+#include "mozilla/MathAlgorithms.h"
 
 #include "nsCSSParser.h"
 #include "nsCSSProps.h"
 #include "nsCSSKeywords.h"
 #include "nsCSSScanner.h"
 #include "mozilla/css/ErrorReporter.h"
 #include "mozilla/css/Loader.h"
 #include "mozilla/css/StyleRule.h"
@@ -744,16 +745,17 @@ protected:
   bool ParseFontWeight(nsCSSValue& aValue);
   bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword, bool& aQuoted);
   bool ParseFamily(nsCSSValue& aValue);
   bool ParseFontFeatureSettings(nsCSSValue& aValue);
   bool ParseFontSrc(nsCSSValue& aValue);
   bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
   bool ParseFontRanges(nsCSSValue& aValue);
   bool ParseListStyle();
+  bool ParseListStyleType(nsCSSValue& aValue);
   bool ParseMargin();
   bool ParseMarks(nsCSSValue& aValue);
   bool ParseTransform(bool aIsPrefixed);
   bool ParseOutline();
   bool ParseOverflow();
   bool ParsePadding();
   bool ParseQuotes();
   bool ParseSize();
@@ -6984,34 +6986,25 @@ CSSParserImpl::ParseCounter(nsCSSValue& 
       if (eCSSToken_String != mToken.mType) {
         UngetToken();
         break;
       }
       val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
     }
 
     // get optional type
-    int32_t type = NS_STYLE_LIST_STYLE_DECIMAL;
+    nsString type = NS_LITERAL_STRING("decimal");
     if (ExpectSymbol(',', true)) {
-      if (!GetToken(true)) {
-        break;
-      }
-      nsCSSKeyword keyword;
-      if (eCSSToken_Ident != mToken.mType ||
-          (keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent)) ==
-            eCSSKeyword_UNKNOWN ||
-          !nsCSSProps::FindKeyword(keyword, nsCSSProps::kListStyleKTable,
-                                   type)) {
-        UngetToken();
+      if (!ParseCounterStyleName(type, false)) {
         break;
       }
     }
 
     int32_t typeItem = eCSSUnit_Counters == unit ? 2 : 1;
-    val->Item(typeItem).SetIntValue(type, eCSSUnit_Enumerated);
+    val->Item(typeItem).SetStringValue(type, eCSSUnit_Ident);
 
     if (!ExpectSymbol(')', true)) {
       break;
     }
 
     aValue.SetArrayValue(val, unit);
     return true;
   }
@@ -9699,16 +9692,18 @@ CSSParserImpl::ParseSingleValueProperty(
       case eCSSProperty_font_variant_numeric:
         return ParseFontVariantNumeric(aValue);
       case eCSSProperty_font_feature_settings:
         return ParseFontFeatureSettings(aValue);
       case eCSSProperty_font_weight:
         return ParseFontWeight(aValue);
       case eCSSProperty_image_orientation:
         return ParseImageOrientation(aValue);
+      case eCSSProperty_list_style_type:
+        return ParseListStyleType(aValue);
       case eCSSProperty_marks:
         return ParseMarks(aValue);
       case eCSSProperty_text_align:
         return ParseTextAlign(aValue);
       case eCSSProperty_text_align_last:
         return ParseTextAlignLast(aValue);
       case eCSSProperty_text_decoration_line:
         return ParseTextDecorationLine(aValue);
@@ -12399,71 +12394,86 @@ CSSParserImpl::ParseFontFeatureSettings(
 
   return true;
 }
 
 bool
 CSSParserImpl::ParseListStyle()
 {
   // 'list-style' can accept 'none' for two different subproperties,
-  // 'list-style-type' and 'list-style-position'.  In order to accept
+  // 'list-style-type' and 'list-style-image'.  In order to accept
   // 'none' as the value of either but still allow another value for
   // either, we need to ensure that the first 'none' we find gets
-  // allocated to a dummy property instead.
+  // allocated to a dummy property instead. Since parse function for
+  // 'list-style-type' could accept values for 'list-style-position',
+  // we put position in front of type.
   static const nsCSSProperty listStyleIDs[] = {
     eCSSPropertyExtra_x_none_value,
+    eCSSProperty_list_style_position,
     eCSSProperty_list_style_type,
-    eCSSProperty_list_style_position,
     eCSSProperty_list_style_image
   };
 
   nsCSSValue values[MOZ_ARRAY_LENGTH(listStyleIDs)];
   int32_t found =
     ParseChoice(values, listStyleIDs, ArrayLength(listStyleIDs));
   if (found < 1) {
     return false;
   }
 
-  if ((found & (1|2|8)) == (1|2|8)) {
+  if ((found & (1|4|8)) == (1|4|8)) {
     if (values[0].GetUnit() == eCSSUnit_None) {
       // We found a 'none' plus another value for both of
       // 'list-style-type' and 'list-style-image'.  This is a parse
       // error, since the 'none' has to count for at least one of them.
       return false;
     } else {
       NS_ASSERTION(found == (1|2|4|8) && values[0] == values[1] &&
                    values[0] == values[2] && values[0] == values[3],
                    "should be a special value");
     }
   }
 
-  // Provide default values
   if ((found & 2) == 0) {
-    if (found & 1) {
-      values[1].SetIntValue(NS_STYLE_LIST_STYLE_NONE, eCSSUnit_Enumerated);
-    } else {
-      values[1].SetIntValue(NS_STYLE_LIST_STYLE_DISC, eCSSUnit_Enumerated);
-    }
+    values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE,
+                          eCSSUnit_Enumerated);
   }
   if ((found & 4) == 0) {
-    values[2].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE,
-                          eCSSUnit_Enumerated);
+    // Provide default values
+    nsString type = (found & 1) ?
+      NS_LITERAL_STRING("none") : NS_LITERAL_STRING("disc");
+    values[2].SetStringValue(type, eCSSUnit_Ident);
   }
   if ((found & 8) == 0) {
     values[3].SetNoneValue();
   }
 
   // Start at 1 to avoid appending fake value.
   for (uint32_t index = 1; index < ArrayLength(listStyleIDs); ++index) {
     AppendValue(listStyleIDs[index], values[index]);
   }
   return true;
 }
 
 bool
+CSSParserImpl::ParseListStyleType(nsCSSValue& aValue)
+{
+  if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
+    return true;
+  }
+
+  nsString name;
+  if (ParseCounterStyleName(name, false)) {
+    aValue.SetStringValue(name, eCSSUnit_Ident);
+    return true;
+  }
+  return false;
+}
+
+bool
 CSSParserImpl::ParseMargin()
 {
   static const nsCSSProperty kMarginSideIDs[] = {
     eCSSProperty_margin_top,
     eCSSProperty_margin_right_value,
     eCSSProperty_margin_bottom,
     eCSSProperty_margin_left_value
   };
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -2249,20 +2249,21 @@ CSS_PROP_LIST(
     VARIANT_HK,
     kListStylePositionKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_LIST(
     list-style-type,
     list_style_type,
     ListStyleType,
-    CSS_PROPERTY_PARSE_VALUE,
-    "",
-    VARIANT_HK,
-    kListStyleKTable,
+    CSS_PROPERTY_PARSE_VALUE |
+        CSS_PROPERTY_VALUE_PARSER_FUNCTION,
+    "",
+    0,
+    nullptr,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_SHORTHAND(
     margin,
     margin,
     Margin,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_UNITLESS_LENGTH_QUIRK |
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1093,23 +1093,22 @@ nsComputedDOMStyle::DoGetContent()
           int32_t typeItem = 1;
           if (data.mType == eStyleContentType_Counters) {
             typeItem = 2;
             str.AppendLiteral(", ");
             nsStyleUtil::AppendEscapedCSSString(
               nsDependentString(a->Item(1).GetStringBufferValue()), str);
           }
           NS_ABORT_IF_FALSE(eCSSUnit_None != a->Item(typeItem).GetUnit(),
-                            "'none' should be handled  as enumerated value");
-          int32_t type = a->Item(typeItem).GetIntValue();
-          if (type != NS_STYLE_LIST_STYLE_DECIMAL) {
+                            "'none' should be handled as identifier value");
+          nsString type;
+          a->Item(typeItem).GetStringValue(type);
+          if (!type.LowerCaseEqualsLiteral("decimal")) {
             str.AppendLiteral(", ");
-            AppendASCIItoUTF16(
-              nsCSSProps::ValueToKeyword(type, nsCSSProps::kListStyleKTable),
-              str);
+            nsStyleUtil::AppendEscapedCSSIdent(type, str);
           }
 
           str.Append(char16_t(')'));
           val->SetString(str, nsIDOMCSSPrimitiveValue::CSS_COUNTER);
         }
         break;
       case eStyleContentType_OpenQuote:
         val->SetIdent(eCSSKeyword_open_quote);
@@ -2987,19 +2986,22 @@ nsComputedDOMStyle::DoGetListStylePositi
                                    nsCSSProps::kListStylePositionKTable));
   return val;
 }
 
 CSSValue*
 nsComputedDOMStyle::DoGetListStyleType()
 {
   nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
-  val->SetIdent(
-    nsCSSProps::ValueToKeywordEnum(StyleList()->mListStyleType,
-                                   nsCSSProps::kListStyleKTable));
+  // want SetIdent
+  nsString type;
+  StyleList()->GetListStyleType(type);
+  nsString value;
+  nsStyleUtil::AppendEscapedCSSIdent(type, value);
+  val->SetString(value);
   return val;
 }
 
 CSSValue*
 nsComputedDOMStyle::DoGetImageRegion()
 {
   nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
 
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -40,16 +40,17 @@
 #include "CSSCalc.h"
 #include "nsPrintfCString.h"
 #include "nsRenderingContext.h"
 #include "nsStyleUtil.h"
 #include "nsIDocument.h"
 #include "prtime.h"
 #include "CSSVariableResolver.h"
 #include "nsCSSParser.h"
+#include "CounterStyleManager.h"
 
 #if defined(_MSC_VER) || defined(__MINGW32__)
 #include <malloc.h>
 #ifdef _MSC_VER
 #define alloca _alloca
 #endif
 #endif
 #ifdef SOLARIS
@@ -2382,17 +2383,17 @@ nsRuleNode::SetDefaultOnRoot(const nsSty
     case eStyleStruct_Outline:
     {
       nsStyleOutline* outline = new (mPresContext) nsStyleOutline(mPresContext);
       aContext->SetStyle(eStyleStruct_Outline, outline);
       return outline;
     }
     case eStyleStruct_List:
     {
-      nsStyleList* list = new (mPresContext) nsStyleList();
+      nsStyleList* list = new (mPresContext) nsStyleList(mPresContext);
       aContext->SetStyle(eStyleStruct_List, list);
       return list;
     }
     case eStyleStruct_Position:
     {
       nsStylePosition* pos = new (mPresContext) nsStylePosition();
       aContext->SetStyle(eStyleStruct_Position, pos);
       return pos;
@@ -7011,24 +7012,52 @@ nsRuleNode::ComputeOutlineData(void* aSt
 const void*
 nsRuleNode::ComputeListData(void* aStartStruct,
                             const nsRuleData* aRuleData,
                             nsStyleContext* aContext,
                             nsRuleNode* aHighestNode,
                             const RuleDetail aRuleDetail,
                             const bool aCanStoreInRuleTree)
 {
-  COMPUTE_START_INHERITED(List, (), list, parentList)
-
-  // list-style-type: enum, inherit, initial
-  SetDiscrete(*aRuleData->ValueForListStyleType(),
-              list->mListStyleType, canStoreInRuleTree,
-              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
-              parentList->mListStyleType,
-              NS_STYLE_LIST_STYLE_DISC, 0, 0, 0, 0);
+  COMPUTE_START_INHERITED(List, (mPresContext), list, parentList)
+
+  // list-style-type: string, none, inherit, initial
+  const nsCSSValue* typeValue = aRuleData->ValueForListStyleType();
+  switch (typeValue->GetUnit()) {
+    case eCSSUnit_Unset:
+    case eCSSUnit_Inherit: {
+      canStoreInRuleTree = false;
+      nsString type;
+      parentList->GetListStyleType(type);
+      list->SetListStyleType(type, parentList->GetCounterStyle());
+      break;
+    }
+    case eCSSUnit_Initial:
+      list->SetListStyleType(NS_LITERAL_STRING("disc"), mPresContext);
+      break;
+    case eCSSUnit_Ident: {
+      nsString typeIdent;
+      typeValue->GetStringValue(typeIdent);
+      list->SetListStyleType(typeIdent, mPresContext);
+      break;
+    }
+    case eCSSUnit_Enumerated:
+      // For compatibility with html attribute map.
+      // This branch should never be called for value from CSS.
+      list->SetListStyleType(
+          NS_ConvertASCIItoUTF16(
+              nsCSSProps::ValueToKeyword(
+                  typeValue->GetIntValue(), nsCSSProps::kListStyleKTable)),
+          mPresContext);
+      break;
+    case eCSSUnit_Null:
+      break;
+    default:
+      NS_NOTREACHED("Unexpected value unit");
+  }
 
   // list-style-image: url, none, inherit
   const nsCSSValue* imageValue = aRuleData->ValueForListStyleImage();
   if (eCSSUnit_Image == imageValue->GetUnit()) {
     NS_SET_IMAGE_REQUEST_WITH_DOC(list->SetListStyleImage,
                                   aContext,
                                   imageValue->GetImageValue)
   }
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -20,16 +20,17 @@
 
 #include "nsCOMPtr.h"
 
 #include "nsBidiUtils.h"
 #include "nsLayoutUtils.h"
 
 #include "imgIRequest.h"
 #include "imgIContainer.h"
+#include "CounterStyleManager.h"
 
 #include "mozilla/Likely.h"
 #include "nsIURI.h"
 #include "nsIDocument.h"
 #include <algorithm>
 
 static_assert((((1 << nsStyleStructID_Length) - 1) &
                ~(NS_STYLE_INHERIT_MASK)) == 0,
@@ -646,31 +647,34 @@ nsChangeHint nsStyleOutline::CalcDiffere
     return nsChangeHint_RepaintFrame;
   }
   return NS_STYLE_HINT_NONE;
 }
 
 // --------------------
 // nsStyleList
 //
-nsStyleList::nsStyleList() 
-  : mListStyleType(NS_STYLE_LIST_STYLE_DISC),
-    mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE)
+nsStyleList::nsStyleList(nsPresContext* aPresContext) 
+  : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE),
+    mListStyleType(NS_LITERAL_STRING("disc")),
+    mCounterStyle(aPresContext->CounterStyleManager()->
+                  BuildCounterStyle(mListStyleType))
 {
   MOZ_COUNT_CTOR(nsStyleList);
 }
 
 nsStyleList::~nsStyleList() 
 {
   MOZ_COUNT_DTOR(nsStyleList);
 }
 
 nsStyleList::nsStyleList(const nsStyleList& aSource)
-  : mListStyleType(aSource.mListStyleType),
-    mListStylePosition(aSource.mListStylePosition),
+  : mListStylePosition(aSource.mListStylePosition),
+    mListStyleType(aSource.mListStyleType),
+    mCounterStyle(aSource.mCounterStyle),
     mImageRegion(aSource.mImageRegion)
 {
   SetListStyleImage(aSource.GetListStyleImage());
   MOZ_COUNT_CTOR(nsStyleList);
 }
 
 nsChangeHint nsStyleList::CalcDifference(const nsStyleList& aOther) const
 {
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -22,16 +22,17 @@
 #include "nsChangeHint.h"
 #include "nsPresContext.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsTArray.h"
 #include "nsCSSValue.h"
 #include "imgRequestProxy.h"
 #include "Orientation.h"
+#include "CounterStyleManager.h"
 
 class nsIFrame;
 class nsIURI;
 class imgIContainer;
 
 // Includes nsStyleStructID.
 #include "nsStyleStructFwd.h"
 
@@ -1103,17 +1104,17 @@ protected:
   bool          mHasCachedOutline;
   uint8_t       mOutlineStyle;    // [reset] See nsStyleConsts.h
 
   nscoord       mTwipsPerPixel;
 };
 
 
 struct nsStyleList {
-  nsStyleList(void);
+  nsStyleList(nsPresContext* aPresContext);
   nsStyleList(const nsStyleList& aStyleList);
   ~nsStyleList(void);
 
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
     return aContext->AllocateFromShell(sz);
   }
   void Destroy(nsPresContext* aContext) {
     this->~nsStyleList();
@@ -1136,19 +1137,38 @@ struct nsStyleList {
   {
     if (mListStyleImage)
       mListStyleImage->UnlockImage();
     mListStyleImage = aReq;
     if (mListStyleImage)
       mListStyleImage->LockImage();
   }
 
-  uint8_t   mListStyleType;             // [inherited] See nsStyleConsts.h
+  void GetListStyleType(nsSubstring& aType) const { aType = mListStyleType; }
+  mozilla::CounterStyle* GetCounterStyle() const
+  {
+    return mCounterStyle.get();
+  }
+  void SetListStyleType(const nsSubstring& aType,
+                        mozilla::CounterStyle* aStyle)
+  {
+    mListStyleType = aType;
+    mCounterStyle = aStyle;
+  }
+  void SetListStyleType(const nsSubstring& aType,
+                        nsPresContext* aPresContext)
+  {
+    SetListStyleType(aType, aPresContext->
+                     CounterStyleManager()->BuildCounterStyle(aType));
+  }
+
   uint8_t   mListStylePosition;         // [inherited]
 private:
+  nsString  mListStyleType;             // [inherited]
+  nsRefPtr<mozilla::CounterStyle> mCounterStyle; // [inherited]
   nsRefPtr<imgRequestProxy> mListStyleImage; // [inherited]
   nsStyleList& operator=(const nsStyleList& aOther) MOZ_DELETE;
 public:
   nsRect        mImageRegion;           // [inherited] the rect to use within an image
 };
 
 // Computed value of the grid-template-columns or grid-columns-rows property
 // (but *not* grid-template-areas.)
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -2610,30 +2610,30 @@ var gCSSProperties = {
     invalid_values: []
   },
   "list-style": {
     domProp: "listStyle",
     inherited: true,
     type: CSS_TYPE_TRUE_SHORTHAND,
     subproperties: [ "list-style-type", "list-style-position", "list-style-image" ],
     initial_values: [ "outside", "disc", "disc outside", "outside disc", "disc none", "none disc", "none disc outside", "none outside disc", "disc none outside", "disc outside none", "outside none disc", "outside disc none" ],
-    other_values: [ "inside none", "none inside", "none none inside", "square", "none", "none none", "outside none none", "none outside none", "none none outside", "none outside", "outside none",
+    other_values: [ "inside none", "none inside", "none none inside", "square", "none", "none none", "outside none none", "none outside none", "none none outside", "none outside", "outside none", "outside outside", "outside inside", "\\32 style", "\\32 style inside",
       'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")',
       'none url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")',
       'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==") none',
       'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==") outside',
       'outside url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")',
       'outside none url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")',
       'outside url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==") none',
       'none url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==") outside',
       'none outside url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")',
       'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==") outside none',
       'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==") none outside'
     ],
-    invalid_values: [ "outside outside", "disc disc", "unknown value", "none none none", "none disc url(404.png)", "none url(404.png) disc", "disc none url(404.png)", "disc url(404.png) none", "url(404.png) none disc", "url(404.png) disc none", "none disc outside url(404.png)" ]
+    invalid_values: [ "disc disc", "unknown value", "none none none", "none disc url(404.png)", "none url(404.png) disc", "disc none url(404.png)", "disc url(404.png) none", "url(404.png) none disc", "url(404.png) disc none", "none disc outside url(404.png)" ]
   },
   "list-style-image": {
     domProp: "listStyleImage",
     inherited: true,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "none" ],
     other_values: [ 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")',
       // Add some tests for interesting url() values here to test serialization, etc.
@@ -2678,17 +2678,18 @@ var gCSSProperties = {
       "-moz-arabic-indic", "-moz-persian", "-moz-urdu",
       "-moz-devanagari", "-moz-gurmukhi", "-moz-gujarati",
       "-moz-oriya", "-moz-kannada", "-moz-malayalam", "-moz-bengali",
       "-moz-tamil", "-moz-telugu", "-moz-thai", "-moz-lao",
       "-moz-myanmar", "-moz-khmer",
       "-moz-hangul", "-moz-hangul-consonant",
       "-moz-ethiopic-halehame", "-moz-ethiopic-numeric",
       "-moz-ethiopic-halehame-am",
-      "-moz-ethiopic-halehame-ti-er", "-moz-ethiopic-halehame-ti-et"
+      "-moz-ethiopic-halehame-ti-er", "-moz-ethiopic-halehame-ti-et",
+      "other-style", "inside", "outside", "\\32 style"
     ],
     invalid_values: []
   },
   "margin": {
     domProp: "margin",
     inherited: false,
     type: CSS_TYPE_TRUE_SHORTHAND,
     subproperties: [ "margin-top", "margin-right", "margin-bottom", "margin-left" ],
--- a/layout/style/test/test_garbage_at_end_of_declarations.html
+++ b/layout/style/test/test_garbage_at_end_of_declarations.html
@@ -45,16 +45,23 @@ var gDeclaration = gElement.style;
  */
 var gAllowsExtra = {
   "counter-increment": { "none": true },
   "counter-reset": { "none": true },
   "font-family": {},
   "font": { "caption": true, "icon": true, "menu": true, "message-box": true,
             "small-caption": true, "status-bar": true },
   "voice-family": {},
+  "list-style": {
+    "inside none": true, "none inside": true, "none": true,
+    "none outside": true, "outside none": true,
+    'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")': true,
+    'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==") outside': true,
+    'outside url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")': true
+  },
 };
 
 /* These are the reverse of the above list; they're the unusual values
    that do allow extra keywords afterwards */
 var gAllowsExtraUnusual = {
   "transition": { "all": true, "0s": true, "0s 0s": true, "ease": true,
                   "1s 2s linear": true, "1s linear 2s": true,
                   "linear 1s 2s": true, "linear 1s": true,