Bug 966166 - Part 5: Move computation code with minor changes. r=dbaron
authorXidorn Quan <quanxunzhen@gmail.com>
Wed, 11 Jun 2014 21:13:00 -0400
changeset 188455 6576396848f1
parent 188454 bffb0b2f7c2f
child 188456 34456c194587
push id26956
push userkwierso@gmail.com
push dateFri, 13 Jun 2014 00:23:52 +0000
treeherdermozilla-central@adcf3f05f813 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
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 5: Move computation code with minor changes. r=dbaron In addition to moving the code, this patch also: * changes the square character from U+25AA to U+25FE to match the spec * changes all the generating functions from using append to using assign * removes negative handling from CJKIdeographicToText since it is no longer used there * fixes indentation and naming conventions * changes buffer size in DecimalToText
accessible/tests/mochitest/common.js
layout/generic/nsBulletFrame.cpp
layout/generic/nsBulletFrame.h
layout/style/CounterStyleManager.cpp
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -78,17 +78,17 @@ const COORDTYPE_SCREEN_RELATIVE = nsIAcc
 const COORDTYPE_WINDOW_RELATIVE = nsIAccessibleCoordinateType.COORDTYPE_WINDOW_RELATIVE;
 const COORDTYPE_PARENT_RELATIVE = nsIAccessibleCoordinateType.COORDTYPE_PARENT_RELATIVE;
 
 const kEmbedChar = String.fromCharCode(0xfffc);
 
 const kDiscBulletChar = String.fromCharCode(0x2022);
 const kDiscBulletText = kDiscBulletChar + " ";
 const kCircleBulletText = String.fromCharCode(0x25e6) + " ";
-const kSquareBulletText = String.fromCharCode(0x25aa) + " ";
+const kSquareBulletText = String.fromCharCode(0x25fe) + " ";
 
 const MAX_TRIM_LENGTH = 100;
 
 /**
  * nsIAccessibleRetrieval service.
  */
 var gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
   getService(nsIAccessibleRetrieval);
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -11,17 +11,16 @@
 #include "nsCOMPtr.h"
 #include "nsGkAtoms.h"
 #include "nsGenericHTMLElement.h"
 #include "nsAttrValueInlines.h"
 #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"
@@ -400,520 +399,16 @@ nsBulletFrame::SetListItemOrdinal(int32_
     }
   }
 
   *aChanged = oldOrdinal != mOrdinal;
 
   return nsCounterManager::IncrementCounter(mOrdinal, aIncrement);
 }
 
