Bug 982355 - Support disclosure-{open,closed} counter styles. r=jfkthame
authorXidorn Quan <quanxunzhen@gmail.com>
Fri, 13 Jun 2014 17:34:00 +0200
changeset 210787 9603399b9ee1a95a7e2359d3c878e41cc6012ed2
parent 210786 2ab8afeb4b2d3b95c5dacd35b687206eb047b6e4
child 210788 c3475bd79048ad16b24b8a1075476261592276d1
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)
reviewersjfkthame
bugs982355
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 982355 - Support disclosure-{open,closed} counter styles. r=jfkthame
layout/base/nsCounterManager.cpp
layout/generic/nsBulletFrame.cpp
layout/generic/nsBulletFrame.h
layout/generic/nsHTMLReflowState.cpp
layout/reftests/counter-style/disclosure-styles-ref.html
layout/reftests/counter-style/disclosure-styles.html
layout/reftests/counter-style/reftest.list
layout/style/CounterStyleManager.cpp
layout/style/CounterStyleManager.h
layout/style/nsCSSKeywordList.h
layout/style/nsCSSProps.cpp
layout/style/nsStyleConsts.h
layout/style/test/property_database.js
--- a/layout/base/nsCounterManager.cpp
+++ b/layout/base/nsCounterManager.cpp
@@ -7,16 +7,17 @@
 /* implementation of CSS counters (for numbering things) */
 
 #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"