-
-/**
- * For all functions below, a return value of true means that we
- * could represent mOrder in the desired numbering system.  false
- * means we had to fall back to decimal
- */
-static bool DecimalToText(int32_t ordinal, nsString& result)
-{
-   char cbuf[40];
-   PR_snprintf(cbuf, sizeof(cbuf), "%ld", ordinal);
-   result.AppendASCII(cbuf);
-   return true;
-}
-static bool TamilToText(int32_t ordinal,  nsString& result)
-{
-   if (ordinal < 1 || ordinal > 9999) {
-     // Can't do those in this system.
-     return false;
-   }
-   char16_t diff = 0x0BE6 - char16_t('0');
-   // We're going to be appending to whatever is in "result" already, so make
-   // sure to only munge the new bits.  Note that we can't just grab the pointer
-   // to the new stuff here, since appending to the string can realloc.
-   size_t offset = result.Length();
-   DecimalToText(ordinal, result); 
-   char16_t* p = result.BeginWriting() + offset;
-   for(; '\0' != *p ; p++) 
-      if(*p != char16_t('0'))
-         *p += diff;
-   return true;
-}
-
-// We know cjk-ideographic need 31 characters to display 99,999,999,999,999,999
-// georgian needs 6 at most
-// armenian needs 12 at most
-// hebrew may need more...
-
-#define NUM_BUF_SIZE 34 
-
-enum CJKIdeographicLang {
-  CHINESE, KOREAN, JAPANESE
-};
-struct CJKIdeographicData {
-  const char16_t *negative;
-  char16_t digit[10];
-  char16_t unit[3];
-  char16_t unit10K[2];
-  uint8_t lang;
-  bool informal;
-};
-extern const char16_t gJapaneseNegative[] = {
-  0x30de, 0x30a4, 0x30ca, 0x30b9, 0x0000
-};
-static const CJKIdeographicData gDataJapaneseInformal = {
-  gJapaneseNegative,          // negative
-  {                           // digit
-    0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
-    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
-  },
-  { 0x5341, 0x767e, 0x5343 }, // unit
-  { 0x4e07, 0x5104 },         // unit10K
-  JAPANESE,                   // lang
-  true                        // informal
-};
-static const CJKIdeographicData gDataJapaneseFormal = {
-  gJapaneseNegative,          // negative
-  {                           // digit
-    0x96f6, 0x58f1, 0x5f10, 0x53c2, 0x56db,
-    0x4f0d, 0x516d, 0x4e03, 0x516b, 0x4e5d
-  },
-  { 0x62fe, 0x767e, 0x9621 }, // unit
-  { 0x842c, 0x5104 },         // unit10K
-  JAPANESE,                   // lang
-  false                       // informal
-};
-extern const char16_t gKoreanNegative[] = {
-  0xb9c8, 0xc774, 0xb108, 0xc2a4, 0x0020, 0x0000
-};
-static const CJKIdeographicData gDataKoreanHangulFormal = {
-  gKoreanNegative,            // negative
-  {                           // digit
-    0xc601, 0xc77c, 0xc774, 0xc0bc, 0xc0ac,
-    0xc624, 0xc721, 0xce60, 0xd314, 0xad6c
-  },
-  { 0xc2ed, 0xbc31, 0xcc9c }, // unit
-  { 0xb9cc, 0xc5b5 },         // unit10K
-  KOREAN,                     // lang
-  false                       // informal
-};
-static const CJKIdeographicData gDataKoreanHanjaInformal = {
-  gKoreanNegative,            // negative
-  {                           // digit
-    0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
-    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
-  },
-  { 0x5341, 0x767e, 0x5343 }, // unit
-  { 0x842c, 0x5104 },         // unit10K
-  KOREAN,                     // lang
-  true                        // informal
-};
-static const CJKIdeographicData gDataKoreanHanjaFormal = {
-  gKoreanNegative,            // negative
-  {                           // digit
-    0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x56db,
-    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
-  },
-  { 0x62fe, 0x767e, 0x4edf }, // unit
-  { 0x842c, 0x5104 },         // unit10K
-  KOREAN,                     // lang
-  false                       // informal
-};
-extern const char16_t gSimpChineseNegative[] = {
-  0x8d1f, 0x0000
-};
-static const CJKIdeographicData gDataSimpChineseInformal = {
-  gSimpChineseNegative,       // negative
-  {                           // digit
-    0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
-    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
-  },
-  { 0x5341, 0x767e, 0x5343 }, // unit
-  { 0x4e07, 0x4ebf },         // unit10K
-  CHINESE,                    // lang
-  true                        // informal
-};
-static const CJKIdeographicData gDataSimpChineseFormal = {
-  gSimpChineseNegative,       // negative
-  {                           // digit
-    0x96f6, 0x58f9, 0x8d30, 0x53c1, 0x8086,
-    0x4f0d, 0x9646, 0x67d2, 0x634c, 0x7396
-  },
-  { 0x62fe, 0x4f70, 0x4edf }, // unit
-  { 0x4e07, 0x4ebf },         // unit10K
-  CHINESE,                    // lang
-  false                       // informal
-};
-extern const char16_t gTradChineseNegative[] = {
-  0x8ca0, 0x0000
-};
-static const CJKIdeographicData gDataTradChineseInformal = {
-  gTradChineseNegative,       // negative
-  {                           // digit
-    0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
-    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
-  },
-  { 0x5341, 0x767e, 0x5343 }, // unit
-  { 0x842c, 0x5104 },         // unit10K
-  CHINESE,                    // lang
-  true                        // informal
-};
-static const CJKIdeographicData gDataTradChineseFormal = {
-  gTradChineseNegative,       // negative
-  {                           // digit
-    0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x8086,
-    0x4f0d, 0x9678, 0x67d2, 0x634c, 0x7396
-  },
-  { 0x62fe, 0x4f70, 0x4edf }, // unit
-  { 0x842c, 0x5104 },         // unit10K
-  CHINESE,                    // lang
-  false                       // informal
-};
-
-static const bool CJKIdeographicToText(int32_t aOrdinal, nsString& result,
-                                       const CJKIdeographicData& data)
-{
-  char16_t buf[NUM_BUF_SIZE];
-  int32_t idx = NUM_BUF_SIZE;
-  int32_t pos = 0;
-  bool isNegative = (aOrdinal < 0);
-  bool needZero = (aOrdinal == 0);
-  int32_t unitidx = 0, unit10Kidx = 0;
-  uint32_t ordinal = mozilla::Abs(aOrdinal);
-  do {
-    unitidx = pos % 4;
-    if (unitidx == 0) {
-      unit10Kidx = pos / 4;
-    }
-    int32_t cur = ordinal % 10;
-    if (cur == 0) {
-      if (needZero) {
-        needZero = false;
-        buf[--idx] = data.digit[0];
-      }
-    } else {
-      if (data.lang == CHINESE) {
-        needZero = true;
-      }
-      if (unit10Kidx != 0) {
-        if (data.lang == KOREAN && idx != NUM_BUF_SIZE) {
-          buf[--idx] = ' ';
-        }
-        buf[--idx] = data.unit10K[unit10Kidx - 1];
-      }
-      if (unitidx != 0) {
-        buf[--idx] = data.unit[unitidx - 1];
-      }
-      if (cur != 1) {
-        buf[--idx] = data.digit[cur];
-      } else {
-        bool needOne = true;
-        if (data.informal) {
-          switch (data.lang) {
-            case CHINESE:
-              if (unitidx == 1 &&
-                  (ordinal == 1 || (pos > 4 && ordinal % 1000 == 1))) {
-                needOne = false;
-              }
-              break;
-            case JAPANESE:
-              if (unitidx > 0 &&
-                  (unitidx != 3 || (pos == 3 && ordinal == 1))) {
-                needOne = false;
-              }
-              break;
-            case KOREAN:
-              if (unitidx > 0 || (pos == 4 && (ordinal % 1000) == 1)) {
-                needOne = false;
-              }
-              break;
-          }
-        }
-        if (needOne) {
-          buf[--idx] = data.digit[1];
-        }
-      }
-      unit10Kidx = 0;
-    }
-    ordinal /= 10;
-    pos++;
-  } while (ordinal > 0);
-  if (isNegative) {
-    result.Append(data.negative);
-  }
-  result.Append(buf + idx, NUM_BUF_SIZE - idx);
-  return true;
-}
-
-#define HEBREW_GERESH       0x05F3
-static const char16_t gHebrewDigit[22] = 
-{
-//   1       2       3       4       5       6       7       8       9
-0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8,
-//  10      20      30      40      50      60      70      80      90
-0x05D9, 0x05DB, 0x05DC, 0x05DE, 0x05E0, 0x05E1, 0x05E2, 0x05E4, 0x05E6,
-// 100     200     300     400
-0x05E7, 0x05E8, 0x05E9, 0x05EA
-};
-
-static bool HebrewToText(int32_t ordinal, nsString& result)
-{
-  if (ordinal < 1 || ordinal > 999999) {
-    return false;
-  }
-  bool outputSep = false;
-  nsAutoString allText, thousandsGroup;
-  do {
-    thousandsGroup.Truncate();
-    int32_t n3 = ordinal % 1000;
-    // Process digit for 100 - 900
-    for(int32_t n1 = 400; n1 > 0; )
-    {
-      if( n3 >= n1)
-      {
-        n3 -= n1;
-        thousandsGroup.Append(gHebrewDigit[(n1/100)-1+18]);
-      } else {
-        n1 -= 100;
-      } // if
-    } // for
-
-    // Process digit for 10 - 90
-    int32_t n2;
-    if( n3 >= 10 )
-    {
-      // Special process for 15 and 16
-      if(( 15 == n3 ) || (16 == n3)) {
-        // Special rule for religious reason...
-        // 15 is represented by 9 and 6, not 10 and 5
-        // 16 is represented by 9 and 7, not 10 and 6
-        n2 = 9;
-        thousandsGroup.Append(gHebrewDigit[ n2 - 1]);
-      } else {
-        n2 = n3 - (n3 % 10);
-        thousandsGroup.Append(gHebrewDigit[(n2/10)-1+9]);
-      } // if
-      n3 -= n2;
-    } // if
-  
-    // Process digit for 1 - 9 
-    if ( n3 > 0)
-      thousandsGroup.Append(gHebrewDigit[n3-1]);
-    if (outputSep) 
-      thousandsGroup.Append((char16_t)HEBREW_GERESH);
-    if (allText.IsEmpty())
-      allText = thousandsGroup;
-    else
-      allText = thousandsGroup + allText;
-    ordinal /= 1000;
-    outputSep = true;
-  } while (ordinal >= 1);
-
-  result.Append(allText);
-  return true;
-}
-
-// Convert ordinal to Ethiopic numeric representation.
-// The detail is available at http://www.ethiopic.org/Numerals/
-// The algorithm used here is based on the pseudo-code put up there by
-// Daniel Yacob <yacob@geez.org>.
-// Another reference is Unicode 3.0 standard section 11.1.
-#define ETHIOPIC_ONE             0x1369
-#define ETHIOPIC_TEN             0x1372
-#define ETHIOPIC_HUNDRED         0x137B
-#define ETHIOPIC_TEN_THOUSAND    0x137C
-
-static bool EthiopicToText(int32_t ordinal, nsString& result)
-{
-  if (ordinal < 1) {
-    return false;
-  }
-  nsAutoString asciiNumberString;      // decimal string representation of ordinal
-  DecimalToText(ordinal, asciiNumberString);
-  uint8_t asciiStringLength = asciiNumberString.Length();
-
-  // If number length is odd, add a leading "0"
-  // the leading "0" preconditions the string to always have the
-  // leading tens place populated, this avoids a check within the loop.
-  // If we didn't add the leading "0", decrement asciiStringLength so
-  // it will be equivalent to a zero-based index in both cases.
-  if (asciiStringLength & 1) {
-    asciiNumberString.Insert(NS_LITERAL_STRING("0"), 0);
-  } else {
-    asciiStringLength--;
-  }
-
-  // Iterate from the highest digits to lowest
-  // indexFromLeft       indexes digits (0 = most significant)
-  // groupIndexFromRight indexes pairs of digits (0 = least significant)
-  for (uint8_t indexFromLeft = 0, groupIndexFromRight = asciiStringLength >> 1;
-       indexFromLeft <= asciiStringLength;
-       indexFromLeft += 2, groupIndexFromRight--) {
-    uint8_t tensValue  = asciiNumberString.CharAt(indexFromLeft) & 0x0F;
-    uint8_t unitsValue = asciiNumberString.CharAt(indexFromLeft + 1) & 0x0F;
-    uint8_t groupValue = tensValue * 10 + unitsValue;
-
-    bool oddGroup = (groupIndexFromRight & 1);
-
-    // we want to clear ETHIOPIC_ONE when it is superfluous
-    if (ordinal > 1 &&
-        groupValue == 1 &&                  // one without a leading ten
-        (oddGroup || indexFromLeft == 0)) { // preceding (100) or leading the sequence
-      unitsValue = 0;
-    }
-
-    // put it all together...
-    if (tensValue) {
-      // map onto Ethiopic "tens":
-      result.Append((char16_t) (tensValue +  ETHIOPIC_TEN - 1));
-    }
-    if (unitsValue) {
-      //map onto Ethiopic "units":
-      result.Append((char16_t) (unitsValue + ETHIOPIC_ONE - 1));
-    }
-    // Add a separator for all even groups except the last,
-    // and for odd groups with non-zero value.
-    if (oddGroup) {
-      if (groupValue) {
-        result.Append((char16_t) ETHIOPIC_HUNDRED);
-      }
-    } else {
-      if (groupIndexFromRight) {
-        result.Append((char16_t) ETHIOPIC_TEN_THOUSAND);
-      }
-    }
-  }
-  return true;
-}
-
-
-/* static */ void
-nsBulletFrame::AppendCounterText(int32_t aListStyleType,
-                                 int32_t aOrdinal,
-                                 nsString& result,
-                                 bool& isRTL)
-{
-  bool success = true;
-  int32_t fallback = NS_STYLE_LIST_STYLE_DECIMAL;
-  isRTL = false;
-  
-  switch (aListStyleType) {
-    case NS_STYLE_LIST_STYLE_NONE: // used by counters code only
-      break;
-
-    case NS_STYLE_LIST_STYLE_DISC: // used by counters code only
-      // XXX We really need to do this the same way we do list bullets.
-      result.Append(char16_t(0x2022));
-      break;
-
-    case NS_STYLE_LIST_STYLE_CIRCLE: // used by counters code only
-      // XXX We really need to do this the same way we do list bullets.
-      result.Append(char16_t(0x25E6));
-      break;
-
-    case NS_STYLE_LIST_STYLE_SQUARE: // used by counters code only
-      // XXX We really need to do this the same way we do list bullets.
-      result.Append(char16_t(0x25FE));
-      break;
-
-    case NS_STYLE_LIST_STYLE_DECIMAL:
-    default: // CSS2 say "A users  agent that does not recognize a numbering system
-      // should use 'decimal'
-      success = DecimalToText(aOrdinal, result);
-      NS_ASSERTION(success, "DecimalToText must never fail");
-      break;
-
-    case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
-      success =
-        CJKIdeographicToText(aOrdinal, result, gDataTradChineseInformal);
-      break;
-
-    case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
-      success = CJKIdeographicToText(aOrdinal, result, gDataTradChineseFormal);
-      break;
-
-    case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
-      success =
-        CJKIdeographicToText(aOrdinal, result, gDataSimpChineseInformal);
-      break;
-
-    case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
-      success = CJKIdeographicToText(aOrdinal, result, gDataSimpChineseFormal);
-      break;
-
-    case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
-      success = CJKIdeographicToText(aOrdinal, result, gDataJapaneseInformal);
-      break;
-
-    case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
-      success = CJKIdeographicToText(aOrdinal, result, gDataJapaneseFormal);
-      break;
-
-    case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
-      success =
-        CJKIdeographicToText(aOrdinal, result, gDataKoreanHangulFormal);
-      break;
-
-    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
-      success =
-        CJKIdeographicToText(aOrdinal, result, gDataKoreanHanjaInformal);
-      break;
-
-    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
-      success = CJKIdeographicToText(aOrdinal, result, gDataKoreanHanjaFormal);
-      break;
-
-    case NS_STYLE_LIST_STYLE_HEBREW: 
-      isRTL = true;
-      success = HebrewToText(aOrdinal, result);
-      break;
- 
-    case NS_STYLE_LIST_STYLE_MOZ_TAMIL:
-      success = TamilToText(aOrdinal, result);
-      break;
-
-    case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_NUMERIC:
-      success = EthiopicToText(aOrdinal, result);
-      break;
-  }
-  if (!success) {
-    AppendCounterText(fallback, aOrdinal, result, isRTL);
-  }
-}
-
-/* static */ void
-nsBulletFrame::GetListItemSuffix(int32_t aListStyleType,
-                                 nsString& aResult)
-{
-  aResult.AssignLiteral(MOZ_UTF16(". "));
-
-  switch (aListStyleType) {
-    case NS_STYLE_LIST_STYLE_NONE: // used by counters code only
-    case NS_STYLE_LIST_STYLE_DISC: // used by counters code only
-    case NS_STYLE_LIST_STYLE_CIRCLE: // used by counters code only
-    case NS_STYLE_LIST_STYLE_SQUARE: // used by counters code only
-      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:
-    case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
-      aResult = 0x3001;
-      break;
-
-    case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
-    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
-    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
-      aResult.AssignLiteral(MOZ_UTF16(", "));
-      break;
-  }
-}
-
 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,
--- a/layout/generic/nsBulletFrame.h
+++ b/layout/generic/nsBulletFrame.h
@@ -71,28 +71,17 @@ public:
                       nsReflowStatus& aStatus) MOZ_OVERRIDE;
   virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
   virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
 
   // nsBulletFrame
   int32_t SetListItemOrdinal(int32_t aNextOrdinal, bool* aChanged,
                              int32_t aIncrement);
 
-
-  /* get list item text, without '.' */
-  static void AppendCounterText(int32_t aListStyleType,
-                                int32_t aOrdinal,
-                                nsString& aResult,
-                                bool& aIsRTL);
-
-  /* get suffix of list item */
-  static void GetListItemSuffix(int32_t aListStyleType,
-                                nsString& aResult);
-
-  /* get list item text, with '.' */
+  /* get list item text, with prefix & suffix */
   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;
--- a/layout/style/CounterStyleManager.cpp
+++ b/layout/style/CounterStyleManager.cpp
@@ -5,32 +5,24 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CounterStyleManager.h"
 
 #include "mozilla/Types.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/ArrayUtils.h"
+#include "prprf.h"
 #include "nsString.h"
 #include "nsStyleSet.h"
 #include "nsCSSRules.h"
 #include "nsTArray.h"
-#include "nsBulletFrame.h"
 #include "nsTHashtable.h"
 #include "nsUnicodeProperties.h"
 