+#include "WritingModes.h"
 
 using namespace mozilla;
 
 bool
 nsCounterUseNode::InitTextFrame(nsGenConList* aList,
         nsIFrame* aPseudoFrame, nsIFrame* aTextFrame)
 {
   nsCounterNode::InitTextFrame(aList, aPseudoFrame, aTextFrame);
@@ -90,21 +91,23 @@ nsCounterUseNode::GetText(nsString& aRes
         for (nsCounterNode *n = mScopeStart; n->mScopePrev; n = n->mScopeStart)
             stack.AppendElement(n->mScopePrev);
 
     const char16_t* separator;
     if (mAllCounters)
         separator = mCounterFunction->Item(1).GetStringBufferValue();
 
     CounterStyle* style = GetCounterStyle();
+    WritingMode wm = mPseudoFrame ?
+        mPseudoFrame->GetWritingMode() : WritingMode();
     for (uint32_t i = stack.Length() - 1;; --i) {
         nsCounterNode *n = stack[i];
         nsAutoString text;
         bool isTextRTL;
-        style->GetCounterText(n->mValueAfter, text, isTextRTL);
+        style->GetCounterText(n->mValueAfter, wm, text, isTextRTL);
         aResult.Append(text);
         if (i == 0)
             break;
         NS_ASSERTION(mAllCounters, "yikes, separator is uninitialized");
         aResult.Append(separator);
     }
 }
 
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -347,16 +347,62 @@ 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_DISCLOSURE_CLOSED:
+  case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
+    {
+      nsRect rect(aPt, mRect.Size());
+      rect.Deflate(mPadding);
+
+      WritingMode wm = GetWritingMode();
+      bool isVertical = wm.IsVertical();
+      bool isClosed =
+        listStyleType->GetStyle() == NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED;
+      bool isDown = (!isVertical && !isClosed) || (isVertical && isClosed);
+      nscoord diff = NSToCoordRound(0.1f * rect.height);
+      if (isDown) {
+        rect.y += diff * 2;
+        rect.height -= diff * 2;
+      } else {
+        rect.Deflate(diff, 0);
+      }
+      nsPresContext *pc = PresContext();
+      rect.x = pc->RoundAppUnitsToNearestDevPixels(rect.x);
+      rect.y = pc->RoundAppUnitsToNearestDevPixels(rect.y);
+
+      nsPoint points[3];
+      if (isDown) {
+        // to bottom
+        points[0] = rect.TopLeft();
+        points[1] = rect.TopRight();
+        points[2] = (rect.BottomLeft() + rect.BottomRight()) / 2;
+      } else {
+        bool isLR = isVertical ? wm.IsVerticalLR() : wm.IsBidiLTR();
+        if (isLR) {
+          // to right
+          points[0] = rect.TopLeft();
+          points[1] = (rect.TopRight() + rect.BottomRight()) / 2;
+          points[2] = rect.BottomLeft();
+        } else {
+          // to left
+          points[0] = rect.TopRight();
+          points[1] = rect.BottomRight();
+          points[2] = (rect.TopLeft() + rect.BottomLeft()) / 2;
+        }
+      }
+      aRenderingContext.FillPolygon(points, 3);
+    }
+    break;
+
   default:
     nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
                                           GetFontSizeInflation());
     GetListItemText(text);
     aRenderingContext.SetFont(fm);
     nscoord ascent = fm->MaxAscent();
     aPt.MoveBy(mPadding.left, mPadding.top);
     aPt.y = NSToCoordRound(nsLayoutUtils::GetSnappedBaselineY(
@@ -406,24 +452,26 @@ nsBulletFrame::SetListItemOrdinal(int32_
 
 void
 nsBulletFrame::GetListItemText(nsAString& aResult)
 {
   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,
+               style->GetStyle() != NS_STYLE_LIST_STYLE_SQUARE &&
+               style->GetStyle() != NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED &&
+               style->GetStyle() != NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN,
                "we should be using specialized code for these types");
 
   bool isRTL;
   nsAutoString counter, prefix, suffix;
   style->GetPrefix(prefix);
   style->GetSuffix(suffix);
-  style->GetCounterText(mOrdinal, counter, isRTL);
+  style->GetCounterText(mOrdinal, GetWritingMode(), counter, isRTL);
 
   aResult.Truncate();
   aResult.Append(prefix);
   if (GetWritingMode().IsBidiLTR() != isRTL) {
     aResult.Append(counter);
   } else {
     // RLM = 0x200f, LRM = 0x200e
     char16_t mark = isRTL ? 0x200f : 0x200e;
@@ -431,16 +479,29 @@ nsBulletFrame::GetListItemText(nsAString
     aResult.Append(counter);
     aResult.Append(mark);
   }
   aResult.Append(suffix);
 }
 
 #define MIN_BULLET_SIZE 1
 
+void
+nsBulletFrame::AppendSpacingToPadding(nsFontMetrics* aFontMetrics)
+{
+  nscoord halfEm = aFontMetrics->EmHeight() / 2;
+  WritingMode wm = GetWritingMode();
+  if (wm.IsVertical()) {
+    mPadding.bottom += halfEm;
+  } else if (wm.IsBidiLTR()) {
+    mPadding.right += halfEm;
+  } else {
+    mPadding.left += halfEm;
+  }
+}
 
 void
 nsBulletFrame::GetDesiredSize(nsPresContext*  aCX,
                               nsRenderingContext *aRenderingContext,
                               nsHTMLReflowMetrics& aMetrics,
                               float aFontSizeInflation)
 {
   // Reset our padding.  If we need it, we'll set it below.
@@ -458,26 +519,17 @@ nsBulletFrame::GetDesiredSize(nsPresCont
     uint32_t status;
     mImageRequest->GetImageStatus(&status);
     if (status & imgIRequest::STATUS_SIZE_AVAILABLE &&
         !(status & imgIRequest::STATUS_ERROR)) {
       // auto size the image
       aMetrics.Width() = mIntrinsicSize.width;
       aMetrics.SetBlockStartAscent(aMetrics.Height() = mIntrinsicSize.height);
 
-      // Add spacing to the padding.
-      nscoord halfEm = fm->EmHeight() / 2;
-      WritingMode wm = GetWritingMode();
-      if (wm.IsVertical()) {
-        mPadding.bottom += halfEm;
-      } else if (wm.IsBidiLTR()) {
-        mPadding.right += halfEm;
-      } else {
-        mPadding.left += halfEm;
-      }
+      AppendSpacingToPadding(fm);
 
       AddStateBits(BULLET_FRAME_IMAGE_LOADING);
 
       return;
     }
   }
 
   // If we're getting our desired size and don't have an image, reset
@@ -501,30 +553,34 @@ nsBulletFrame::GetDesiredSize(nsPresCont
     case NS_STYLE_LIST_STYLE_CIRCLE:
     case NS_STYLE_LIST_STYLE_SQUARE: {
       ascent = fm->MaxAscent();
       bulletSize = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
                           NSToCoordRound(0.8f * (float(ascent) / 2.0f)));
       mPadding.bottom = NSToCoordRound(float(ascent) / 8.0f);
       aMetrics.Width() = aMetrics.Height() = bulletSize;
       aMetrics.SetBlockStartAscent(bulletSize + mPadding.bottom);
-
-      // Add spacing to the padding.
-      nscoord halfEm = fm->EmHeight() / 2;
-      WritingMode wm = GetWritingMode();
-      if (wm.IsVertical()) {
-        mPadding.bottom += halfEm;
-      } else if (wm.IsBidiLTR()) {
-        mPadding.right += halfEm;
-      } else {
-        mPadding.left += halfEm;
-      }
+      AppendSpacingToPadding(fm);
       break;
     }
 
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
+      ascent = fm->EmAscent();
+      bulletSize = std::max(
+          nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
+          NSToCoordRound(0.75f * ascent));
+      mPadding.bottom = NSToCoordRound(0.125f * ascent);
+      aMetrics.Width() = aMetrics.Height() = bulletSize;
+      if (!GetWritingMode().IsVertical()) {
+        aMetrics.SetBlockStartAscent(bulletSize + mPadding.bottom);
+      }
+      AppendSpacingToPadding(fm);
+      break;
+
     default:
       GetListItemText(text);
       aMetrics.Height() = fm->MaxHeight();
       aRenderingContext->SetFont(fm);
       aMetrics.Width() =
         nsLayoutUtils::GetStringWidth(this, aRenderingContext,
                                       text.get(), text.Length());
       aMetrics.SetBlockStartAscent(fm->MaxAscent());
@@ -744,30 +800,40 @@ nsBulletFrame::GetBaseline() const
       case NS_STYLE_LIST_STYLE_SQUARE:
         ascent = fm->MaxAscent();
         bottomPadding = NSToCoordRound(float(ascent) / 8.0f);
         ascent = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
                         NSToCoordRound(0.8f * (float(ascent) / 2.0f)));
         ascent += bottomPadding;
         break;
 
+      case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
+      case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
+        ascent = fm->EmAscent();
+        bottomPadding = NSToCoordRound(0.125f * ascent);
+        ascent = std::max(
+            nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
+            NSToCoordRound(0.75f * ascent));
+        ascent += bottomPadding;
+        break;
+
       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);
+  style->GetSpokenCounterText(mOrdinal, GetWritingMode(), aText, isBullet);
   if (isBullet) {
     aText.Append(' ');
   } else {
     nsAutoString prefix, suffix;
     style->GetPrefix(prefix);
     style->GetSuffix(suffix);
     aText = prefix + aText + suffix;
   }
--- a/layout/generic/nsBulletFrame.h
+++ b/layout/generic/nsBulletFrame.h
@@ -96,16 +96,17 @@ public:
 
   int32_t GetOrdinal() { return mOrdinal; }
 
   already_AddRefed<imgIContainer> GetImage() const;
 
 protected:
   nsresult OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
 
+  void AppendSpacingToPadding(nsFontMetrics* aFontMetrics);
   void GetDesiredSize(nsPresContext* aPresContext,
                       nsRenderingContext *aRenderingContext,
                       nsHTMLReflowMetrics& aMetrics,
                       float aFontSizeInflation);
 
   void GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup);
 
   nsMargin mPadding;
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -114,17 +114,19 @@ FontSizeInflationListMarginAdjustment(co
     if (inflation > 1.0f &&
         blockFrame->HasBullet() &&
         inflation > 1.0f) {
 
       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) {
+          listStyleType != NS_STYLE_LIST_STYLE_SQUARE &&
+          listStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED &&
+          listStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN) {
         // 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);
       }
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/counter-style/disclosure-styles-ref.html
@@ -0,0 +1,34 @@
+<style type="text/css">
+  @counter-style disclosure-closed2-ltr {
+    system: cyclic;
+    symbols: \25b8;
+    suffix: ' ';
+  }
+  @counter-style disclosure-closed2-rtl {
+    system: cyclic;
+    symbols: \25c2;
+    suffix: ' ';
+  }
+  @counter-style disclosure-open2 {
+    system: cyclic;
+    symbols: \25be;
+    suffix: ' ';
+  }
+  .open { list-style-type: disclosure-open2; }
+  .closed:-moz-dir(ltr) { list-style-type: disclosure-closed2-ltr; }
+  .closed:-moz-dir(rtl) { list-style-type: disclosure-closed2-rtl; }
+  ul {
+    padding: 0;
+    list-style-position: inside;
+  }
+</style>
+<ul dir="ltr">
+  <li class="closed">closed ltr
+  <li class="open">open ltr
+</ul>
+<ul dir="rtl">
+  <li class="closed">closed rtl
+  <li class="open">open rtl
+</ul>
+<p dir="ltr">&#x25b8;&nbsp;closed ltr
+<p dir="rtl">&#x25c2;&nbsp;closed rtl
new file mode 100644
--- /dev/null
+++ b/layout/reftests/counter-style/disclosure-styles.html
@@ -0,0 +1,27 @@
+<style type="text/css">
+  @counter-style disclosure-closed2 {
+    system: extends disclosure-closed;
+  }
+  @counter-style disclosure-open2 {
+    system: extends disclosure-open;
+  }
+  ul {
+    padding: 0;
+    list-style-position: inside;
+  }
+  .closed { list-style-type: disclosure-closed2; }
+  .open { list-style-type: disclosure-open2; }
+  p::before {
+    content: counter(a, disclosure-closed) " ";
+  }
+</style>
+<ul dir="ltr">
+  <li class="closed">closed ltr
+  <li class="open">open ltr
+</ul>
+<ul dir="rtl">
+  <li class="closed">closed rtl
+  <li class="open">open rtl
+</ul>
+<p dir="ltr">closed ltr
+<p dir="rtl">closed rtl
--- a/layout/reftests/counter-style/reftest.list
+++ b/layout/reftests/counter-style/reftest.list
@@ -25,8 +25,9 @@ fails-if(B2G) == descriptor-suffix.html 
 == descriptor-range-invalid.html    descriptor-range-invalid-ref.html
 == descriptor-pad-invalid.html      descriptor-pad-invalid-ref.html
 == descriptor-fallback.html         descriptor-fallback-ref.html
 == descriptor-symbols-invalid.html  descriptor-symbols-invalid-ref.html
 == name-case-sensitivity.html       name-case-sensitivity-ref.html
 == dependent-builtin.html           dependent-builtin-ref.html
 == redefine-builtin.html            redefine-builtin-ref.html
 == redefine-attr-mapping.html       redefine-attr-mapping-ref.html
+== disclosure-styles.html           disclosure-styles-ref.html
--- a/layout/style/CounterStyleManager.cpp
+++ b/layout/style/CounterStyleManager.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/ArrayUtils.h"
 #include "prprf.h"
 #include "nsString.h"
 #include "nsStyleSet.h"
 #include "nsCSSRules.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"
 #include "nsUnicodeProperties.h"
+#include "WritingModes.h"
 
 namespace mozilla {
 
 struct AdditiveSymbol
 {
   CounterValue weight;
   nsString symbol;
 };
@@ -575,32 +576,35 @@ protected:
     : CounterStyle(aStyle)
   {
   }
 
 public:
   virtual void GetPrefix(nsSubstring& aResult) MOZ_OVERRIDE;
   virtual void GetSuffix(nsSubstring& aResult) MOZ_OVERRIDE;
   virtual void GetSpokenCounterText(CounterValue aOrdinal,
+                                    WritingMode aWritingMode,
                                     nsSubstring& aResult,
                                     bool& aIsBullet) MOZ_OVERRIDE;
   virtual bool IsBullet() MOZ_OVERRIDE;
 
   virtual void GetNegative(NegativeType& aResult) MOZ_OVERRIDE;
   virtual bool IsOrdinalInRange(CounterValue aOrdinal) MOZ_OVERRIDE;
   virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) MOZ_OVERRIDE;
   virtual void GetPad(PadType& aResult) MOZ_OVERRIDE;
   virtual CounterStyle* GetFallback() MOZ_OVERRIDE;
   virtual uint8_t GetSpeakAs() MOZ_OVERRIDE;
   virtual bool UseNegativeSign() MOZ_OVERRIDE;
 
   virtual void CallFallbackStyle(CounterValue aOrdinal,
+                                 WritingMode aWritingMode,
                                  nsSubstring& aResult,
                                  bool& aIsRTL) MOZ_OVERRIDE;
   virtual bool GetInitialCounterText(CounterValue aOrdinal,
+                                     WritingMode aWritingMode,
                                      nsSubstring& aResult,
                                      bool& aIsRTL) MOZ_OVERRIDE;
 
   // Builtin counter style does not need refcount at all
   NS_IMETHOD_(MozExternalRefCountType) AddRef() MOZ_OVERRIDE { return 2; }
   NS_IMETHOD_(MozExternalRefCountType) Release() MOZ_OVERRIDE { return 2; }
 };
 
@@ -616,16 +620,18 @@ BuiltinCounterStyle::GetSuffix(nsSubstri
   switch (mStyle) {
     case NS_STYLE_LIST_STYLE_NONE:
       aResult.Truncate();
       break;
  
     case NS_STYLE_LIST_STYLE_DISC:
     case NS_STYLE_LIST_STYLE_CIRCLE:
     case NS_STYLE_LIST_STYLE_SQUARE:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
       aResult = ' ';
       break;
 
     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
     case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
@@ -640,57 +646,58 @@ BuiltinCounterStyle::GetSuffix(nsSubstri
       break;
 
     default:
       aResult.AssignLiteral(MOZ_UTF16(". "));
       break;
   }
 }
 
-// XXX stolen from nsBlockFrame.cpp
 static const char16_t kDiscCharacter = 0x2022;
 static const char16_t kCircleCharacter = 0x25e6;
 static const char16_t kSquareCharacter = 0x25fe;
+static const char16_t kRightPointingCharacter = 0x25b8;
+static const char16_t kLeftPointingCharacter = 0x25c2;
+static const char16_t kDownPointingCharacter = 0x25be;
 
 /* virtual */ void
 BuiltinCounterStyle::GetSpokenCounterText(CounterValue aOrdinal,
+                                          WritingMode aWritingMode,
                                           nsSubstring& aResult,
                                           bool& aIsBullet)
 {
   switch (mStyle) {
     case NS_STYLE_LIST_STYLE_NONE:
-      aResult.Truncate();
-      aIsBullet = true;
-      break;
     case NS_STYLE_LIST_STYLE_DISC:
-      aResult.Assign(kDiscCharacter);
+    case NS_STYLE_LIST_STYLE_CIRCLE:
+    case NS_STYLE_LIST_STYLE_SQUARE:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN: {
+      // Same as the initial representation
+      bool isRTL;
+      GetInitialCounterText(aOrdinal, aWritingMode, aResult, isRTL);
       aIsBullet = true;
       break;
-    case NS_STYLE_LIST_STYLE_CIRCLE:
-      aResult.Assign(kCircleCharacter);
-      aIsBullet = true;
-      break;
-    case NS_STYLE_LIST_STYLE_SQUARE:
-      aResult.Assign(kSquareCharacter);
-      aIsBullet = true;
-      break;
+    }
     default:
-      CounterStyle::GetSpokenCounterText(aOrdinal, aResult, aIsBullet);
-      aIsBullet = false;
+      CounterStyle::GetSpokenCounterText(
+          aOrdinal, aWritingMode, aResult, aIsBullet);
       break;
   }
 }
 
 /* virtual */ bool
 BuiltinCounterStyle::IsBullet()
 {
   switch (mStyle) {
     case NS_STYLE_LIST_STYLE_DISC:
     case NS_STYLE_LIST_STYLE_CIRCLE:
     case NS_STYLE_LIST_STYLE_SQUARE:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
       return true;
     default:
       return false;
   }
 }
 
 static const char16_t gJapaneseNegative[] = {
   0x30de, 0x30a4, 0x30ca, 0x30b9, 0x0000
@@ -741,16 +748,18 @@ BuiltinCounterStyle::IsOrdinalInRange(Co
 {
   switch (mStyle) {
     default:
     // cyclic
     case NS_STYLE_LIST_STYLE_NONE:
     case NS_STYLE_LIST_STYLE_DISC:
     case NS_STYLE_LIST_STYLE_CIRCLE:
     case NS_STYLE_LIST_STYLE_SQUARE:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
     // use DecimalToText
     case NS_STYLE_LIST_STYLE_DECIMAL:
     // use CJKIdeographicToText
     case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
     case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
     case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
@@ -778,16 +787,18 @@ BuiltinCounterStyle::IsOrdinalInRange(Co
 BuiltinCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal)
 {
   switch (mStyle) {
     // cyclic:
     case NS_STYLE_LIST_STYLE_NONE:
     case NS_STYLE_LIST_STYLE_DISC:
     case NS_STYLE_LIST_STYLE_CIRCLE:
     case NS_STYLE_LIST_STYLE_SQUARE:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
     // numeric:
     case NS_STYLE_LIST_STYLE_DECIMAL:
       return true;
 
     // additive:
     case NS_STYLE_LIST_STYLE_HEBREW:
       return aOrdinal >= 0;
 
@@ -829,46 +840,52 @@ BuiltinCounterStyle::GetFallback()
 /* virtual */ uint8_t
 BuiltinCounterStyle::GetSpeakAs()
 {
   switch (mStyle) {
     case NS_STYLE_LIST_STYLE_NONE:
     case NS_STYLE_LIST_STYLE_DISC:
     case NS_STYLE_LIST_STYLE_CIRCLE:
     case NS_STYLE_LIST_STYLE_SQUARE:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
       return NS_STYLE_COUNTER_SPEAKAS_BULLETS;
     default:
       return NS_STYLE_COUNTER_SPEAKAS_NUMBERS;
   }
 }
 
 /* virtual */ bool
 BuiltinCounterStyle::UseNegativeSign()
 {
   switch (mStyle) {
     case NS_STYLE_LIST_STYLE_NONE:
     case NS_STYLE_LIST_STYLE_DISC:
     case NS_STYLE_LIST_STYLE_CIRCLE:
     case NS_STYLE_LIST_STYLE_SQUARE:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
       return false;
     default:
       return true;
   }
 }
 
 /* virtual */ void
 BuiltinCounterStyle::CallFallbackStyle(CounterValue aOrdinal,
+                                       WritingMode aWritingMode,
                                        nsSubstring& aResult,
                                        bool& aIsRTL)
 {
-  GetFallback()->GetCounterText(aOrdinal, aResult, aIsRTL);
+  GetFallback()->GetCounterText(aOrdinal, aWritingMode, aResult, aIsRTL);
 }
 
 /* virtual */ bool
 BuiltinCounterStyle::GetInitialCounterText(CounterValue aOrdinal,
+                                           WritingMode aWritingMode,
                                            nsSubstring& aResult,
                                            bool& aIsRTL)
 {
   aIsRTL = false;
   switch (mStyle) {
     // used by counters & extends counter-style code only
     // XXX We really need to do this the same way we do list bullets.
     case NS_STYLE_LIST_STYLE_NONE:
@@ -878,16 +895,34 @@ BuiltinCounterStyle::GetInitialCounterTe
       aResult.Assign(kDiscCharacter);
       return true;
     case NS_STYLE_LIST_STYLE_CIRCLE:
       aResult.Assign(kCircleCharacter);
       return true;
     case NS_STYLE_LIST_STYLE_SQUARE:
       aResult.Assign(kSquareCharacter);
       return true;
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
+      if (aWritingMode.IsVertical()) {
+        aResult.Assign(kDownPointingCharacter);
+      } else if (aWritingMode.IsBidiLTR()) {
+        aResult.Assign(kRightPointingCharacter);
+      } else {
+        aResult.Assign(kLeftPointingCharacter);
+      }
+      return true;
+    case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
+      if (!aWritingMode.IsVertical()) {
+        aResult.Assign(kDownPointingCharacter);
+      } else if (aWritingMode.IsVerticalLR()) {
+        aResult.Assign(kRightPointingCharacter);
+      } else {
+        aResult.Assign(kLeftPointingCharacter);
+      }
+      return true;
 
     case NS_STYLE_LIST_STYLE_DECIMAL:
       return DecimalToText(aOrdinal, aResult);
 
     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
       return CJKIdeographicToText(aOrdinal, aResult, gDataTradChineseInformal);
     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
       return CJKIdeographicToText(aOrdinal, aResult, gDataTradChineseFormal);
@@ -998,32 +1033,35 @@ public:
   void ResetDependentData();
 
   nsCSSCounterStyleRule* GetRule() const { return mRule; }
   uint32_t GetRuleGeneration() const { return mRuleGeneration; }
 
   virtual void GetPrefix(nsSubstring& aResult) MOZ_OVERRIDE;
   virtual void GetSuffix(nsSubstring& aResult) MOZ_OVERRIDE;
   virtual void GetSpokenCounterText(CounterValue aOrdinal,
+                                    WritingMode aWritingMode,
                                     nsSubstring& aResult,
                                     bool& aIsBullet) MOZ_OVERRIDE;
   virtual bool IsBullet() MOZ_OVERRIDE;
 
   virtual void GetNegative(NegativeType& aResult) MOZ_OVERRIDE;
   virtual bool IsOrdinalInRange(CounterValue aOrdinal) MOZ_OVERRIDE;
   virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) MOZ_OVERRIDE;
   virtual void GetPad(PadType& aResult) MOZ_OVERRIDE;
   virtual CounterStyle* GetFallback() MOZ_OVERRIDE;
   virtual uint8_t GetSpeakAs() MOZ_OVERRIDE;
   virtual bool UseNegativeSign() MOZ_OVERRIDE;
 
   virtual void CallFallbackStyle(CounterValue aOrdinal,
+                                 WritingMode aWritingMode,
                                  nsSubstring& aResult,
                                  bool& aIsRTL) MOZ_OVERRIDE;
   virtual bool GetInitialCounterText(CounterValue aOrdinal,
+                                     WritingMode aWritingMode,
                                      nsSubstring& aResult,
                                      bool& aIsRTL) MOZ_OVERRIDE;
 
   bool IsExtendsSystem()
   {
     return mSystem == NS_STYLE_COUNTER_SYSTEM_EXTENDS;
   }
 
@@ -1173,25 +1211,28 @@ CustomCounterStyle::GetSuffix(nsSubstrin
       mSuffix.AssignLiteral(MOZ_UTF16(". "));
     }
   }
   aResult = mSuffix;
 }
 
 /* virtual */ void
 CustomCounterStyle::GetSpokenCounterText(CounterValue aOrdinal,
+                                         WritingMode aWritingMode,
                                          nsSubstring& aResult,
                                          bool& aIsBullet)
 {
   if (GetSpeakAs() != NS_STYLE_COUNTER_SPEAKAS_OTHER) {
-    CounterStyle::GetSpokenCounterText(aOrdinal, aResult, aIsBullet);
+    CounterStyle::GetSpokenCounterText(
+        aOrdinal, aWritingMode, aResult, aIsBullet);
   } else {
     NS_ABORT_IF_FALSE(mSpeakAsCounter,
                       "mSpeakAsCounter should have been initialized.");
-    mSpeakAsCounter->GetSpokenCounterText(aOrdinal, aResult, aIsBullet);
+    mSpeakAsCounter->GetSpokenCounterText(
+        aOrdinal, aWritingMode, aResult, aIsBullet);
   }
 }
 
 /* virtual */ bool
 CustomCounterStyle::IsBullet()
 {
   switch (mSystem) {
     case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
@@ -1346,29 +1387,31 @@ CustomCounterStyle::UseNegativeSign()
       return GetExtendsRoot()->UseNegativeSign();
     default:
       return false;
   }
 }
 
 /* virtual */ void
 CustomCounterStyle::CallFallbackStyle(CounterValue aOrdinal,
+                                      WritingMode aWritingMode,
                                       nsSubstring& aResult,
                                       bool& aIsRTL)
 {
   CounterStyle* fallback = GetFallback();
   // If it recursively falls back to this counter style again,
   // it will then fallback to decimal to break the loop.
   mFallback = CounterStyleManager::GetDecimalStyle();
-  fallback->GetCounterText(aOrdinal, aResult, aIsRTL);
+  fallback->GetCounterText(aOrdinal, aWritingMode, aResult, aIsRTL);
   mFallback = fallback;
 }
 
 /* virtual */ bool
 CustomCounterStyle::GetInitialCounterText(CounterValue aOrdinal,
+                                          WritingMode aWritingMode,
                                           nsSubstring& aResult,
                                           bool& aIsRTL)
 {
   switch (mSystem) {
     case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
       return GetCyclicCounterText(aOrdinal, aResult, GetSymbols());
     case NS_STYLE_COUNTER_SYSTEM_FIXED: {
       int32_t start = mRule->GetSystemArgument().GetIntValue();
@@ -1379,17 +1422,17 @@ CustomCounterStyle::GetInitialCounterTex
     case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
       return GetAlphabeticCounterText(aOrdinal, aResult, GetSymbols());
     case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
       return GetNumericCounterText(aOrdinal, aResult, GetSymbols());
     case NS_STYLE_COUNTER_SYSTEM_ADDITIVE:
       return GetAdditiveCounterText(aOrdinal, aResult, GetAdditiveSymbols());
     case NS_STYLE_COUNTER_SYSTEM_EXTENDS:
       return GetExtendsRoot()->
-        GetInitialCounterText(aOrdinal, aResult, aIsRTL);
+        GetInitialCounterText(aOrdinal, aWritingMode, aResult, aIsRTL);
     default:
       NS_NOTREACHED("Invalid system.");
       return false;
   }
 }
 
 const nsTArray<nsString>&
 CustomCounterStyle::GetSymbols()
@@ -1664,16 +1707,17 @@ CountGraphemeClusters(const nsSubstring&
     ++result;
     iter.Next();
   }
   return result;
 }
 
 void
 CounterStyle::GetCounterText(CounterValue aOrdinal,
+                             WritingMode aWritingMode,
                              nsSubstring& aResult,
                              bool& aIsRTL)
 {
   bool success = IsOrdinalInRange(aOrdinal);
   aIsRTL = false;
 
   if (success) {
     // generate initial representation
@@ -1682,17 +1726,18 @@ CounterStyle::GetCounterText(CounterValu
     CounterValue ordinal;
     if (!useNegativeSign) {
       ordinal = aOrdinal;
     } else {
       CheckedInt<CounterValue> absolute(Abs(aOrdinal));
       ordinal = absolute.isValid() ?
         absolute.value() : std::numeric_limits<CounterValue>::max();
     }
-    success = GetInitialCounterText(ordinal, initialText, aIsRTL);
+    success = GetInitialCounterText(
+        ordinal, aWritingMode, initialText, aIsRTL);
 
     // add pad & negative, build the final result
     if (success) {
       PadType pad;
       GetPad(pad);
       // We have to calculate the difference here since suffix part of negative
       // sign may be appended to initialText later.
       int32_t diff = pad.width - CountGraphemeClusters(initialText);
@@ -1718,22 +1763,23 @@ CounterStyle::GetCounterText(CounterValu
       }
       if (success) {
         aResult.Append(initialText);
       }
     }
   }
 
   if (!success) {
-    CallFallbackStyle(aOrdinal, aResult, aIsRTL);
+    CallFallbackStyle(aOrdinal, aWritingMode, aResult, aIsRTL);
   }
 }
 
 /* virtual */ void
 CounterStyle::GetSpokenCounterText(CounterValue aOrdinal,
+                                   WritingMode aWritingMode,
                                    nsSubstring& aResult,
                                    bool& aIsBullet)
 {
   bool isRTL; // we don't care about direction for spoken text
   aIsBullet = false;
   switch (GetSpeakAs()) {
     case NS_STYLE_COUNTER_SPEAKAS_BULLETS:
       aResult.Assign(kDiscCharacter);
@@ -1741,17 +1787,17 @@ CounterStyle::GetSpokenCounterText(Count
       break;
     case NS_STYLE_COUNTER_SPEAKAS_NUMBERS:
       DecimalToText(aOrdinal, aResult);
       break;
     case NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT:
       // we currently do not actually support 'spell-out',
       // so 'words' is used instead.
     case NS_STYLE_COUNTER_SPEAKAS_WORDS:
-      GetCounterText(aOrdinal, aResult, isRTL);
+      GetCounterText(aOrdinal, WritingMode(), aResult, isRTL);
       break;
     case NS_STYLE_COUNTER_SPEAKAS_OTHER:
       // This should be processed by CustomCounterStyle
       NS_NOTREACHED("Invalid speak-as value");
       break;
     default:
       NS_NOTREACHED("Unknown speak-as value");
       break;
--- a/layout/style/CounterStyleManager.h
+++ b/layout/style/CounterStyleManager.h
@@ -15,16 +15,18 @@
 #include "mozilla/NullPtr.h"
 #include "mozilla/Attributes.h"
 
 class nsPresContext;
 class nsCSSCounterStyleRule;
 
 namespace mozilla {
 
+class WritingMode;
+
 typedef int32_t CounterValue;
 
 class CounterStyleManager;
 
 struct NegativeType;
 struct PadType;
 
 class CounterStyle
@@ -46,19 +48,21 @@ public:
   // A style is dependent if it depends on the counter style manager.
   // Custom styles are certainly dependent. In addition, some builtin
   // styles are dependent for fallback.
   bool IsDependentStyle() const;
 
   virtual void GetPrefix(nsSubstring& aResult) = 0;
   virtual void GetSuffix(nsSubstring& aResult) = 0;
   void GetCounterText(CounterValue aOrdinal,
+                      WritingMode aWritingMode,
                       nsSubstring& aResult,
                       bool& aIsRTL);
   virtual void GetSpokenCounterText(CounterValue aOrdinal,
+                                    WritingMode aWritingMode,
                                     nsSubstring& aResult,
                                     bool& aIsBullet);
 
   // XXX This method could be removed once ::-moz-list-bullet and
   //     ::-moz-list-number are completely merged into ::marker.
   virtual bool IsBullet() = 0;
 
   virtual void GetNegative(NegativeType& aResult) = 0;
@@ -75,19 +79,21 @@ public:
    */
   virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) = 0;
   virtual void GetPad(PadType& aResult) = 0;
   virtual CounterStyle* GetFallback() = 0;
   virtual uint8_t GetSpeakAs() = 0;
   virtual bool UseNegativeSign() = 0;
 
   virtual void CallFallbackStyle(CounterValue aOrdinal,
+                                 WritingMode aWritingMode,
                                  nsSubstring& aResult,
                                  bool& aIsRTL) = 0;
   virtual bool GetInitialCounterText(CounterValue aOrdinal,
+                                     WritingMode aWritingMode,
                                      nsSubstring& aResult,
                                      bool& aIsRTL) = 0;
 
   NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
   NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
 
 protected:
   int32_t mStyle;
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -219,16 +219,18 @@ CSS_KEY(decimal, decimal)
 CSS_KEY(default, default)
 CSS_KEY(deg, deg)
 CSS_KEY(diagonal-fractions, diagonal_fractions)
 CSS_KEY(dialog, dialog)
 CSS_KEY(difference, difference)
 CSS_KEY(digits, digits)
 CSS_KEY(disabled, disabled)
 CSS_KEY(disc, disc)
+CSS_KEY(disclosure-closed, disclosure_closed)
+CSS_KEY(disclosure-open, disclosure_open)
 CSS_KEY(discretionary-ligatures, discretionary_ligatures)
 CSS_KEY(dotted, dotted)
 CSS_KEY(double, double)
 CSS_KEY(double-struck, double_struck)
 CSS_KEY(drop-shadow, drop_shadow)
 CSS_KEY(e-resize, e_resize)
 CSS_KEY(ease, ease)
 CSS_KEY(ease-in, ease_in)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1364,16 +1364,18 @@ const KTableValue nsCSSProps::kListStyle
 const KTableValue nsCSSProps::kListStyleKTable[] = {
   // none and decimal are not redefinable, so they should not be moved.
   eCSSKeyword_none, NS_STYLE_LIST_STYLE_NONE,
   eCSSKeyword_decimal, NS_STYLE_LIST_STYLE_DECIMAL,
   // the following graphic styles are processed in a different way.
   eCSSKeyword_disc, NS_STYLE_LIST_STYLE_DISC,
   eCSSKeyword_circle, NS_STYLE_LIST_STYLE_CIRCLE,
   eCSSKeyword_square, NS_STYLE_LIST_STYLE_SQUARE,
+  eCSSKeyword_disclosure_closed, NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED,
+  eCSSKeyword_disclosure_open, NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN,
   // {lower,upper}-{roman,alpha} are also used by html attribute map.
   eCSSKeyword_lower_roman, NS_STYLE_LIST_STYLE_LOWER_ROMAN,
   eCSSKeyword_upper_roman, NS_STYLE_LIST_STYLE_UPPER_ROMAN,
   eCSSKeyword_lower_alpha, NS_STYLE_LIST_STYLE_LOWER_ALPHA,
   eCSSKeyword_upper_alpha, NS_STYLE_LIST_STYLE_UPPER_ALPHA,
   // the following counter styles require specific algorithms to generate.
   eCSSKeyword_hebrew, NS_STYLE_LIST_STYLE_HEBREW,
   eCSSKeyword_japanese_informal, NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL,
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -651,17 +651,19 @@ static inline mozilla::css::Side operato
 #define NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL 13
 #define NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL   14
 #define NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL 15
 #define NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL   16
 #define NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL 17
 #define NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL   18
 #define NS_STYLE_LIST_STYLE_MOZ_TAMIL             19
 #define NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_NUMERIC  20
-#define NS_STYLE_LIST_STYLE__MAX                  21
+#define NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED     21
+#define NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN       22
+#define NS_STYLE_LIST_STYLE__MAX                  23
 
 // See nsStyleList
 #define NS_STYLE_LIST_STYLE_POSITION_INSIDE     0
 #define NS_STYLE_LIST_STYLE_POSITION_OUTSIDE    1
 
 // See nsStyleMargin
 #define NS_STYLE_MARGIN_SIZE_AUTO               0
 
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -2656,16 +2656,17 @@ var gCSSProperties = {
     invalid_values: []
   },
   "list-style-type": {
     domProp: "listStyleType",
     inherited: true,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "disc" ],
     other_values: [ "none", "circle", "square",
+      "disclosure-closed", "disclosure-open",
       "decimal", "decimal-leading-zero",
       "lower-roman", "upper-roman", "lower-greek",
       "lower-alpha", "lower-latin", "upper-alpha", "upper-latin",
       "hebrew", "armenian", "georgian",
       "cjk-decimal", "cjk-ideographic",
       "hiragana", "katakana", "hiragana-iroha", "katakana-iroha",
       "japanese-informal", "japanese-formal", "korean-hangul-formal",
       "korean-hanja-informal", "korean-hanja-formal",