-// XXX The following global constants are defined in nsBulletFrame.cpp
-// They are temporarily referenced here. All counter text generating code should
-// be moved here in some later patches.
-extern const char16_t gJapaneseNegative[];
-extern const char16_t gKoreanNegative[];
-extern const char16_t gSimpChineseNegative[];
-extern const char16_t gTradChineseNegative[];
-
 namespace mozilla {
 
 struct AdditiveSymbol
 {
   CounterValue weight;
   nsString symbol;
 };
 
@@ -208,16 +200,370 @@ GetAdditiveCounterText(CounterValue aOrd
         }
       }
       aOrdinal -= times * symbol.weight;
     }
   }
   return aOrdinal == 0;
 }
 
+static bool
+DecimalToText(CounterValue aOrdinal, nsSubstring& aResult)
+{
+  // 3 for additional digit, negative sign, and null
+  char cbuf[std::numeric_limits<CounterValue>::digits10 + 3];
+  PR_snprintf(cbuf, sizeof(cbuf), "%ld", aOrdinal);
+  aResult.AssignASCII(cbuf);
+  return true;
+}
+
+static bool
+TamilToText(CounterValue aOrdinal, nsSubstring& aResult)
+{
+  if (aOrdinal < 1 || aOrdinal > 9999) {
+    return false;
+  }
+  char16_t diff = 0x0BE6 - char16_t('0');
+  // We're going to be appending to whatever is in "result" already, so make
+  // sure to only munge the new bits.  Note that we can't just grab the pointer
+  // to the new stuff here, since appending to the string can realloc.
+  DecimalToText(aOrdinal, aResult);
+  char16_t* p = aResult.BeginWriting();
+  for(; '\0' != *p ; p++) 
+    if(*p != char16_t('0'))
+      *p += diff;
+  return true;
+}
+
+// We know cjk-ideographic need 31 characters to display 99,999,999,999,999,999
+// georgian needs 6 at most
+// armenian needs 12 at most
+// hebrew may need more...
+
+#define NUM_BUF_SIZE 34 
+
+enum CJKIdeographicLang {
+  CHINESE, KOREAN, JAPANESE
+};
+struct CJKIdeographicData {
+  char16_t digit[10];
+  char16_t unit[3];
+  char16_t unit10K[2];
+  uint8_t lang;
+  bool informal;
+};
+static const CJKIdeographicData gDataJapaneseInformal = {
+  {                           // digit
+    0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
+    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
+  },
+  { 0x5341, 0x767e, 0x5343 }, // unit
+  { 0x4e07, 0x5104 },         // unit10K
+  JAPANESE,                   // lang
+  true                        // informal
+};
+static const CJKIdeographicData gDataJapaneseFormal = {
+  {                           // digit
+    0x96f6, 0x58f1, 0x5f10, 0x53c2, 0x56db,
+    0x4f0d, 0x516d, 0x4e03, 0x516b, 0x4e5d
+  },
+  { 0x62fe, 0x767e, 0x9621 }, // unit
+  { 0x842c, 0x5104 },         // unit10K
+  JAPANESE,                   // lang
+  false                       // informal
+};
+static const CJKIdeographicData gDataKoreanHangulFormal = {
+  {                           // digit
+    0xc601, 0xc77c, 0xc774, 0xc0bc, 0xc0ac,
+    0xc624, 0xc721, 0xce60, 0xd314, 0xad6c
+  },
+  { 0xc2ed, 0xbc31, 0xcc9c }, // unit
+  { 0xb9cc, 0xc5b5 },         // unit10K
+  KOREAN,                     // lang
+  false                       // informal
+};
+static const CJKIdeographicData gDataKoreanHanjaInformal = {
+  {                           // digit
+    0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
+    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
+  },
+  { 0x5341, 0x767e, 0x5343 }, // unit
+  { 0x842c, 0x5104 },         // unit10K
+  KOREAN,                     // lang
+  true                        // informal
+};
+static const CJKIdeographicData gDataKoreanHanjaFormal = {
+  {                           // digit
+    0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x56db,
+    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
+  },
+  { 0x62fe, 0x767e, 0x4edf }, // unit
+  { 0x842c, 0x5104 },         // unit10K
+  KOREAN,                     // lang
+  false                       // informal
+};
+static const CJKIdeographicData gDataSimpChineseInformal = {
+  {                           // digit
+    0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
+    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
+  },
+  { 0x5341, 0x767e, 0x5343 }, // unit
+  { 0x4e07, 0x4ebf },         // unit10K
+  CHINESE,                    // lang
+  true                        // informal
+};
+static const CJKIdeographicData gDataSimpChineseFormal = {
+  {                           // digit
+    0x96f6, 0x58f9, 0x8d30, 0x53c1, 0x8086,
+    0x4f0d, 0x9646, 0x67d2, 0x634c, 0x7396
+  },
+  { 0x62fe, 0x4f70, 0x4edf }, // unit
+  { 0x4e07, 0x4ebf },         // unit10K
+  CHINESE,                    // lang
+  false                       // informal
+};
+static const CJKIdeographicData gDataTradChineseInformal = {
+  {                           // digit
+    0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
+    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
+  },
+  { 0x5341, 0x767e, 0x5343 }, // unit
+  { 0x842c, 0x5104 },         // unit10K
+  CHINESE,                    // lang
+  true                        // informal
+};
+static const CJKIdeographicData gDataTradChineseFormal = {
+  {                           // digit
+    0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x8086,
+    0x4f0d, 0x9678, 0x67d2, 0x634c, 0x7396
+  },
+  { 0x62fe, 0x4f70, 0x4edf }, // unit
+  { 0x842c, 0x5104 },         // unit10K
+  CHINESE,                    // lang
+  false                       // informal
+};
+
+static bool
+CJKIdeographicToText(CounterValue aOrdinal, nsSubstring& aResult,
+                     const CJKIdeographicData& data)
+{
+  NS_ASSERTION(aOrdinal >= 0, "Only accept non-negative ordinal");
+  char16_t buf[NUM_BUF_SIZE];
+  int32_t idx = NUM_BUF_SIZE;
+  int32_t pos = 0;
+  bool needZero = (aOrdinal == 0);
+  int32_t unitidx = 0, unit10Kidx = 0;
+  do {
+    unitidx = pos % 4;
+    if (unitidx == 0) {
+      unit10Kidx = pos / 4;
+    }
+    int32_t cur = aOrdinal % 10;
+    if (cur == 0) {
+      if (needZero) {
+        needZero = false;
+        buf[--idx] = data.digit[0];
+      }
+    } else {
+      if (data.lang == CHINESE) {
+        needZero = true;
+      }
+      if (unit10Kidx != 0) {
+        if (data.lang == KOREAN && idx != NUM_BUF_SIZE) {
+          buf[--idx] = ' ';
+        }
+        buf[--idx] = data.unit10K[unit10Kidx - 1];
+      }
+      if (unitidx != 0) {
+        buf[--idx] = data.unit[unitidx - 1];
+      }
+      if (cur != 1) {
+        buf[--idx] = data.digit[cur];
+      } else {
+        bool needOne = true;
+        if (data.informal) {
+          switch (data.lang) {
+            case CHINESE:
+              if (unitidx == 1 &&
+                  (aOrdinal == 1 || (pos > 4 && aOrdinal % 1000 == 1))) {
+                needOne = false;
+              }
+              break;
+            case JAPANESE:
+              if (unitidx > 0 &&
+                  (unitidx != 3 || (pos == 3 && aOrdinal == 1))) {
+                needOne = false;
+              }
+              break;
+            case KOREAN:
+              if (unitidx > 0 || (pos == 4 && (aOrdinal % 1000) == 1)) {
+                needOne = false;
+              }
+              break;
+          }
+        }
+        if (needOne) {
+          buf[--idx] = data.digit[1];
+        }
+      }
+      unit10Kidx = 0;
+    }
+    aOrdinal /= 10;
+    pos++;
+  } while (aOrdinal > 0);
+  aResult.Assign(buf + idx, NUM_BUF_SIZE - idx);
+  return true;
+}
+
+#define HEBREW_GERESH       0x05F3
+static const char16_t gHebrewDigit[22] = 
+{
+  //   1       2       3       4       5       6       7       8       9
+  0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8,
+  //  10      20      30      40      50      60      70      80      90
+  0x05D9, 0x05DB, 0x05DC, 0x05DE, 0x05E0, 0x05E1, 0x05E2, 0x05E4, 0x05E6,
+  // 100     200     300     400
+  0x05E7, 0x05E8, 0x05E9, 0x05EA
+};
+
+static bool
+HebrewToText(CounterValue aOrdinal, nsSubstring& aResult)
+{
+  if (aOrdinal < 1 || aOrdinal > 999999) {
+    return false;
+  }
+
+  bool outputSep = false;
+  nsAutoString allText, thousandsGroup;
+  do {
+    thousandsGroup.Truncate();
+    int32_t n3 = aOrdinal % 1000;
+    // Process digit for 100 - 900
+    for(int32_t n1 = 400; n1 > 0; )
+    {
+      if( n3 >= n1)
+      {
+        n3 -= n1;
+        thousandsGroup.Append(gHebrewDigit[(n1/100)-1+18]);
+      } else {
+        n1 -= 100;
+      } // if
+    } // for
+
+    // Process digit for 10 - 90
+    int32_t n2;
+    if( n3 >= 10 )
+    {
+      // Special process for 15 and 16
+      if(( 15 == n3 ) || (16 == n3)) {
+        // Special rule for religious reason...
+        // 15 is represented by 9 and 6, not 10 and 5
+        // 16 is represented by 9 and 7, not 10 and 6
+        n2 = 9;
+        thousandsGroup.Append(gHebrewDigit[ n2 - 1]);
+      } else {
+        n2 = n3 - (n3 % 10);
+        thousandsGroup.Append(gHebrewDigit[(n2/10)-1+9]);
+      } // if
+      n3 -= n2;
+    } // if
+  
+    // Process digit for 1 - 9 
+    if ( n3 > 0)
+      thousandsGroup.Append(gHebrewDigit[n3-1]);
+    if (outputSep) 
+      thousandsGroup.Append((char16_t)HEBREW_GERESH);
+    if (allText.IsEmpty())
+      allText = thousandsGroup;
+    else
+      allText = thousandsGroup + allText;
+    aOrdinal /= 1000;
+    outputSep = true;
+  } while (aOrdinal >= 1);
+
+  aResult = allText;
+  return true;
+}
+
+// Convert ordinal to Ethiopic numeric representation.
+// The detail is available at http://www.ethiopic.org/Numerals/
+// The algorithm used here is based on the pseudo-code put up there by
+// Daniel Yacob <yacob@geez.org>.
+// Another reference is Unicode 3.0 standard section 11.1.
+#define ETHIOPIC_ONE             0x1369
+#define ETHIOPIC_TEN             0x1372
+#define ETHIOPIC_HUNDRED         0x137B
+#define ETHIOPIC_TEN_THOUSAND    0x137C
+
+static bool
+EthiopicToText(CounterValue aOrdinal, nsSubstring& aResult)
+{
+  if (aOrdinal < 1) {
+    return false;
+  }
+
+  nsAutoString asciiNumberString;      // decimal string representation of ordinal
+  DecimalToText(aOrdinal, asciiNumberString);
+  uint8_t asciiStringLength = asciiNumberString.Length();
+
+  // If number length is odd, add a leading "0"
+  // the leading "0" preconditions the string to always have the
+  // leading tens place populated, this avoids a check within the loop.
+  // If we didn't add the leading "0", decrement asciiStringLength so
+  // it will be equivalent to a zero-based index in both cases.
+  if (asciiStringLength & 1) {
+    asciiNumberString.Insert(NS_LITERAL_STRING("0"), 0);
+  } else {
+    asciiStringLength--;
+  }
+
+  aResult.Truncate();
+  // Iterate from the highest digits to lowest
+  // indexFromLeft       indexes digits (0 = most significant)
+  // groupIndexFromRight indexes pairs of digits (0 = least significant)
+  for (uint8_t indexFromLeft = 0, groupIndexFromRight = asciiStringLength >> 1;
+       indexFromLeft <= asciiStringLength;
+       indexFromLeft += 2, groupIndexFromRight--) {
+    uint8_t tensValue  = asciiNumberString.CharAt(indexFromLeft) & 0x0F;
+    uint8_t unitsValue = asciiNumberString.CharAt(indexFromLeft + 1) & 0x0F;
+    uint8_t groupValue = tensValue * 10 + unitsValue;
+
+    bool oddGroup = (groupIndexFromRight & 1);
+
+    // we want to clear ETHIOPIC_ONE when it is superfluous
+    if (aOrdinal > 1 &&
+        groupValue == 1 &&                  // one without a leading ten
+        (oddGroup || indexFromLeft == 0)) { // preceding (100) or leading the sequence
+      unitsValue = 0;
+    }
+
+    // put it all together...
+    if (tensValue) {
+      // map onto Ethiopic "tens":
+      aResult.Append((char16_t) (tensValue +  ETHIOPIC_TEN - 1));
+    }
+    if (unitsValue) {
+      //map onto Ethiopic "units":
+      aResult.Append((char16_t) (unitsValue + ETHIOPIC_ONE - 1));
+    }
+    // Add a separator for all even groups except the last,
+    // and for odd groups with non-zero value.
+    if (oddGroup) {
+      if (groupValue) {
+        aResult.Append((char16_t) ETHIOPIC_HUNDRED);
+      }
+    } else {
+      if (groupIndexFromRight) {
+        aResult.Append((char16_t) ETHIOPIC_TEN_THOUSAND);
+      }
+    }
+  }
+  return true;
+}
+
 class BuiltinCounterStyle : public CounterStyle
 {
 public:
   friend class CounterStyleManager;
 
   // will be initialized by CounterStyleManager::InitializeBuiltinCounterStyles
   MOZ_CONSTEXPR BuiltinCounterStyle()
     : CounterStyle(NS_STYLE_LIST_STYLE_NONE)
@@ -262,27 +608,52 @@ public:
 BuiltinCounterStyle::GetPrefix(nsSubstring& aResult)
 {
   aResult.Truncate();
 }
 
 /* virtual */ void
 BuiltinCounterStyle::GetSuffix(nsSubstring& aResult)
 {
-  // XXX nsBulletFrame::GetListItemSuffix accepts only nsString&, so we reassign
-  // the result here. This could be avoided when the whole code is moved here.
-  nsAutoString result;
-  nsBulletFrame::GetListItemSuffix(mStyle, result);
-  aResult = result;
+  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:
+      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:
+    case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
+      aResult = 0x3001;
+      break;
+
+    case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
+    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
+    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
+      aResult.AssignLiteral(MOZ_UTF16(", "));
+      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 = 0x25aa;
+static const char16_t kSquareCharacter = 0x25fe;
 
 /* virtual */ void
 BuiltinCounterStyle::GetSpokenCounterText(CounterValue aOrdinal,
                                           nsSubstring& aResult,
                                           bool& aIsBullet)
 {
   switch (mStyle) {
     case NS_STYLE_LIST_STYLE_NONE:
@@ -316,16 +687,29 @@ BuiltinCounterStyle::IsBullet()
     case NS_STYLE_LIST_STYLE_CIRCLE:
     case NS_STYLE_LIST_STYLE_SQUARE:
       return true;
     default:
       return false;
   }
 }
 
+static const char16_t gJapaneseNegative[] = {
+  0x30de, 0x30a4, 0x30ca, 0x30b9, 0x0000
+};
+static const char16_t gKoreanNegative[] = {
+  0xb9c8, 0xc774, 0xb108, 0xc2a4, 0x0020, 0x0000
+};
+static const char16_t gSimpChineseNegative[] = {
+  0x8d1f, 0x0000
+};
+static const char16_t gTradChineseNegative[] = {
+  0x8ca0, 0x0000
+};
+
 /* virtual */ void
 BuiltinCounterStyle::GetNegative(NegativeType& aResult)
 {
   switch (mStyle) {
     case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
     case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
       aResult.before = gJapaneseNegative;
       break;
@@ -478,26 +862,69 @@ BuiltinCounterStyle::CallFallbackStyle(C
   GetFallback()->GetCounterText(aOrdinal, aResult, aIsRTL);
 }
 
 /* virtual */ bool
 BuiltinCounterStyle::GetInitialCounterText(CounterValue aOrdinal,
                                            nsSubstring& aResult,
                                            bool& aIsRTL)
 {
-  // Since nsBulletFrame::AppendCounterText use nsString& as parameter, we
-  // have to reassign it.
-  nsAutoString result;
-  nsBulletFrame::AppendCounterText(mStyle, aOrdinal, result, aIsRTL);
-  aResult = result;
-  // Out-of-range ordinals should have been filtered out before invoking this
-  // function.  Since all builtin styles are defined in a continuous range,
-  // no extendable fallback should have happend inside the function above,
-  // consequently this method will always succeed.
-  return true;
+  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:
+      aResult.Truncate();
+      return true;
+    case NS_STYLE_LIST_STYLE_DISC:
+      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_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);
+    case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
+      return CJKIdeographicToText(aOrdinal, aResult, gDataSimpChineseInformal);
+    case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
+      return CJKIdeographicToText(aOrdinal, aResult, gDataSimpChineseFormal);
+    case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
+      return CJKIdeographicToText(aOrdinal, aResult, gDataJapaneseInformal);
+    case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
+      return CJKIdeographicToText(aOrdinal, aResult, gDataJapaneseFormal);
+    case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
+      return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHangulFormal);
+    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
+      return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHanjaInformal);
+    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
+      return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHanjaFormal);
+
+    case NS_STYLE_LIST_STYLE_HEBREW: 
+      aIsRTL = true;
+      return HebrewToText(aOrdinal, aResult);
+ 
+    case NS_STYLE_LIST_STYLE_MOZ_TAMIL:
+      return TamilToText(aOrdinal, aResult);
+
+    case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_NUMERIC:
+      return EthiopicToText(aOrdinal, aResult);
+
+    default:
+      NS_NOTREACHED("Unknown builtin counter style");
+      return false;
+  }
 }
 
 class DependentBuiltinCounterStyle MOZ_FINAL : public BuiltinCounterStyle
 {
 public:
   DependentBuiltinCounterStyle(int32_t aStyle, CounterStyleManager* aManager)
     : BuiltinCounterStyle(aStyle), 
       mManager(aManager)
@@ -1308,20 +1735,17 @@ CounterStyle::GetSpokenCounterText(Count
   bool isRTL; // we don't care about direction for spoken text
   aIsBullet = false;
   switch (GetSpeakAs()) {
     case NS_STYLE_COUNTER_SPEAKAS_BULLETS:
       aResult.Assign(kDiscCharacter);
       aIsBullet = true;
       break;
     case NS_STYLE_COUNTER_SPEAKAS_NUMBERS:
-      // use decimal to generate numeric text
-      // XXX change to DecimalToText after moving it here
-      CounterStyleManager::GetDecimalStyle()->
-        GetCounterText(aOrdinal, aResult, isRTL);
+      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);
       break;
     case NS_STYLE_COUNTER_SPEAKAS_OTHER: