LCOV - code coverage report
Current view: top level - layout/style - CounterStyleManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 32 851 3.8 %
Date: 2017-07-14 16:53:18 Functions: 7 100 7.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "CounterStyleManager.h"
       8             : 
       9             : #include "mozilla/ArenaObjectID.h"
      10             : #include "mozilla/ArrayUtils.h"
      11             : #include "mozilla/CheckedInt.h"
      12             : #include "mozilla/MathAlgorithms.h"
      13             : #include "mozilla/Types.h"
      14             : #include "mozilla/WritingModes.h"
      15             : #include "nsCSSCounterStyleRule.h"
      16             : #include "nsString.h"
      17             : #include "nsStyleSet.h"
      18             : #include "nsTArray.h"
      19             : #include "nsTHashtable.h"
      20             : #include "nsUnicodeProperties.h"
      21             : #include "mozilla/StyleSetHandle.h"
      22             : #include "mozilla/StyleSetHandleInlines.h"
      23             : 
      24             : namespace mozilla {
      25             : 
      26           0 : struct AdditiveSymbol
      27             : {
      28             :   CounterValue weight;
      29             :   nsString symbol;
      30             : };
      31             : 
      32           0 : struct NegativeType
      33             : {
      34             :   nsString before, after;
      35             : };
      36             : 
      37           0 : struct PadType
      38             : {
      39             :   int32_t width;
      40             :   nsString symbol;
      41             : };
      42             : 
      43             : // This limitation will be applied to some systems, and pad descriptor.
      44             : // Any initial representation generated by symbolic or additive which is
      45             : // longer than this limitation will be dropped. If any pad is longer
      46             : // than this, the whole counter text will be dropped as well.
      47             : // The spec requires user agents to support at least 60 Unicode code-
      48             : // points for counter text. However, this constant only limits the
      49             : // length in 16-bit units. So it has to be at least 120, since code-
      50             : // points outside the BMP will need 2 16-bit units.
      51             : #define LENGTH_LIMIT 150
      52             : 
      53             : static bool
      54           0 : GetCyclicCounterText(CounterValue aOrdinal,
      55             :                      nsAString& aResult,
      56             :                      const nsTArray<nsString>& aSymbols)
      57             : {
      58           0 :   MOZ_ASSERT(aSymbols.Length() >= 1,
      59             :              "No symbol available for cyclic counter.");
      60           0 :   auto n = aSymbols.Length();
      61           0 :   CounterValue index = (aOrdinal - 1) % n;
      62           0 :   aResult = aSymbols[index >= 0 ? index : index + n];
      63           0 :   return true;
      64             : }
      65             : 
      66             : static bool
      67           0 : GetFixedCounterText(CounterValue aOrdinal,
      68             :                     nsAString& aResult,
      69             :                     CounterValue aStart,
      70             :                     const nsTArray<nsString>& aSymbols)
      71             : {
      72           0 :   CounterValue index = aOrdinal - aStart;
      73           0 :   if (index >= 0 && index < CounterValue(aSymbols.Length())) {
      74           0 :     aResult = aSymbols[index];
      75           0 :     return true;
      76             :   } else {
      77           0 :     return false;
      78             :   }
      79             : }
      80             : 
      81             : static bool
      82           0 : GetSymbolicCounterText(CounterValue aOrdinal,
      83             :                        nsAString& aResult,
      84             :                        const nsTArray<nsString>& aSymbols)
      85             : {
      86           0 :   MOZ_ASSERT(aSymbols.Length() >= 1,
      87             :              "No symbol available for symbolic counter.");
      88           0 :   MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal.");
      89           0 :   if (aOrdinal == 0) {
      90           0 :     return false;
      91             :   }
      92             : 
      93           0 :   aResult.Truncate();
      94           0 :   auto n = aSymbols.Length();
      95           0 :   const nsString& symbol = aSymbols[(aOrdinal - 1) % n];
      96           0 :   size_t len = (aOrdinal + n - 1) / n;
      97           0 :   auto symbolLength = symbol.Length();
      98           0 :   if (symbolLength > 0) {
      99           0 :     if (len > LENGTH_LIMIT || symbolLength > LENGTH_LIMIT ||
     100           0 :         len * symbolLength > LENGTH_LIMIT) {
     101           0 :       return false;
     102             :     }
     103           0 :     for (size_t i = 0; i < len; ++i) {
     104           0 :       aResult.Append(symbol);
     105             :     }
     106             :   }
     107           0 :   return true;
     108             : }
     109             : 
     110             : static bool
     111           0 : GetAlphabeticCounterText(CounterValue aOrdinal,
     112             :                          nsAString& aResult,
     113             :                          const nsTArray<nsString>& aSymbols)
     114             : {
     115           0 :   MOZ_ASSERT(aSymbols.Length() >= 2,
     116             :              "Too few symbols for alphabetic counter.");
     117           0 :   MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal.");
     118           0 :   if (aOrdinal == 0) {
     119           0 :     return false;
     120             :   }
     121             : 
     122           0 :   auto n = aSymbols.Length();
     123             :   // The precise length of this array should be
     124             :   // ceil(log((double) aOrdinal / n * (n - 1) + 1) / log(n)).
     125             :   // The max length is slightly smaller than which defined below.
     126           0 :   AutoTArray<int32_t, std::numeric_limits<CounterValue>::digits> indexes;
     127           0 :   while (aOrdinal > 0) {
     128           0 :     --aOrdinal;
     129           0 :     indexes.AppendElement(aOrdinal % n);
     130           0 :     aOrdinal /= n;
     131             :   }
     132             : 
     133           0 :   aResult.Truncate();
     134           0 :   for (auto i = indexes.Length(); i > 0; --i) {
     135           0 :     aResult.Append(aSymbols[indexes[i - 1]]);
     136             :   }
     137           0 :   return true;
     138             : }
     139             : 
     140             : static bool
     141           0 : GetNumericCounterText(CounterValue aOrdinal,
     142             :                       nsAString& aResult,
     143             :                       const nsTArray<nsString>& aSymbols)
     144             : {
     145           0 :   MOZ_ASSERT(aSymbols.Length() >= 2,
     146             :              "Too few symbols for numeric counter.");
     147           0 :   MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal.");
     148             : 
     149           0 :   if (aOrdinal == 0) {
     150           0 :     aResult = aSymbols[0];
     151           0 :     return true;
     152             :   }
     153             : 
     154           0 :   auto n = aSymbols.Length();
     155           0 :   AutoTArray<int32_t, std::numeric_limits<CounterValue>::digits> indexes;
     156           0 :   while (aOrdinal > 0) {
     157           0 :     indexes.AppendElement(aOrdinal % n);
     158           0 :     aOrdinal /= n;
     159             :   }
     160             : 
     161           0 :   aResult.Truncate();
     162           0 :   for (auto i = indexes.Length(); i > 0; --i) {
     163           0 :     aResult.Append(aSymbols[indexes[i - 1]]);
     164             :   }
     165           0 :   return true;
     166             : }
     167             : 
     168             : static bool
     169           0 : GetAdditiveCounterText(CounterValue aOrdinal,
     170             :                        nsAString& aResult,
     171             :                        const nsTArray<AdditiveSymbol>& aSymbols)
     172             : {
     173           0 :   MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal.");
     174             : 
     175           0 :   if (aOrdinal == 0) {
     176           0 :     const AdditiveSymbol& last = aSymbols.LastElement();
     177           0 :     if (last.weight == 0) {
     178           0 :       aResult = last.symbol;
     179           0 :       return true;
     180             :     }
     181           0 :     return false;
     182             :   }
     183             : 
     184           0 :   aResult.Truncate();
     185           0 :   size_t length = 0;
     186           0 :   for (size_t i = 0, iEnd = aSymbols.Length(); i < iEnd; ++i) {
     187           0 :     const AdditiveSymbol& symbol = aSymbols[i];
     188           0 :     if (symbol.weight == 0) {
     189           0 :       break;
     190             :     }
     191           0 :     CounterValue times = aOrdinal / symbol.weight;
     192           0 :     if (times > 0) {
     193           0 :       auto symbolLength = symbol.symbol.Length();
     194           0 :       if (symbolLength > 0) {
     195           0 :         length += times * symbolLength;
     196           0 :         if (times > LENGTH_LIMIT ||
     197           0 :             symbolLength > LENGTH_LIMIT ||
     198             :             length > LENGTH_LIMIT) {
     199           0 :           return false;
     200             :         }
     201           0 :         for (CounterValue j = 0; j < times; ++j) {
     202           0 :           aResult.Append(symbol.symbol);
     203             :         }
     204             :       }
     205           0 :       aOrdinal -= times * symbol.weight;
     206             :     }
     207             :   }
     208           0 :   return aOrdinal == 0;
     209             : }
     210             : 
     211             : static bool
     212           0 : DecimalToText(CounterValue aOrdinal, nsAString& aResult)
     213             : {
     214           0 :   aResult.AppendInt(aOrdinal);
     215           0 :   return true;
     216             : }
     217             : 
     218             : // We know cjk-ideographic need 31 characters to display 99,999,999,999,999,999
     219             : // georgian needs 6 at most
     220             : // armenian needs 12 at most
     221             : // hebrew may need more...
     222             : 
     223             : #define NUM_BUF_SIZE 34
     224             : 
     225             : enum CJKIdeographicLang {
     226             :   CHINESE, KOREAN, JAPANESE
     227             : };
     228             : struct CJKIdeographicData {
     229             :   char16_t digit[10];
     230             :   char16_t unit[3];
     231             :   char16_t unit10K[2];
     232             :   uint8_t lang;
     233             :   bool informal;
     234             : };
     235             : static const CJKIdeographicData gDataJapaneseInformal = {
     236             :   {                           // digit
     237             :     0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
     238             :     0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
     239             :   },
     240             :   { 0x5341, 0x767e, 0x5343 }, // unit
     241             :   { 0x4e07, 0x5104 },         // unit10K
     242             :   JAPANESE,                   // lang
     243             :   true                        // informal
     244             : };
     245             : static const CJKIdeographicData gDataJapaneseFormal = {
     246             :   {                           // digit
     247             :     0x96f6, 0x58f1, 0x5f10, 0x53c2, 0x56db,
     248             :     0x4f0d, 0x516d, 0x4e03, 0x516b, 0x4e5d
     249             :   },
     250             :   { 0x62fe, 0x767e, 0x9621 }, // unit
     251             :   { 0x842c, 0x5104 },         // unit10K
     252             :   JAPANESE,                   // lang
     253             :   false                       // informal
     254             : };
     255             : static const CJKIdeographicData gDataKoreanHangulFormal = {
     256             :   {                           // digit
     257             :     0xc601, 0xc77c, 0xc774, 0xc0bc, 0xc0ac,
     258             :     0xc624, 0xc721, 0xce60, 0xd314, 0xad6c
     259             :   },
     260             :   { 0xc2ed, 0xbc31, 0xcc9c }, // unit
     261             :   { 0xb9cc, 0xc5b5 },         // unit10K
     262             :   KOREAN,                     // lang
     263             :   false                       // informal
     264             : };
     265             : static const CJKIdeographicData gDataKoreanHanjaInformal = {
     266             :   {                           // digit
     267             :     0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
     268             :     0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
     269             :   },
     270             :   { 0x5341, 0x767e, 0x5343 }, // unit
     271             :   { 0x842c, 0x5104 },         // unit10K
     272             :   KOREAN,                     // lang
     273             :   true                        // informal
     274             : };
     275             : static const CJKIdeographicData gDataKoreanHanjaFormal = {
     276             :   {                           // digit
     277             :     0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x56db,
     278             :     0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
     279             :   },
     280             :   { 0x62fe, 0x767e, 0x4edf }, // unit
     281             :   { 0x842c, 0x5104 },         // unit10K
     282             :   KOREAN,                     // lang
     283             :   false                       // informal
     284             : };
     285             : static const CJKIdeographicData gDataSimpChineseInformal = {
     286             :   {                           // digit
     287             :     0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
     288             :     0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
     289             :   },
     290             :   { 0x5341, 0x767e, 0x5343 }, // unit
     291             :   { 0x4e07, 0x4ebf },         // unit10K
     292             :   CHINESE,                    // lang
     293             :   true                        // informal
     294             : };
     295             : static const CJKIdeographicData gDataSimpChineseFormal = {
     296             :   {                           // digit
     297             :     0x96f6, 0x58f9, 0x8d30, 0x53c1, 0x8086,
     298             :     0x4f0d, 0x9646, 0x67d2, 0x634c, 0x7396
     299             :   },
     300             :   { 0x62fe, 0x4f70, 0x4edf }, // unit
     301             :   { 0x4e07, 0x4ebf },         // unit10K
     302             :   CHINESE,                    // lang
     303             :   false                       // informal
     304             : };
     305             : static const CJKIdeographicData gDataTradChineseInformal = {
     306             :   {                           // digit
     307             :     0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
     308             :     0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
     309             :   },
     310             :   { 0x5341, 0x767e, 0x5343 }, // unit
     311             :   { 0x842c, 0x5104 },         // unit10K
     312             :   CHINESE,                    // lang
     313             :   true                        // informal
     314             : };
     315             : static const CJKIdeographicData gDataTradChineseFormal = {
     316             :   {                           // digit
     317             :     0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x8086,
     318             :     0x4f0d, 0x9678, 0x67d2, 0x634c, 0x7396
     319             :   },
     320             :   { 0x62fe, 0x4f70, 0x4edf }, // unit
     321             :   { 0x842c, 0x5104 },         // unit10K
     322             :   CHINESE,                    // lang
     323             :   false                       // informal
     324             : };
     325             : 
     326             : static bool
     327           0 : CJKIdeographicToText(CounterValue aOrdinal, nsAString& aResult,
     328             :                      const CJKIdeographicData& data)
     329             : {
     330           0 :   NS_ASSERTION(aOrdinal >= 0, "Only accept non-negative ordinal");
     331             :   char16_t buf[NUM_BUF_SIZE];
     332           0 :   int32_t idx = NUM_BUF_SIZE;
     333           0 :   int32_t pos = 0;
     334           0 :   bool needZero = (aOrdinal == 0);
     335           0 :   int32_t unitidx = 0, unit10Kidx = 0;
     336           0 :   do {
     337           0 :     unitidx = pos % 4;
     338           0 :     if (unitidx == 0) {
     339           0 :       unit10Kidx = pos / 4;
     340             :     }
     341           0 :     auto cur = static_cast<MakeUnsigned<CounterValue>::Type>(aOrdinal) % 10;
     342           0 :     if (cur == 0) {
     343           0 :       if (needZero) {
     344           0 :         needZero = false;
     345           0 :         buf[--idx] = data.digit[0];
     346             :       }
     347             :     } else {
     348           0 :       if (data.lang == CHINESE) {
     349           0 :         needZero = true;
     350             :       }
     351           0 :       if (unit10Kidx != 0) {
     352           0 :         if (data.lang == KOREAN && idx != NUM_BUF_SIZE) {
     353           0 :           buf[--idx] = ' ';
     354             :         }
     355           0 :         buf[--idx] = data.unit10K[unit10Kidx - 1];
     356             :       }
     357           0 :       if (unitidx != 0) {
     358           0 :         buf[--idx] = data.unit[unitidx - 1];
     359             :       }
     360           0 :       if (cur != 1) {
     361           0 :         buf[--idx] = data.digit[cur];
     362             :       } else {
     363           0 :         bool needOne = true;
     364           0 :         if (data.informal) {
     365           0 :           switch (data.lang) {
     366             :             case CHINESE:
     367           0 :               if (unitidx == 1 &&
     368           0 :                   (aOrdinal == 1 || (pos > 4 && aOrdinal % 1000 == 1))) {
     369           0 :                 needOne = false;
     370             :               }
     371           0 :               break;
     372             :             case JAPANESE:
     373           0 :               if (unitidx > 0 &&
     374           0 :                   (unitidx != 3 || (pos == 3 && aOrdinal == 1))) {
     375           0 :                 needOne = false;
     376             :               }
     377           0 :               break;
     378             :             case KOREAN:
     379           0 :               if (unitidx > 0 || (pos == 4 && (aOrdinal % 1000) == 1)) {
     380           0 :                 needOne = false;
     381             :               }
     382           0 :               break;
     383             :           }
     384             :         }
     385           0 :         if (needOne) {
     386           0 :           buf[--idx] = data.digit[1];
     387             :         }
     388             :       }
     389           0 :       unit10Kidx = 0;
     390             :     }
     391           0 :     aOrdinal /= 10;
     392           0 :     pos++;
     393           0 :   } while (aOrdinal > 0);
     394           0 :   aResult.Assign(buf + idx, NUM_BUF_SIZE - idx);
     395           0 :   return true;
     396             : }
     397             : 
     398             : #define HEBREW_GERESH       0x05F3
     399             : static const char16_t gHebrewDigit[22] =
     400             : {
     401             :   //   1       2       3       4       5       6       7       8       9
     402             :   0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8,
     403             :   //  10      20      30      40      50      60      70      80      90
     404             :   0x05D9, 0x05DB, 0x05DC, 0x05DE, 0x05E0, 0x05E1, 0x05E2, 0x05E4, 0x05E6,
     405             :   // 100     200     300     400
     406             :   0x05E7, 0x05E8, 0x05E9, 0x05EA
     407             : };
     408             : 
     409             : static bool
     410           0 : HebrewToText(CounterValue aOrdinal, nsAString& aResult)
     411             : {
     412           0 :   if (aOrdinal < 1 || aOrdinal > 999999) {
     413           0 :     return false;
     414             :   }
     415             : 
     416           0 :   bool outputSep = false;
     417           0 :   nsAutoString allText, thousandsGroup;
     418           0 :   do {
     419           0 :     thousandsGroup.Truncate();
     420           0 :     int32_t n3 = aOrdinal % 1000;
     421             :     // Process digit for 100 - 900
     422           0 :     for(int32_t n1 = 400; n1 > 0; )
     423             :     {
     424           0 :       if( n3 >= n1)
     425             :       {
     426           0 :         n3 -= n1;
     427           0 :         thousandsGroup.Append(gHebrewDigit[(n1/100)-1+18]);
     428             :       } else {
     429           0 :         n1 -= 100;
     430             :       } // if
     431             :     } // for
     432             : 
     433             :     // Process digit for 10 - 90
     434             :     int32_t n2;
     435           0 :     if( n3 >= 10 )
     436             :     {
     437             :       // Special process for 15 and 16
     438           0 :       if(( 15 == n3 ) || (16 == n3)) {
     439             :         // Special rule for religious reason...
     440             :         // 15 is represented by 9 and 6, not 10 and 5
     441             :         // 16 is represented by 9 and 7, not 10 and 6
     442           0 :         n2 = 9;
     443           0 :         thousandsGroup.Append(gHebrewDigit[ n2 - 1]);
     444             :       } else {
     445           0 :         n2 = n3 - (n3 % 10);
     446           0 :         thousandsGroup.Append(gHebrewDigit[(n2/10)-1+9]);
     447             :       } // if
     448           0 :       n3 -= n2;
     449             :     } // if
     450             : 
     451             :     // Process digit for 1 - 9
     452           0 :     if ( n3 > 0)
     453           0 :       thousandsGroup.Append(gHebrewDigit[n3-1]);
     454           0 :     if (outputSep)
     455           0 :       thousandsGroup.Append((char16_t)HEBREW_GERESH);
     456           0 :     if (allText.IsEmpty())
     457           0 :       allText = thousandsGroup;
     458             :     else
     459           0 :       allText = thousandsGroup + allText;
     460           0 :     aOrdinal /= 1000;
     461           0 :     outputSep = true;
     462           0 :   } while (aOrdinal >= 1);
     463             : 
     464           0 :   aResult = allText;
     465           0 :   return true;
     466             : }
     467             : 
     468             : // Convert ordinal to Ethiopic numeric representation.
     469             : // The detail is available at http://www.ethiopic.org/Numerals/
     470             : // The algorithm used here is based on the pseudo-code put up there by
     471             : // Daniel Yacob <yacob@geez.org>.
     472             : // Another reference is Unicode 3.0 standard section 11.1.
     473             : #define ETHIOPIC_ONE             0x1369
     474             : #define ETHIOPIC_TEN             0x1372
     475             : #define ETHIOPIC_HUNDRED         0x137B
     476             : #define ETHIOPIC_TEN_THOUSAND    0x137C
     477             : 
     478             : static bool
     479           0 : EthiopicToText(CounterValue aOrdinal, nsAString& aResult)
     480             : {
     481           0 :   if (aOrdinal < 1) {
     482           0 :     return false;
     483             :   }
     484             : 
     485           0 :   nsAutoString asciiNumberString;      // decimal string representation of ordinal
     486           0 :   DecimalToText(aOrdinal, asciiNumberString);
     487           0 :   uint8_t asciiStringLength = asciiNumberString.Length();
     488             : 
     489             :   // If number length is odd, add a leading "0"
     490             :   // the leading "0" preconditions the string to always have the
     491             :   // leading tens place populated, this avoids a check within the loop.
     492             :   // If we didn't add the leading "0", decrement asciiStringLength so
     493             :   // it will be equivalent to a zero-based index in both cases.
     494           0 :   if (asciiStringLength & 1) {
     495           0 :     asciiNumberString.Insert(NS_LITERAL_STRING("0"), 0);
     496             :   } else {
     497           0 :     asciiStringLength--;
     498             :   }
     499             : 
     500           0 :   aResult.Truncate();
     501             :   // Iterate from the highest digits to lowest
     502             :   // indexFromLeft       indexes digits (0 = most significant)
     503             :   // groupIndexFromRight indexes pairs of digits (0 = least significant)
     504           0 :   for (uint8_t indexFromLeft = 0, groupIndexFromRight = asciiStringLength >> 1;
     505           0 :        indexFromLeft <= asciiStringLength;
     506           0 :        indexFromLeft += 2, groupIndexFromRight--) {
     507           0 :     uint8_t tensValue  = asciiNumberString.CharAt(indexFromLeft) & 0x0F;
     508           0 :     uint8_t unitsValue = asciiNumberString.CharAt(indexFromLeft + 1) & 0x0F;
     509           0 :     uint8_t groupValue = tensValue * 10 + unitsValue;
     510             : 
     511           0 :     bool oddGroup = (groupIndexFromRight & 1);
     512             : 
     513             :     // we want to clear ETHIOPIC_ONE when it is superfluous
     514           0 :     if (aOrdinal > 1 &&
     515           0 :         groupValue == 1 &&                  // one without a leading ten
     516           0 :         (oddGroup || indexFromLeft == 0)) { // preceding (100) or leading the sequence
     517           0 :       unitsValue = 0;
     518             :     }
     519             : 
     520             :     // put it all together...
     521           0 :     if (tensValue) {
     522             :       // map onto Ethiopic "tens":
     523           0 :       aResult.Append((char16_t) (tensValue +  ETHIOPIC_TEN - 1));
     524             :     }
     525           0 :     if (unitsValue) {
     526             :       //map onto Ethiopic "units":
     527           0 :       aResult.Append((char16_t) (unitsValue + ETHIOPIC_ONE - 1));
     528             :     }
     529             :     // Add a separator for all even groups except the last,
     530             :     // and for odd groups with non-zero value.
     531           0 :     if (oddGroup) {
     532           0 :       if (groupValue) {
     533           0 :         aResult.Append((char16_t) ETHIOPIC_HUNDRED);
     534             :       }
     535             :     } else {
     536           0 :       if (groupIndexFromRight) {
     537           0 :         aResult.Append((char16_t) ETHIOPIC_TEN_THOUSAND);
     538             :       }
     539             :     }
     540             :   }
     541           0 :   return true;
     542             : }
     543             : 
     544             : static uint8_t
     545           0 : GetDefaultSpeakAsForSystem(uint8_t aSystem)
     546             : {
     547           0 :   MOZ_ASSERT(aSystem != NS_STYLE_COUNTER_SYSTEM_EXTENDS,
     548             :              "Extends system does not have static default speak-as");
     549           0 :   switch (aSystem) {
     550             :     case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
     551           0 :       return NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT;
     552             :     case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
     553           0 :       return NS_STYLE_COUNTER_SPEAKAS_BULLETS;
     554             :     default:
     555           0 :       return NS_STYLE_COUNTER_SPEAKAS_NUMBERS;
     556             :   }
     557             : }
     558             : 
     559             : static bool
     560           0 : SystemUsesNegativeSign(uint8_t aSystem)
     561             : {
     562           0 :   MOZ_ASSERT(aSystem != NS_STYLE_COUNTER_SYSTEM_EXTENDS,
     563             :              "Cannot check this for extending style");
     564           0 :   switch (aSystem) {
     565             :     case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC:
     566             :     case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
     567             :     case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
     568             :     case NS_STYLE_COUNTER_SYSTEM_ADDITIVE:
     569           0 :       return true;
     570             :     default:
     571           0 :       return false;
     572             :   }
     573             : }
     574             : 
     575             : class BuiltinCounterStyle : public CounterStyle
     576             : {
     577             : public:
     578             :   friend class CounterStyleManager;
     579             : 
     580             :   // will be initialized by CounterStyleManager::InitializeBuiltinCounterStyles
     581             :   constexpr BuiltinCounterStyle()
     582             :     : CounterStyle(NS_STYLE_LIST_STYLE_NONE)
     583             :   {
     584             :   }
     585             : 
     586             : protected:
     587           0 :   constexpr explicit BuiltinCounterStyle(int32_t aStyle)
     588           0 :     : CounterStyle(aStyle)
     589             :   {
     590           0 :   }
     591             : 
     592             : public:
     593             :   virtual void GetStyleName(nsAString& aResult) override;
     594             :   virtual void GetPrefix(nsAString& aResult) override;
     595             :   virtual void GetSuffix(nsAString& aResult) override;
     596             :   virtual void GetSpokenCounterText(CounterValue aOrdinal,
     597             :                                     WritingMode aWritingMode,
     598             :                                     nsAString& aResult,
     599             :                                     bool& aIsBullet) override;
     600             :   virtual bool IsBullet() override;
     601             : 
     602             :   virtual void GetNegative(NegativeType& aResult) override;
     603             :   virtual bool IsOrdinalInRange(CounterValue aOrdinal) override;
     604             :   virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) override;
     605             :   virtual void GetPad(PadType& aResult) override;
     606             :   virtual CounterStyle* GetFallback() override;
     607             :   virtual uint8_t GetSpeakAs() override;
     608             :   virtual bool UseNegativeSign() override;
     609             : 
     610             :   virtual bool GetInitialCounterText(CounterValue aOrdinal,
     611             :                                      WritingMode aWritingMode,
     612             :                                      nsAString& aResult,
     613             :                                      bool& aIsRTL) override;
     614             : };
     615             : 
     616             : /* virtual */ void
     617           0 : BuiltinCounterStyle::GetStyleName(nsAString& aResult)
     618             : {
     619           0 :   MOZ_ASSERT(mStyle != NS_STYLE_LIST_STYLE_CUSTOM);
     620             :   const nsCString& str =
     621           0 :     nsCSSProps::ValueToKeyword(mStyle, nsCSSProps::kListStyleKTable);
     622           0 :   MOZ_ASSERT(!str.IsEmpty());
     623           0 :   aResult.Assign(NS_ConvertUTF8toUTF16(str));
     624           0 : }
     625             : 
     626             : /* virtual */ void
     627           0 : BuiltinCounterStyle::GetPrefix(nsAString& aResult)
     628             : {
     629           0 :   aResult.Truncate();
     630           0 : }
     631             : 
     632             : /* virtual */ void
     633           0 : BuiltinCounterStyle::GetSuffix(nsAString& aResult)
     634             : {
     635           0 :   switch (mStyle) {
     636             :     case NS_STYLE_LIST_STYLE_NONE:
     637           0 :       aResult.Truncate();
     638           0 :       break;
     639             : 
     640             :     case NS_STYLE_LIST_STYLE_DISC:
     641             :     case NS_STYLE_LIST_STYLE_CIRCLE:
     642             :     case NS_STYLE_LIST_STYLE_SQUARE:
     643             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
     644             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
     645             :     case NS_STYLE_LIST_STYLE_ETHIOPIC_NUMERIC:
     646           0 :       aResult = ' ';
     647           0 :       break;
     648             : 
     649             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
     650             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
     651             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
     652             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
     653             :     case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
     654             :     case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
     655           0 :       aResult = 0x3001;
     656           0 :       break;
     657             : 
     658             :     case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
     659             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
     660             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
     661           0 :       aResult.AssignLiteral(u", ");
     662           0 :       break;
     663             : 
     664             :     default:
     665           0 :       aResult.AssignLiteral(u". ");
     666           0 :       break;
     667             :   }
     668           0 : }
     669             : 
     670             : static const char16_t kDiscCharacter = 0x2022;
     671             : static const char16_t kCircleCharacter = 0x25e6;
     672             : static const char16_t kSquareCharacter = 0x25fe;
     673             : static const char16_t kRightPointingCharacter = 0x25b8;
     674             : static const char16_t kLeftPointingCharacter = 0x25c2;
     675             : static const char16_t kDownPointingCharacter = 0x25be;
     676             : 
     677             : /* virtual */ void
     678           0 : BuiltinCounterStyle::GetSpokenCounterText(CounterValue aOrdinal,
     679             :                                           WritingMode aWritingMode,
     680             :                                           nsAString& aResult,
     681             :                                           bool& aIsBullet)
     682             : {
     683           0 :   switch (mStyle) {
     684             :     case NS_STYLE_LIST_STYLE_NONE:
     685             :     case NS_STYLE_LIST_STYLE_DISC:
     686             :     case NS_STYLE_LIST_STYLE_CIRCLE:
     687             :     case NS_STYLE_LIST_STYLE_SQUARE:
     688             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
     689             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN: {
     690             :       // Same as the initial representation
     691             :       bool isRTL;
     692           0 :       GetInitialCounterText(aOrdinal, aWritingMode, aResult, isRTL);
     693           0 :       aIsBullet = true;
     694           0 :       break;
     695             :     }
     696             :     default:
     697           0 :       CounterStyle::GetSpokenCounterText(
     698           0 :           aOrdinal, aWritingMode, aResult, aIsBullet);
     699           0 :       break;
     700             :   }
     701           0 : }
     702             : 
     703             : /* virtual */ bool
     704           0 : BuiltinCounterStyle::IsBullet()
     705             : {
     706           0 :   switch (mStyle) {
     707             :     case NS_STYLE_LIST_STYLE_DISC:
     708             :     case NS_STYLE_LIST_STYLE_CIRCLE:
     709             :     case NS_STYLE_LIST_STYLE_SQUARE:
     710             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
     711             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
     712           0 :       return true;
     713             :     default:
     714           0 :       return false;
     715             :   }
     716             : }
     717             : 
     718             : static const char16_t gJapaneseNegative[] = {
     719             :   0x30de, 0x30a4, 0x30ca, 0x30b9, 0x0000
     720             : };
     721             : static const char16_t gKoreanNegative[] = {
     722             :   0xb9c8, 0xc774, 0xb108, 0xc2a4, 0x0020, 0x0000
     723             : };
     724             : static const char16_t gSimpChineseNegative[] = {
     725             :   0x8d1f, 0x0000
     726             : };
     727             : static const char16_t gTradChineseNegative[] = {
     728             :   0x8ca0, 0x0000
     729             : };
     730             : 
     731             : /* virtual */ void
     732           0 : BuiltinCounterStyle::GetNegative(NegativeType& aResult)
     733             : {
     734           0 :   switch (mStyle) {
     735             :     case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
     736             :     case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
     737           0 :       aResult.before = gJapaneseNegative;
     738           0 :       break;
     739             : 
     740             :     case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
     741             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
     742             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
     743           0 :       aResult.before = gKoreanNegative;
     744           0 :       break;
     745             : 
     746             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
     747             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
     748           0 :       aResult.before = gSimpChineseNegative;
     749           0 :       break;
     750             : 
     751             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
     752             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
     753           0 :       aResult.before = gTradChineseNegative;
     754           0 :       break;
     755             : 
     756             :     default:
     757           0 :       aResult.before.AssignLiteral(u"-");
     758             :   }
     759           0 :   aResult.after.Truncate();
     760           0 : }
     761             : 
     762             : /* virtual */ bool
     763           0 : BuiltinCounterStyle::IsOrdinalInRange(CounterValue aOrdinal)
     764             : {
     765           0 :   switch (mStyle) {
     766             :     default:
     767             :     // cyclic
     768             :     case NS_STYLE_LIST_STYLE_NONE:
     769             :     case NS_STYLE_LIST_STYLE_DISC:
     770             :     case NS_STYLE_LIST_STYLE_CIRCLE:
     771             :     case NS_STYLE_LIST_STYLE_SQUARE:
     772             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
     773             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
     774             :     // use DecimalToText
     775             :     case NS_STYLE_LIST_STYLE_DECIMAL:
     776             :     // use CJKIdeographicToText
     777             :     case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
     778             :     case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
     779             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
     780             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
     781             :     case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
     782             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
     783             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
     784             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
     785             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
     786           0 :       return true;
     787             : 
     788             :     // use EthiopicToText
     789             :     case NS_STYLE_LIST_STYLE_ETHIOPIC_NUMERIC:
     790           0 :       return aOrdinal >= 1;
     791             : 
     792             :     // use HebrewToText
     793             :     case NS_STYLE_LIST_STYLE_HEBREW:
     794           0 :       return aOrdinal >= 1 && aOrdinal <= 999999;
     795             :   }
     796             : }
     797             : 
     798             : /* virtual */ bool
     799           0 : BuiltinCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal)
     800             : {
     801           0 :   switch (mStyle) {
     802             :     // cyclic:
     803             :     case NS_STYLE_LIST_STYLE_NONE:
     804             :     case NS_STYLE_LIST_STYLE_DISC:
     805             :     case NS_STYLE_LIST_STYLE_CIRCLE:
     806             :     case NS_STYLE_LIST_STYLE_SQUARE:
     807             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
     808             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
     809             :     // numeric:
     810             :     case NS_STYLE_LIST_STYLE_DECIMAL:
     811           0 :       return true;
     812             : 
     813             :     // additive:
     814             :     case NS_STYLE_LIST_STYLE_HEBREW:
     815           0 :       return aOrdinal >= 0;
     816             : 
     817             :     // complex predefined:
     818             :     case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
     819             :     case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
     820             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
     821             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
     822             :     case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
     823             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
     824             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
     825             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
     826             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
     827             :     case NS_STYLE_LIST_STYLE_ETHIOPIC_NUMERIC:
     828           0 :       return IsOrdinalInRange(aOrdinal);
     829             : 
     830             :     default:
     831           0 :       NS_NOTREACHED("Unknown counter style");
     832           0 :       return false;
     833             :   }
     834             : }
     835             : 
     836             : /* virtual */ void
     837           0 : BuiltinCounterStyle::GetPad(PadType& aResult)
     838             : {
     839           0 :   aResult.width = 0;
     840           0 :   aResult.symbol.Truncate();
     841           0 : }
     842             : 
     843             : /* virtual */ CounterStyle*
     844           0 : BuiltinCounterStyle::GetFallback()
     845             : {
     846             :   // Fallback of dependent builtin counter styles are handled in class
     847             :   // DependentBuiltinCounterStyle.
     848           0 :   return CounterStyleManager::GetDecimalStyle();
     849             : }
     850             : 
     851             : /* virtual */ uint8_t
     852           0 : BuiltinCounterStyle::GetSpeakAs()
     853             : {
     854           0 :   switch (mStyle) {
     855             :     case NS_STYLE_LIST_STYLE_NONE:
     856             :     case NS_STYLE_LIST_STYLE_DISC:
     857             :     case NS_STYLE_LIST_STYLE_CIRCLE:
     858             :     case NS_STYLE_LIST_STYLE_SQUARE:
     859             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
     860             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
     861           0 :       return NS_STYLE_COUNTER_SPEAKAS_BULLETS;
     862             :     default:
     863           0 :       return NS_STYLE_COUNTER_SPEAKAS_NUMBERS;
     864             :   }
     865             : }
     866             : 
     867             : /* virtual */ bool
     868           0 : BuiltinCounterStyle::UseNegativeSign()
     869             : {
     870           0 :   switch (mStyle) {
     871             :     case NS_STYLE_LIST_STYLE_NONE:
     872             :     case NS_STYLE_LIST_STYLE_DISC:
     873             :     case NS_STYLE_LIST_STYLE_CIRCLE:
     874             :     case NS_STYLE_LIST_STYLE_SQUARE:
     875             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
     876             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
     877           0 :       return false;
     878             :     default:
     879           0 :       return true;
     880             :   }
     881             : }
     882             : 
     883             : /* virtual */ bool
     884           0 : BuiltinCounterStyle::GetInitialCounterText(CounterValue aOrdinal,
     885             :                                            WritingMode aWritingMode,
     886             :                                            nsAString& aResult,
     887             :                                            bool& aIsRTL)
     888             : {
     889           0 :   aIsRTL = false;
     890           0 :   switch (mStyle) {
     891             :     // used by counters & extends counter-style code only
     892             :     // XXX We really need to do this the same way we do list bullets.
     893             :     case NS_STYLE_LIST_STYLE_NONE:
     894           0 :       aResult.Truncate();
     895           0 :       return true;
     896             :     case NS_STYLE_LIST_STYLE_DISC:
     897           0 :       aResult.Assign(kDiscCharacter);
     898           0 :       return true;
     899             :     case NS_STYLE_LIST_STYLE_CIRCLE:
     900           0 :       aResult.Assign(kCircleCharacter);
     901           0 :       return true;
     902             :     case NS_STYLE_LIST_STYLE_SQUARE:
     903           0 :       aResult.Assign(kSquareCharacter);
     904           0 :       return true;
     905             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
     906           0 :       if (aWritingMode.IsVertical()) {
     907           0 :         aResult.Assign(kDownPointingCharacter);
     908           0 :       } else if (aWritingMode.IsBidiLTR()) {
     909           0 :         aResult.Assign(kRightPointingCharacter);
     910             :       } else {
     911           0 :         aResult.Assign(kLeftPointingCharacter);
     912             :       }
     913           0 :       return true;
     914             :     case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
     915           0 :       if (!aWritingMode.IsVertical()) {
     916           0 :         aResult.Assign(kDownPointingCharacter);
     917           0 :       } else if (aWritingMode.IsVerticalLR()) {
     918           0 :         aResult.Assign(kRightPointingCharacter);
     919             :       } else {
     920           0 :         aResult.Assign(kLeftPointingCharacter);
     921             :       }
     922           0 :       return true;
     923             : 
     924             :     case NS_STYLE_LIST_STYLE_DECIMAL:
     925           0 :       return DecimalToText(aOrdinal, aResult);
     926             : 
     927             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
     928           0 :       return CJKIdeographicToText(aOrdinal, aResult, gDataTradChineseInformal);
     929             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
     930           0 :       return CJKIdeographicToText(aOrdinal, aResult, gDataTradChineseFormal);
     931             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
     932           0 :       return CJKIdeographicToText(aOrdinal, aResult, gDataSimpChineseInformal);
     933             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
     934           0 :       return CJKIdeographicToText(aOrdinal, aResult, gDataSimpChineseFormal);
     935             :     case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
     936           0 :       return CJKIdeographicToText(aOrdinal, aResult, gDataJapaneseInformal);
     937             :     case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
     938           0 :       return CJKIdeographicToText(aOrdinal, aResult, gDataJapaneseFormal);
     939             :     case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
     940           0 :       return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHangulFormal);
     941             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
     942           0 :       return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHanjaInformal);
     943             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
     944           0 :       return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHanjaFormal);
     945             : 
     946             :     case NS_STYLE_LIST_STYLE_HEBREW:
     947           0 :       aIsRTL = true;
     948           0 :       return HebrewToText(aOrdinal, aResult);
     949             : 
     950             :     case NS_STYLE_LIST_STYLE_ETHIOPIC_NUMERIC:
     951           0 :       return EthiopicToText(aOrdinal, aResult);
     952             : 
     953             :     default:
     954           0 :       NS_NOTREACHED("Unknown builtin counter style");
     955           0 :       return false;
     956             :   }
     957             : }
     958             : 
     959             : class DependentBuiltinCounterStyle final : public BuiltinCounterStyle
     960             : {
     961             : public:
     962           0 :   DependentBuiltinCounterStyle(int32_t aStyle, CounterStyleManager* aManager)
     963           0 :     : BuiltinCounterStyle(aStyle),
     964           0 :       mManager(aManager)
     965             :   {
     966           0 :     NS_ASSERTION(IsDependentStyle(), "Not a dependent builtin style");
     967           0 :     MOZ_ASSERT(!IsCustomStyle(), "Not a builtin style");
     968           0 :   }
     969             : 
     970             :   virtual CounterStyle* GetFallback() override;
     971             : 
     972           0 :   void* operator new(size_t sz, nsPresContext* aPresContext)
     973             :   {
     974           0 :     return aPresContext->PresShell()->AllocateByObjectID(
     975           0 :         eArenaObjectID_DependentBuiltinCounterStyle, sz);
     976             :   }
     977             : 
     978           0 :   void Destroy()
     979             :   {
     980           0 :     nsIPresShell* shell = mManager->PresContext()->PresShell();
     981           0 :     this->~DependentBuiltinCounterStyle();
     982           0 :     shell->FreeByObjectID(eArenaObjectID_DependentBuiltinCounterStyle, this);
     983           0 :   }
     984             : 
     985             : private:
     986           0 :   ~DependentBuiltinCounterStyle() {}
     987             : 
     988             :   CounterStyleManager* mManager;
     989             : };
     990             : 
     991             : /* virtual */ CounterStyle*
     992           0 : DependentBuiltinCounterStyle::GetFallback()
     993             : {
     994           0 :   switch (GetStyle()) {
     995             :     case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
     996             :     case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
     997             :     case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
     998             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
     999             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
    1000             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
    1001             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
    1002             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
    1003             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
    1004             :       // These styles all have a larger range than cjk-decimal, so the
    1005             :       // only case fallback is accessed is that they are extended.
    1006             :       // Since extending styles will cache the data themselves, we need
    1007             :       // not cache it here.
    1008           0 :       return mManager->BuildCounterStyle(nsGkAtoms::cjkDecimal);
    1009             :     default:
    1010           0 :       NS_NOTREACHED("Not a valid dependent builtin style");
    1011           0 :       return BuiltinCounterStyle::GetFallback();
    1012             :   }
    1013             : }
    1014             : 
    1015             : class CustomCounterStyle final : public CounterStyle
    1016             : {
    1017             : public:
    1018           0 :   CustomCounterStyle(nsIAtom* aName,
    1019             :                      CounterStyleManager* aManager,
    1020             :                      nsCSSCounterStyleRule* aRule)
    1021           0 :     : CounterStyle(NS_STYLE_LIST_STYLE_CUSTOM),
    1022             :       mName(aName),
    1023             :       mManager(aManager),
    1024             :       mRule(aRule),
    1025           0 :       mRuleGeneration(aRule->GetGeneration()),
    1026           0 :       mSystem(aRule->GetSystem()),
    1027             :       mFlags(0),
    1028             :       mFallback(nullptr),
    1029             :       mSpeakAsCounter(nullptr),
    1030             :       mExtends(nullptr),
    1031           0 :       mExtendsRoot(nullptr)
    1032             :   {
    1033           0 :   }
    1034             : 
    1035             :   // This method will clear all cached data in the style and update the
    1036             :   // generation number of the rule. It should be called when the rule of
    1037             :   // this style is changed.
    1038             :   void ResetCachedData();
    1039             : 
    1040             :   // This method will reset all cached data which may depend on other
    1041             :   // counter style. It will reset all pointers to other counter styles.
    1042             :   // For counter style extends other, in addition, all fields will be
    1043             :   // reset to uninitialized state. This method should be called when any
    1044             :   // other counter style is added, removed, or changed.
    1045             :   void ResetDependentData();
    1046             : 
    1047           0 :   nsCSSCounterStyleRule* GetRule() const { return mRule; }
    1048           0 :   uint32_t GetRuleGeneration() const { return mRuleGeneration; }
    1049             : 
    1050             :   virtual void GetStyleName(nsAString& aResult) override;
    1051             :   virtual void GetPrefix(nsAString& aResult) override;
    1052             :   virtual void GetSuffix(nsAString& aResult) override;
    1053             :   virtual void GetSpokenCounterText(CounterValue aOrdinal,
    1054             :                                     WritingMode aWritingMode,
    1055             :                                     nsAString& aResult,
    1056             :                                     bool& aIsBullet) override;
    1057             :   virtual bool IsBullet() override;
    1058             : 
    1059             :   virtual void GetNegative(NegativeType& aResult) override;
    1060             :   virtual bool IsOrdinalInRange(CounterValue aOrdinal) override;
    1061             :   virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) override;
    1062             :   virtual void GetPad(PadType& aResult) override;
    1063             :   virtual CounterStyle* GetFallback() override;
    1064             :   virtual uint8_t GetSpeakAs() override;
    1065             :   virtual bool UseNegativeSign() override;
    1066             : 
    1067             :   virtual void CallFallbackStyle(CounterValue aOrdinal,
    1068             :                                  WritingMode aWritingMode,
    1069             :                                  nsAString& aResult,
    1070             :                                  bool& aIsRTL) override;
    1071             :   virtual bool GetInitialCounterText(CounterValue aOrdinal,
    1072             :                                      WritingMode aWritingMode,
    1073             :                                      nsAString& aResult,
    1074             :                                      bool& aIsRTL) override;
    1075             : 
    1076           0 :   bool IsExtendsSystem()
    1077             :   {
    1078           0 :     return mSystem == NS_STYLE_COUNTER_SYSTEM_EXTENDS;
    1079             :   }
    1080             : 
    1081           0 :   void* operator new(size_t sz, nsPresContext* aPresContext)
    1082             :   {
    1083           0 :     return aPresContext->PresShell()->AllocateByObjectID(
    1084           0 :         eArenaObjectID_CustomCounterStyle, sz);
    1085             :   }
    1086             : 
    1087           0 :   void Destroy()
    1088             :   {
    1089           0 :     nsIPresShell* shell = mManager->PresContext()->PresShell();
    1090           0 :     this->~CustomCounterStyle();
    1091           0 :     shell->FreeByObjectID(eArenaObjectID_CustomCounterStyle, this);
    1092           0 :   }
    1093             : 
    1094             : private:
    1095           0 :   ~CustomCounterStyle() {}
    1096             : 
    1097             :   const nsTArray<nsString>& GetSymbols();
    1098             :   const nsTArray<AdditiveSymbol>& GetAdditiveSymbols();
    1099             : 
    1100             :   // The speak-as values of counter styles may form a loop, and the
    1101             :   // loops may have complex interaction with the loop formed by
    1102             :   // extending. To solve this problem, the computation of speak-as is
    1103             :   // divided into two phases:
    1104             :   // 1. figure out the raw value, by ComputeRawSpeakAs, and
    1105             :   // 2. eliminate loop, by ComputeSpeakAs.
    1106             :   // See comments before the definitions of these methods for details.
    1107             :   uint8_t GetSpeakAsAutoValue();
    1108             :   void ComputeRawSpeakAs(uint8_t& aSpeakAs,
    1109             :                          CounterStyle*& aSpeakAsCounter);
    1110             :   CounterStyle* ComputeSpeakAs();
    1111             : 
    1112             :   CounterStyle* ComputeExtends();
    1113             :   CounterStyle* GetExtends();
    1114             :   CounterStyle* GetExtendsRoot();
    1115             : 
    1116             :   nsCOMPtr<nsIAtom> mName;
    1117             : 
    1118             :   // CounterStyleManager should always overlive any CounterStyle as it
    1119             :   // is owned by nsPresContext, and will be released after all nodes and
    1120             :   // frames are released.
    1121             :   CounterStyleManager* mManager;
    1122             : 
    1123             :   RefPtr<nsCSSCounterStyleRule> mRule;
    1124             :   uint32_t mRuleGeneration;
    1125             : 
    1126             :   uint8_t mSystem;
    1127             :   // GetSpeakAs will ensure that private member mSpeakAs is initialized before used
    1128             :   MOZ_INIT_OUTSIDE_CTOR uint8_t mSpeakAs;
    1129             : 
    1130             :   enum {
    1131             :     // loop detection
    1132             :     FLAG_EXTENDS_VISITED = 1 << 0,
    1133             :     FLAG_EXTENDS_LOOP    = 1 << 1,
    1134             :     FLAG_SPEAKAS_VISITED  = 1 << 2,
    1135             :     FLAG_SPEAKAS_LOOP     = 1 << 3,
    1136             :     // field status
    1137             :     FLAG_NEGATIVE_INITED  = 1 << 4,
    1138             :     FLAG_PREFIX_INITED    = 1 << 5,
    1139             :     FLAG_SUFFIX_INITED    = 1 << 6,
    1140             :     FLAG_PAD_INITED       = 1 << 7,
    1141             :     FLAG_SPEAKAS_INITED   = 1 << 8,
    1142             :   };
    1143             :   uint16_t mFlags;
    1144             : 
    1145             :   // Fields below will be initialized when necessary.
    1146             :   nsTArray<nsString> mSymbols;
    1147             :   nsTArray<AdditiveSymbol> mAdditiveSymbols;
    1148             :   NegativeType mNegative;
    1149             :   nsString mPrefix, mSuffix;
    1150             :   PadType mPad;
    1151             : 
    1152             :   // CounterStyleManager will guarantee that none of the pointers below
    1153             :   // refers to a freed CounterStyle. There are two possible cases where
    1154             :   // the manager will release its reference to a CounterStyle: 1. the
    1155             :   // manager itself is released, 2. a rule is invalidated. In the first
    1156             :   // case, all counter style are removed from the manager, and should
    1157             :   // also have been dereferenced from other objects. All styles will be
    1158             :   // released all together. In the second case, CounterStyleManager::
    1159             :   // NotifyRuleChanged will guarantee that all pointers will be reset
    1160             :   // before any CounterStyle is released.
    1161             : 
    1162             :   CounterStyle* mFallback;
    1163             :   // This field refers to the last counter in a speak-as chain.
    1164             :   // That counter must not speak as another counter.
    1165             :   CounterStyle* mSpeakAsCounter;
    1166             : 
    1167             :   CounterStyle* mExtends;
    1168             :   // This field refers to the last counter in the extends chain. The
    1169             :   // counter must be either a builtin style or a style whose system is
    1170             :   // not 'extends'.
    1171             :   CounterStyle* mExtendsRoot;
    1172             : };
    1173             : 
    1174             : void
    1175           0 : CustomCounterStyle::ResetCachedData()
    1176             : {
    1177           0 :   mSymbols.Clear();
    1178           0 :   mAdditiveSymbols.Clear();
    1179           0 :   mFlags &= ~(FLAG_NEGATIVE_INITED |
    1180             :               FLAG_PREFIX_INITED |
    1181             :               FLAG_SUFFIX_INITED |
    1182             :               FLAG_PAD_INITED |
    1183           0 :               FLAG_SPEAKAS_INITED);
    1184           0 :   mFallback = nullptr;
    1185           0 :   mSpeakAsCounter = nullptr;
    1186           0 :   mExtends = nullptr;
    1187           0 :   mExtendsRoot = nullptr;
    1188           0 :   mRuleGeneration = mRule->GetGeneration();
    1189           0 : }
    1190             : 
    1191             : void
    1192           0 : CustomCounterStyle::ResetDependentData()
    1193             : {
    1194           0 :   mFlags &= ~FLAG_SPEAKAS_INITED;
    1195           0 :   mSpeakAsCounter = nullptr;
    1196           0 :   mFallback = nullptr;
    1197           0 :   mExtends = nullptr;
    1198           0 :   mExtendsRoot = nullptr;
    1199           0 :   if (IsExtendsSystem()) {
    1200           0 :     mFlags &= ~(FLAG_NEGATIVE_INITED |
    1201             :                 FLAG_PREFIX_INITED |
    1202             :                 FLAG_SUFFIX_INITED |
    1203           0 :                 FLAG_PAD_INITED);
    1204             :   }
    1205           0 : }
    1206             : 
    1207             : /* virtual */ void
    1208           0 : CustomCounterStyle::GetStyleName(nsAString& aResult)
    1209             : {
    1210           0 :   nsDependentAtomString name(mName);
    1211           0 :   aResult.Assign(name);
    1212           0 : }
    1213             : 
    1214             : /* virtual */ void
    1215           0 : CustomCounterStyle::GetPrefix(nsAString& aResult)
    1216             : {
    1217           0 :   if (!(mFlags & FLAG_PREFIX_INITED)) {
    1218           0 :     mFlags |= FLAG_PREFIX_INITED;
    1219             : 
    1220           0 :     const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Prefix);
    1221           0 :     if (value.UnitHasStringValue()) {
    1222           0 :       value.GetStringValue(mPrefix);
    1223           0 :     } else if (IsExtendsSystem()) {
    1224           0 :       GetExtends()->GetPrefix(mPrefix);
    1225             :     } else {
    1226           0 :       mPrefix.Truncate();
    1227             :     }
    1228             :   }
    1229           0 :   aResult = mPrefix;
    1230           0 : }
    1231             : 
    1232             : /* virtual */ void
    1233           0 : CustomCounterStyle::GetSuffix(nsAString& aResult)
    1234             : {
    1235           0 :   if (!(mFlags & FLAG_SUFFIX_INITED)) {
    1236           0 :     mFlags |= FLAG_SUFFIX_INITED;
    1237             : 
    1238           0 :     const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Suffix);
    1239           0 :     if (value.UnitHasStringValue()) {
    1240           0 :       value.GetStringValue(mSuffix);
    1241           0 :     } else if (IsExtendsSystem()) {
    1242           0 :       GetExtends()->GetSuffix(mSuffix);
    1243             :     } else {
    1244           0 :       mSuffix.AssignLiteral(u". ");
    1245             :     }
    1246             :   }
    1247           0 :   aResult = mSuffix;
    1248           0 : }
    1249             : 
    1250             : /* virtual */ void
    1251           0 : CustomCounterStyle::GetSpokenCounterText(CounterValue aOrdinal,
    1252             :                                          WritingMode aWritingMode,
    1253             :                                          nsAString& aResult,
    1254             :                                          bool& aIsBullet)
    1255             : {
    1256           0 :   if (GetSpeakAs() != NS_STYLE_COUNTER_SPEAKAS_OTHER) {
    1257           0 :     CounterStyle::GetSpokenCounterText(
    1258           0 :         aOrdinal, aWritingMode, aResult, aIsBullet);
    1259             :   } else {
    1260           0 :     MOZ_ASSERT(mSpeakAsCounter,
    1261             :                "mSpeakAsCounter should have been initialized.");
    1262           0 :     mSpeakAsCounter->GetSpokenCounterText(
    1263           0 :         aOrdinal, aWritingMode, aResult, aIsBullet);
    1264             :   }
    1265           0 : }
    1266             : 
    1267             : /* virtual */ bool
    1268           0 : CustomCounterStyle::IsBullet()
    1269             : {
    1270           0 :   switch (mSystem) {
    1271             :     case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
    1272             :       // Only use ::-moz-list-bullet for cyclic system
    1273           0 :       return true;
    1274             :     case NS_STYLE_COUNTER_SYSTEM_EXTENDS:
    1275           0 :       return GetExtendsRoot()->IsBullet();
    1276             :     default:
    1277           0 :       return false;
    1278             :   }
    1279             : }
    1280             : 
    1281             : /* virtual */ void
    1282           0 : CustomCounterStyle::GetNegative(NegativeType& aResult)
    1283             : {
    1284           0 :   if (!(mFlags & FLAG_NEGATIVE_INITED)) {
    1285           0 :     mFlags |= FLAG_NEGATIVE_INITED;
    1286           0 :     const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Negative);
    1287           0 :     switch (value.GetUnit()) {
    1288             :       case eCSSUnit_Ident:
    1289             :       case eCSSUnit_String:
    1290           0 :         value.GetStringValue(mNegative.before);
    1291           0 :         mNegative.after.Truncate();
    1292           0 :         break;
    1293             :       case eCSSUnit_Pair: {
    1294           0 :         const nsCSSValuePair& pair = value.GetPairValue();
    1295           0 :         pair.mXValue.GetStringValue(mNegative.before);
    1296           0 :         pair.mYValue.GetStringValue(mNegative.after);
    1297           0 :         break;
    1298             :       }
    1299             :       default: {
    1300           0 :         if (IsExtendsSystem()) {
    1301           0 :           GetExtends()->GetNegative(mNegative);
    1302             :         } else {
    1303           0 :           mNegative.before.AssignLiteral(u"-");
    1304           0 :           mNegative.after.Truncate();
    1305             :         }
    1306             :       }
    1307             :     }
    1308             :   }
    1309           0 :   aResult = mNegative;
    1310           0 : }
    1311             : 
    1312             : static inline bool
    1313           0 : IsRangeValueInfinite(const nsCSSValue& aValue)
    1314             : {
    1315           0 :   return aValue.GetUnit() == eCSSUnit_Enumerated &&
    1316           0 :          aValue.GetIntValue() == NS_STYLE_COUNTER_RANGE_INFINITE;
    1317             : }
    1318             : 
    1319             : /* virtual */ bool
    1320           0 : CustomCounterStyle::IsOrdinalInRange(CounterValue aOrdinal)
    1321             : {
    1322           0 :   const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Range);
    1323           0 :   if (value.GetUnit() == eCSSUnit_PairList) {
    1324           0 :     for (const nsCSSValuePairList* item = value.GetPairListValue();
    1325           0 :          item != nullptr; item = item->mNext) {
    1326           0 :       const nsCSSValue& lowerBound = item->mXValue;
    1327           0 :       const nsCSSValue& upperBound = item->mYValue;
    1328           0 :       if ((IsRangeValueInfinite(lowerBound) ||
    1329           0 :            aOrdinal >= lowerBound.GetIntValue()) &&
    1330           0 :           (IsRangeValueInfinite(upperBound) ||
    1331           0 :            aOrdinal <= upperBound.GetIntValue())) {
    1332           0 :         return true;
    1333             :       }
    1334             :     }
    1335           0 :     return false;
    1336           0 :   } else if (IsExtendsSystem() && value.GetUnit() == eCSSUnit_None) {
    1337             :     // Only use the range of extended style when 'range' is not specified.
    1338           0 :     return GetExtends()->IsOrdinalInRange(aOrdinal);
    1339             :   }
    1340           0 :   return IsOrdinalInAutoRange(aOrdinal);
    1341             : }
    1342             : 
    1343             : /* virtual */ bool
    1344           0 : CustomCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal)
    1345             : {
    1346           0 :   switch (mSystem) {
    1347             :     case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
    1348             :     case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
    1349             :     case NS_STYLE_COUNTER_SYSTEM_FIXED:
    1350           0 :       return true;
    1351             :     case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
    1352             :     case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC:
    1353           0 :       return aOrdinal >= 1;
    1354             :     case NS_STYLE_COUNTER_SYSTEM_ADDITIVE:
    1355           0 :       return aOrdinal >= 0;
    1356             :     case NS_STYLE_COUNTER_SYSTEM_EXTENDS:
    1357           0 :       return GetExtendsRoot()->IsOrdinalInAutoRange(aOrdinal);
    1358             :     default:
    1359           0 :       NS_NOTREACHED("Invalid system for computing auto value.");
    1360           0 :       return false;
    1361             :   }
    1362             : }
    1363             : 
    1364             : /* virtual */ void
    1365           0 : CustomCounterStyle::GetPad(PadType& aResult)
    1366             : {
    1367           0 :   if (!(mFlags & FLAG_PAD_INITED)) {
    1368           0 :     mFlags |= FLAG_PAD_INITED;
    1369           0 :     const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Pad);
    1370           0 :     if (value.GetUnit() == eCSSUnit_Pair) {
    1371           0 :       const nsCSSValuePair& pair = value.GetPairValue();
    1372           0 :       mPad.width = pair.mXValue.GetIntValue();
    1373           0 :       pair.mYValue.GetStringValue(mPad.symbol);
    1374           0 :     } else if (IsExtendsSystem()) {
    1375           0 :       GetExtends()->GetPad(mPad);
    1376             :     } else {
    1377           0 :       mPad.width = 0;
    1378           0 :       mPad.symbol.Truncate();
    1379             :     }
    1380             :   }
    1381           0 :   aResult = mPad;
    1382           0 : }
    1383             : 
    1384             : /* virtual */ CounterStyle*
    1385           0 : CustomCounterStyle::GetFallback()
    1386             : {
    1387           0 :   if (!mFallback) {
    1388           0 :     const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Fallback);
    1389           0 :     mFallback = CounterStyleManager::GetDecimalStyle();
    1390           0 :     if (value.GetUnit() != eCSSUnit_Null) {
    1391           0 :       if (value.GetUnit() == eCSSUnit_AtomIdent) {
    1392           0 :         mFallback = mManager->BuildCounterStyle(value.GetAtomValue());
    1393             :       } else {
    1394           0 :         MOZ_ASSERT_UNREACHABLE("Unknown unit!");
    1395             :       }
    1396           0 :     } else if (IsExtendsSystem()) {
    1397           0 :       mFallback = GetExtends()->GetFallback();
    1398             :     }
    1399             :   }
    1400           0 :   return mFallback;
    1401             : }
    1402             : 
    1403             : /* virtual */ uint8_t
    1404           0 : CustomCounterStyle::GetSpeakAs()
    1405             : {
    1406           0 :   if (!(mFlags & FLAG_SPEAKAS_INITED)) {
    1407           0 :     ComputeSpeakAs();
    1408             :   }
    1409           0 :   return mSpeakAs;
    1410             : }
    1411             : 
    1412             : /* virtual */ bool
    1413           0 : CustomCounterStyle::UseNegativeSign()
    1414             : {
    1415           0 :   if (mSystem == NS_STYLE_COUNTER_SYSTEM_EXTENDS) {
    1416           0 :     return GetExtendsRoot()->UseNegativeSign();
    1417             :   }
    1418           0 :   return SystemUsesNegativeSign(mSystem);
    1419             : }
    1420             : 
    1421             : /* virtual */ void
    1422           0 : CustomCounterStyle::CallFallbackStyle(CounterValue aOrdinal,
    1423             :                                       WritingMode aWritingMode,
    1424             :                                       nsAString& aResult,
    1425             :                                       bool& aIsRTL)
    1426             : {
    1427           0 :   CounterStyle* fallback = GetFallback();
    1428             :   // If it recursively falls back to this counter style again,
    1429             :   // it will then fallback to decimal to break the loop.
    1430           0 :   mFallback = CounterStyleManager::GetDecimalStyle();
    1431           0 :   fallback->GetCounterText(aOrdinal, aWritingMode, aResult, aIsRTL);
    1432           0 :   mFallback = fallback;
    1433           0 : }
    1434             : 
    1435             : /* virtual */ bool
    1436           0 : CustomCounterStyle::GetInitialCounterText(CounterValue aOrdinal,
    1437             :                                           WritingMode aWritingMode,
    1438             :                                           nsAString& aResult,
    1439             :                                           bool& aIsRTL)
    1440             : {
    1441           0 :   switch (mSystem) {
    1442             :     case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
    1443           0 :       return GetCyclicCounterText(aOrdinal, aResult, GetSymbols());
    1444             :     case NS_STYLE_COUNTER_SYSTEM_FIXED: {
    1445           0 :       int32_t start = mRule->GetSystemArgument().GetIntValue();
    1446           0 :       return GetFixedCounterText(aOrdinal, aResult, start, GetSymbols());
    1447             :     }
    1448             :     case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC:
    1449           0 :       return GetSymbolicCounterText(aOrdinal, aResult, GetSymbols());
    1450             :     case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
    1451           0 :       return GetAlphabeticCounterText(aOrdinal, aResult, GetSymbols());
    1452             :     case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
    1453           0 :       return GetNumericCounterText(aOrdinal, aResult, GetSymbols());
    1454             :     case NS_STYLE_COUNTER_SYSTEM_ADDITIVE:
    1455           0 :       return GetAdditiveCounterText(aOrdinal, aResult, GetAdditiveSymbols());
    1456             :     case NS_STYLE_COUNTER_SYSTEM_EXTENDS:
    1457           0 :       return GetExtendsRoot()->
    1458           0 :         GetInitialCounterText(aOrdinal, aWritingMode, aResult, aIsRTL);
    1459             :     default:
    1460           0 :       NS_NOTREACHED("Invalid system.");
    1461           0 :       return false;
    1462             :   }
    1463             : }
    1464             : 
    1465             : const nsTArray<nsString>&
    1466           0 : CustomCounterStyle::GetSymbols()
    1467             : {
    1468           0 :   if (mSymbols.IsEmpty()) {
    1469           0 :     const nsCSSValue& values = mRule->GetDesc(eCSSCounterDesc_Symbols);
    1470           0 :     for (const nsCSSValueList* item = values.GetListValue();
    1471           0 :          item; item = item->mNext) {
    1472           0 :       nsString* symbol = mSymbols.AppendElement();
    1473           0 :       item->mValue.GetStringValue(*symbol);
    1474             :     }
    1475           0 :     mSymbols.Compact();
    1476             :   }
    1477           0 :   return mSymbols;
    1478             : }
    1479             : 
    1480             : const nsTArray<AdditiveSymbol>&
    1481           0 : CustomCounterStyle::GetAdditiveSymbols()
    1482             : {
    1483           0 :   if (mAdditiveSymbols.IsEmpty()) {
    1484           0 :     const nsCSSValue& values = mRule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
    1485           0 :     for (const nsCSSValuePairList* item = values.GetPairListValue();
    1486           0 :          item; item = item->mNext) {
    1487           0 :       AdditiveSymbol* symbol = mAdditiveSymbols.AppendElement();
    1488           0 :       symbol->weight = item->mXValue.GetIntValue();
    1489           0 :       item->mYValue.GetStringValue(symbol->symbol);
    1490             :     }
    1491           0 :     mAdditiveSymbols.Compact();
    1492             :   }
    1493           0 :   return mAdditiveSymbols;
    1494             : }
    1495             : 
    1496             : // This method is used to provide the computed value for 'auto'.
    1497             : uint8_t
    1498           0 : CustomCounterStyle::GetSpeakAsAutoValue()
    1499             : {
    1500           0 :   uint8_t system = mSystem;
    1501           0 :   if (IsExtendsSystem()) {
    1502           0 :     CounterStyle* root = GetExtendsRoot();
    1503           0 :     if (!root->IsCustomStyle()) {
    1504             :       // It is safe to call GetSpeakAs on non-custom style.
    1505           0 :       return root->GetSpeakAs();
    1506             :     }
    1507           0 :     system = static_cast<CustomCounterStyle*>(root)->mSystem;
    1508             :   }
    1509           0 :   return GetDefaultSpeakAsForSystem(system);
    1510             : }
    1511             : 
    1512             : // This method corresponds to the first stage of computation of the
    1513             : // value of speak-as. It will extract the value from the rule and
    1514             : // possibly recursively call itself on the extended style to figure
    1515             : // out the raw value. To keep things clear, this method is designed to
    1516             : // have no side effects (but functions it calls may still affect other
    1517             : // fields in the style.)
    1518             : void
    1519           0 : CustomCounterStyle::ComputeRawSpeakAs(uint8_t& aSpeakAs,
    1520             :                                     CounterStyle*& aSpeakAsCounter)
    1521             : {
    1522           0 :   NS_ASSERTION(!(mFlags & FLAG_SPEAKAS_INITED),
    1523             :                "ComputeRawSpeakAs is called with speak-as inited.");
    1524             : 
    1525           0 :   const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_SpeakAs);
    1526           0 :   switch (value.GetUnit()) {
    1527             :     case eCSSUnit_Auto:
    1528           0 :       aSpeakAs = GetSpeakAsAutoValue();
    1529           0 :       break;
    1530             :     case eCSSUnit_Enumerated:
    1531           0 :       aSpeakAs = value.GetIntValue();
    1532           0 :       break;
    1533             :     case eCSSUnit_AtomIdent:
    1534           0 :       aSpeakAs = NS_STYLE_COUNTER_SPEAKAS_OTHER;
    1535           0 :       aSpeakAsCounter = mManager->BuildCounterStyle(value.GetAtomValue());
    1536           0 :       break;
    1537             :     case eCSSUnit_Null: {
    1538           0 :       if (!IsExtendsSystem()) {
    1539           0 :         aSpeakAs = GetSpeakAsAutoValue();
    1540             :       } else {
    1541           0 :         CounterStyle* extended = GetExtends();
    1542           0 :         if (!extended->IsCustomStyle()) {
    1543             :           // It is safe to call GetSpeakAs on non-custom style.
    1544           0 :           aSpeakAs = extended->GetSpeakAs();
    1545             :         } else {
    1546             :           CustomCounterStyle* custom =
    1547           0 :             static_cast<CustomCounterStyle*>(extended);
    1548           0 :           if (!(custom->mFlags & FLAG_SPEAKAS_INITED)) {
    1549           0 :             custom->ComputeRawSpeakAs(aSpeakAs, aSpeakAsCounter);
    1550             :           } else {
    1551           0 :             aSpeakAs = custom->mSpeakAs;
    1552           0 :             aSpeakAsCounter = custom->mSpeakAsCounter;
    1553             :           }
    1554             :         }
    1555             :       }
    1556           0 :       break;
    1557             :     }
    1558             :     default:
    1559           0 :       NS_NOTREACHED("Invalid speak-as value");
    1560             :   }
    1561           0 : }
    1562             : 
    1563             : // This method corresponds to the second stage of getting speak-as
    1564             : // related values. It will recursively figure out the final value of
    1565             : // mSpeakAs and mSpeakAsCounter. This method returns nullptr if the
    1566             : // caller is in a loop, and the root counter style in the chain
    1567             : // otherwise. It use the same loop detection algorithm as
    1568             : // CustomCounterStyle::ComputeExtends, see comments before that
    1569             : // method for more details.
    1570             : CounterStyle*
    1571           0 : CustomCounterStyle::ComputeSpeakAs()
    1572             : {
    1573           0 :   if (mFlags & FLAG_SPEAKAS_INITED) {
    1574           0 :     if (mSpeakAs == NS_STYLE_COUNTER_SPEAKAS_OTHER) {
    1575           0 :       return mSpeakAsCounter;
    1576             :     }
    1577           0 :     return this;
    1578             :   }
    1579             : 
    1580           0 :   if (mFlags & FLAG_SPEAKAS_VISITED) {
    1581             :     // loop detected
    1582           0 :     mFlags |= FLAG_SPEAKAS_LOOP;
    1583           0 :     return nullptr;
    1584             :   }
    1585             : 
    1586             :   CounterStyle* speakAsCounter;
    1587           0 :   ComputeRawSpeakAs(mSpeakAs, speakAsCounter);
    1588             : 
    1589           0 :   bool inLoop = false;
    1590           0 :   if (mSpeakAs != NS_STYLE_COUNTER_SPEAKAS_OTHER) {
    1591           0 :     mSpeakAsCounter = nullptr;
    1592           0 :   } else if (!speakAsCounter->IsCustomStyle()) {
    1593           0 :     mSpeakAsCounter = speakAsCounter;
    1594             :   } else {
    1595           0 :     mFlags |= FLAG_SPEAKAS_VISITED;
    1596             :     CounterStyle* target =
    1597           0 :       static_cast<CustomCounterStyle*>(speakAsCounter)->ComputeSpeakAs();
    1598           0 :     mFlags &= ~FLAG_SPEAKAS_VISITED;
    1599             : 
    1600           0 :     if (target) {
    1601           0 :       NS_ASSERTION(!(mFlags & FLAG_SPEAKAS_LOOP),
    1602             :                    "Invalid state for speak-as loop detecting");
    1603           0 :       mSpeakAsCounter = target;
    1604             :     } else {
    1605           0 :       mSpeakAs = GetSpeakAsAutoValue();
    1606           0 :       mSpeakAsCounter = nullptr;
    1607           0 :       if (mFlags & FLAG_SPEAKAS_LOOP) {
    1608           0 :         mFlags &= ~FLAG_SPEAKAS_LOOP;
    1609             :       } else {
    1610           0 :         inLoop = true;
    1611             :       }
    1612             :     }
    1613             :   }
    1614             : 
    1615           0 :   mFlags |= FLAG_SPEAKAS_INITED;
    1616           0 :   if (inLoop) {
    1617           0 :     return nullptr;
    1618             :   }
    1619           0 :   return mSpeakAsCounter ? mSpeakAsCounter : this;
    1620             : }
    1621             : 
    1622             : // This method will recursively figure out mExtends in the whole chain.
    1623             : // It will return nullptr if the caller is in a loop, and return this
    1624             : // otherwise. To detect the loop, this method marks the style VISITED
    1625             : // before the recursive call. When a VISITED style is reached again, the
    1626             : // loop is detected, and flag LOOP will be marked on the first style in
    1627             : // loop. mExtends of all counter styles in loop will be set to decimal
    1628             : // according to the spec.
    1629             : CounterStyle*
    1630           0 : CustomCounterStyle::ComputeExtends()
    1631             : {
    1632           0 :   if (!IsExtendsSystem() || mExtends) {
    1633           0 :     return this;
    1634             :   }
    1635           0 :   if (mFlags & FLAG_EXTENDS_VISITED) {
    1636             :     // loop detected
    1637           0 :     mFlags |= FLAG_EXTENDS_LOOP;
    1638           0 :     return nullptr;
    1639             :   }
    1640             : 
    1641           0 :   const nsCSSValue& value = mRule->GetSystemArgument();
    1642           0 :   CounterStyle* nextCounter = mManager->BuildCounterStyle(value.GetAtomValue());
    1643           0 :   CounterStyle* target = nextCounter;
    1644           0 :   if (nextCounter->IsCustomStyle()) {
    1645           0 :     mFlags |= FLAG_EXTENDS_VISITED;
    1646           0 :     target = static_cast<CustomCounterStyle*>(nextCounter)->ComputeExtends();
    1647           0 :     mFlags &= ~FLAG_EXTENDS_VISITED;
    1648             :   }
    1649             : 
    1650           0 :   if (target) {
    1651           0 :     NS_ASSERTION(!(mFlags & FLAG_EXTENDS_LOOP),
    1652             :                  "Invalid state for extends loop detecting");
    1653           0 :     mExtends = nextCounter;
    1654           0 :     return this;
    1655             :   } else {
    1656           0 :     mExtends = CounterStyleManager::GetDecimalStyle();
    1657           0 :     if (mFlags & FLAG_EXTENDS_LOOP) {
    1658           0 :       mFlags &= ~FLAG_EXTENDS_LOOP;
    1659           0 :       return this;
    1660             :     } else {
    1661           0 :       return nullptr;
    1662             :     }
    1663             :   }
    1664             : }
    1665             : 
    1666             : CounterStyle*
    1667           0 : CustomCounterStyle::GetExtends()
    1668             : {
    1669           0 :   if (!mExtends) {
    1670             :     // Any extends loop will be eliminated in the method below.
    1671           0 :     ComputeExtends();
    1672             :   }
    1673           0 :   return mExtends;
    1674             : }
    1675             : 
    1676             : CounterStyle*
    1677           0 : CustomCounterStyle::GetExtendsRoot()
    1678             : {
    1679           0 :   if (!mExtendsRoot) {
    1680           0 :     CounterStyle* extended = GetExtends();
    1681           0 :     mExtendsRoot = extended;
    1682           0 :     if (extended->IsCustomStyle()) {
    1683           0 :       CustomCounterStyle* custom = static_cast<CustomCounterStyle*>(extended);
    1684           0 :       if (custom->IsExtendsSystem()) {
    1685             :         // This will make mExtendsRoot in the whole extends chain be
    1686             :         // set recursively, which could save work when part of a chain
    1687             :         // is shared by multiple counter styles.
    1688           0 :         mExtendsRoot = custom->GetExtendsRoot();
    1689             :       }
    1690             :     }
    1691             :   }
    1692           0 :   return mExtendsRoot;
    1693             : }
    1694             : 
    1695           0 : AnonymousCounterStyle::AnonymousCounterStyle(const nsAString& aContent)
    1696             :   : CounterStyle(NS_STYLE_LIST_STYLE_CUSTOM)
    1697             :   , mSingleString(true)
    1698           0 :   , mSystem(NS_STYLE_COUNTER_SYSTEM_CYCLIC)
    1699             : {
    1700           0 :   mSymbols.SetCapacity(1);
    1701           0 :   mSymbols.AppendElement(aContent);
    1702           0 : }
    1703             : 
    1704             : static nsTArray<nsString>
    1705           0 : CollectSymbolsFromCSSValueList(const nsCSSValueList* aList)
    1706             : {
    1707           0 :   nsTArray<nsString> symbols;
    1708           0 :   for (const nsCSSValueList* item = aList; item; item = item->mNext) {
    1709           0 :     item->mValue.GetStringValue(*symbols.AppendElement());
    1710             :   }
    1711           0 :   symbols.Compact();
    1712           0 :   return symbols;
    1713             : }
    1714             : 
    1715           0 : AnonymousCounterStyle::AnonymousCounterStyle(const nsCSSValue::Array* aParams)
    1716             :   : AnonymousCounterStyle(
    1717           0 :       aParams->Item(0).GetIntValue(),
    1718           0 :       CollectSymbolsFromCSSValueList(aParams->Item(1).GetListValue()))
    1719             : {
    1720           0 : }
    1721             : 
    1722           0 : AnonymousCounterStyle::AnonymousCounterStyle(uint8_t aSystem,
    1723           0 :                                              nsTArray<nsString> aSymbols)
    1724             :   : CounterStyle(NS_STYLE_LIST_STYLE_CUSTOM)
    1725             :   , mSingleString(false)
    1726             :   , mSystem(aSystem)
    1727           0 :   , mSymbols(Move(aSymbols))
    1728             : {
    1729           0 : }
    1730             : 
    1731             : /* virtual */ void
    1732           0 : AnonymousCounterStyle::GetStyleName(nsAString& aResult)
    1733             : {
    1734           0 :   aResult.Truncate();
    1735           0 : }
    1736             : 
    1737             : /* virtual */ void
    1738           0 : AnonymousCounterStyle::GetPrefix(nsAString& aResult)
    1739             : {
    1740           0 :   aResult.Truncate();
    1741           0 : }
    1742             : 
    1743             : /* virtual */ void
    1744           0 : AnonymousCounterStyle::GetSuffix(nsAString& aResult)
    1745             : {
    1746           0 :   if (IsSingleString()) {
    1747           0 :     aResult.Truncate();
    1748             :   } else {
    1749           0 :     aResult = ' ';
    1750             :   }
    1751           0 : }
    1752             : 
    1753             : /* virtual */ bool
    1754           0 : AnonymousCounterStyle::IsBullet()
    1755             : {
    1756           0 :   switch (mSystem) {
    1757             :     case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
    1758             :       // Only use ::-moz-list-bullet for cyclic system
    1759           0 :       return true;
    1760             :     default:
    1761           0 :       return false;
    1762             :   }
    1763             : }
    1764             : 
    1765             : /* virtual */ void
    1766           0 : AnonymousCounterStyle::GetNegative(NegativeType& aResult)
    1767             : {
    1768           0 :   aResult.before.AssignLiteral(u"-");
    1769           0 :   aResult.after.Truncate();
    1770           0 : }
    1771             : 
    1772             : /* virtual */ bool
    1773           0 : AnonymousCounterStyle::IsOrdinalInRange(CounterValue aOrdinal)
    1774             : {
    1775           0 :   switch (mSystem) {
    1776             :     case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
    1777             :     case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
    1778             :     case NS_STYLE_COUNTER_SYSTEM_FIXED:
    1779           0 :       return true;
    1780             :     case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
    1781             :     case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC:
    1782           0 :       return aOrdinal >= 1;
    1783             :     default:
    1784           0 :       NS_NOTREACHED("Invalid system.");
    1785           0 :       return false;
    1786             :   }
    1787             : }
    1788             : 
    1789             : /* virtual */ bool
    1790           0 : AnonymousCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal)
    1791             : {
    1792           0 :   return AnonymousCounterStyle::IsOrdinalInRange(aOrdinal);
    1793             : }
    1794             : 
    1795             : /* virtual */ void
    1796           0 : AnonymousCounterStyle::GetPad(PadType& aResult)
    1797             : {
    1798           0 :   aResult.width = 0;
    1799           0 :   aResult.symbol.Truncate();
    1800           0 : }
    1801             : 
    1802             : /* virtual */ CounterStyle*
    1803           0 : AnonymousCounterStyle::GetFallback()
    1804             : {
    1805           0 :   return CounterStyleManager::GetDecimalStyle();
    1806             : }
    1807             : 
    1808             : /* virtual */ uint8_t
    1809           0 : AnonymousCounterStyle::GetSpeakAs()
    1810             : {
    1811           0 :   return GetDefaultSpeakAsForSystem(mSystem);
    1812             : }
    1813             : 
    1814             : /* virtual */ bool
    1815           0 : AnonymousCounterStyle::UseNegativeSign()
    1816             : {
    1817           0 :   return SystemUsesNegativeSign(mSystem);
    1818             : }
    1819             : 
    1820             : /* virtual */ bool
    1821           0 : AnonymousCounterStyle::GetInitialCounterText(CounterValue aOrdinal,
    1822             :                                              WritingMode aWritingMode,
    1823             :                                              nsAString& aResult,
    1824             :                                              bool& aIsRTL)
    1825             : {
    1826           0 :   switch (mSystem) {
    1827             :     case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
    1828           0 :       return GetCyclicCounterText(aOrdinal, aResult, mSymbols);
    1829             :     case NS_STYLE_COUNTER_SYSTEM_FIXED:
    1830           0 :       return GetFixedCounterText(aOrdinal, aResult, 1, mSymbols);
    1831             :     case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC:
    1832           0 :       return GetSymbolicCounterText(aOrdinal, aResult, mSymbols);
    1833             :     case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
    1834           0 :       return GetAlphabeticCounterText(aOrdinal, aResult, mSymbols);
    1835             :     case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
    1836           0 :       return GetNumericCounterText(aOrdinal, aResult, mSymbols);
    1837             :     default:
    1838           0 :       NS_NOTREACHED("Invalid system.");
    1839           0 :       return false;
    1840             :   }
    1841             : }
    1842             : 
    1843             : bool
    1844          99 : CounterStyle::IsDependentStyle() const
    1845             : {
    1846          99 :   switch (mStyle) {
    1847             :     // CustomCounterStyle
    1848             :     case NS_STYLE_LIST_STYLE_CUSTOM:
    1849             :     // DependentBuiltinCounterStyle
    1850             :     case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
    1851             :     case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
    1852             :     case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
    1853             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
    1854             :     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
    1855             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
    1856             :     case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
    1857             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
    1858             :     case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
    1859           0 :       return true;
    1860             : 
    1861             :     // BuiltinCounterStyle
    1862             :     default:
    1863          99 :       return false;
    1864             :   }
    1865             : }
    1866             : 
    1867             : void
    1868           0 : CounterStyle::GetCounterText(CounterValue aOrdinal,
    1869             :                              WritingMode aWritingMode,
    1870             :                              nsAString& aResult,
    1871             :                              bool& aIsRTL)
    1872             : {
    1873           0 :   bool success = IsOrdinalInRange(aOrdinal);
    1874           0 :   aIsRTL = false;
    1875             : 
    1876           0 :   if (success) {
    1877             :     // generate initial representation
    1878           0 :     bool useNegativeSign = UseNegativeSign();
    1879           0 :     nsAutoString initialText;
    1880             :     CounterValue ordinal;
    1881           0 :     if (!useNegativeSign) {
    1882           0 :       ordinal = aOrdinal;
    1883             :     } else {
    1884           0 :       CheckedInt<CounterValue> absolute(Abs(aOrdinal));
    1885           0 :       ordinal = absolute.isValid() ?
    1886             :         absolute.value() : std::numeric_limits<CounterValue>::max();
    1887             :     }
    1888             :     success = GetInitialCounterText(
    1889           0 :         ordinal, aWritingMode, initialText, aIsRTL);
    1890             : 
    1891             :     // add pad & negative, build the final result
    1892           0 :     if (success) {
    1893           0 :       PadType pad;
    1894           0 :       GetPad(pad);
    1895             :       // We have to calculate the difference here since suffix part of negative
    1896             :       // sign may be appended to initialText later.
    1897           0 :       int32_t diff = pad.width -
    1898           0 :         unicode::CountGraphemeClusters(initialText.Data(),
    1899           0 :                                        initialText.Length());
    1900           0 :       aResult.Truncate();
    1901           0 :       if (useNegativeSign && aOrdinal < 0) {
    1902           0 :         NegativeType negative;
    1903           0 :         GetNegative(negative);
    1904           0 :         aResult.Append(negative.before);
    1905             :         // There is nothing between the suffix part of negative and initial
    1906             :         // representation, so we append it directly here.
    1907           0 :         initialText.Append(negative.after);
    1908             :       }
    1909           0 :       if (diff > 0) {
    1910           0 :         auto length = pad.symbol.Length();
    1911           0 :         if (diff > LENGTH_LIMIT || length > LENGTH_LIMIT ||
    1912           0 :             diff * length > LENGTH_LIMIT) {
    1913           0 :           success = false;
    1914           0 :         } else if (length > 0) {
    1915           0 :           for (int32_t i = 0; i < diff; ++i) {
    1916           0 :             aResult.Append(pad.symbol);
    1917             :           }
    1918             :         }
    1919             :       }
    1920           0 :       if (success) {
    1921           0 :         aResult.Append(initialText);
    1922             :       }
    1923             :     }
    1924             :   }
    1925             : 
    1926           0 :   if (!success) {
    1927           0 :     CallFallbackStyle(aOrdinal, aWritingMode, aResult, aIsRTL);
    1928             :   }
    1929           0 : }
    1930             : 
    1931             : /* virtual */ void
    1932           0 : CounterStyle::GetSpokenCounterText(CounterValue aOrdinal,
    1933             :                                    WritingMode aWritingMode,
    1934             :                                    nsAString& aResult,
    1935             :                                    bool& aIsBullet)
    1936             : {
    1937             :   bool isRTL; // we don't care about direction for spoken text
    1938           0 :   aIsBullet = false;
    1939           0 :   switch (GetSpeakAs()) {
    1940             :     case NS_STYLE_COUNTER_SPEAKAS_BULLETS:
    1941           0 :       aResult.Assign(kDiscCharacter);
    1942           0 :       aIsBullet = true;
    1943           0 :       break;
    1944             :     case NS_STYLE_COUNTER_SPEAKAS_NUMBERS:
    1945           0 :       DecimalToText(aOrdinal, aResult);
    1946           0 :       break;
    1947             :     case NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT:
    1948             :       // we currently do not actually support 'spell-out',
    1949             :       // so 'words' is used instead.
    1950             :     case NS_STYLE_COUNTER_SPEAKAS_WORDS:
    1951           0 :       GetCounterText(aOrdinal, WritingMode(), aResult, isRTL);
    1952           0 :       break;
    1953             :     case NS_STYLE_COUNTER_SPEAKAS_OTHER:
    1954             :       // This should be processed by CustomCounterStyle
    1955           0 :       NS_NOTREACHED("Invalid speak-as value");
    1956           0 :       break;
    1957             :     default:
    1958           0 :       NS_NOTREACHED("Unknown speak-as value");
    1959           0 :       break;
    1960             :   }
    1961           0 : }
    1962             : 
    1963             : /* virtual */ void
    1964           0 : CounterStyle::CallFallbackStyle(CounterValue aOrdinal,
    1965             :                                 WritingMode aWritingMode,
    1966             :                                 nsAString& aResult,
    1967             :                                 bool& aIsRTL)
    1968             : {
    1969           0 :   GetFallback()->GetCounterText(aOrdinal, aWritingMode, aResult, aIsRTL);
    1970           0 : }
    1971             : 
    1972             : static BuiltinCounterStyle gBuiltinStyleTable[NS_STYLE_LIST_STYLE__MAX];
    1973             : 
    1974          28 : CounterStyleManager::CounterStyleManager(nsPresContext* aPresContext)
    1975          28 :   : mPresContext(aPresContext)
    1976             : {
    1977             :   // Insert the static styles into cache table
    1978          28 :   mStyles.Put(nsGkAtoms::none, GetNoneStyle());
    1979          28 :   mStyles.Put(nsGkAtoms::decimal, GetDecimalStyle());
    1980          28 :   mStyles.Put(nsGkAtoms::disc, GetDiscStyle());
    1981          28 : }
    1982             : 
    1983           8 : CounterStyleManager::~CounterStyleManager()
    1984             : {
    1985           4 :   MOZ_ASSERT(!mPresContext, "Disconnect should have been called");
    1986           4 : }
    1987             : 
    1988             : /* static */ void
    1989           3 : CounterStyleManager::InitializeBuiltinCounterStyles()
    1990             : {
    1991          57 :   for (uint32_t i = 0; i < NS_STYLE_LIST_STYLE__MAX; ++i) {
    1992          54 :     gBuiltinStyleTable[i].mStyle = i;
    1993             :   }
    1994           3 : }
    1995             : 
    1996             : void
    1997           0 : CounterStyleManager::DestroyCounterStyle(CounterStyle* aCounterStyle)
    1998             : {
    1999           0 :   if (aCounterStyle->IsCustomStyle()) {
    2000           0 :     MOZ_ASSERT(!aCounterStyle->AsAnonymous(), "Anonymous counter styles "
    2001             :                "are not managed by CounterStyleManager");
    2002           0 :     static_cast<CustomCounterStyle*>(aCounterStyle)->Destroy();
    2003           0 :   } else if (aCounterStyle->IsDependentStyle()) {
    2004           0 :     static_cast<DependentBuiltinCounterStyle*>(aCounterStyle)->Destroy();
    2005             :   } else {
    2006           0 :     MOZ_ASSERT_UNREACHABLE("Builtin counter styles should not be destroyed");
    2007             :   }
    2008           0 : }
    2009             : 
    2010             : void
    2011           4 : CounterStyleManager::Disconnect()
    2012             : {
    2013           4 :   CleanRetiredStyles();
    2014          16 :   for (auto iter = mStyles.Iter(); !iter.Done(); iter.Next()) {
    2015          12 :     CounterStyle* style = iter.Data();
    2016          12 :     if (style->IsDependentStyle()) {
    2017           0 :       DestroyCounterStyle(style);
    2018             :     }
    2019             :   }
    2020           4 :   mStyles.Clear();
    2021           4 :   mPresContext = nullptr;
    2022           4 : }
    2023             : 
    2024             : CounterStyle*
    2025           0 : CounterStyleManager::BuildCounterStyle(nsIAtom* aName)
    2026             : {
    2027           0 :   MOZ_ASSERT(NS_IsMainThread());
    2028           0 :   CounterStyle* data = GetCounterStyle(aName);
    2029           0 :   if (data) {
    2030           0 :     return data;
    2031             :   }
    2032             : 
    2033             :   // It is intentional that the predefined names are case-insensitive
    2034             :   // but the user-defined names case-sensitive.
    2035           0 :   StyleSetHandle styleSet = mPresContext->StyleSet();
    2036           0 :   nsCSSCounterStyleRule* rule = styleSet->CounterStyleRuleForName(aName);
    2037           0 :   if (rule) {
    2038           0 :     MOZ_ASSERT(rule->Name() == aName);
    2039           0 :     data = new (mPresContext) CustomCounterStyle(aName, this, rule);
    2040             :   } else {
    2041             :     int32_t type;
    2042           0 :     nsDependentAtomString name(aName);
    2043           0 :     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(name);
    2044           0 :     if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kListStyleKTable, type)) {
    2045           0 :       if (gBuiltinStyleTable[type].IsDependentStyle()) {
    2046           0 :         data = new (mPresContext) DependentBuiltinCounterStyle(type, this);
    2047             :       } else {
    2048           0 :         data = GetBuiltinStyle(type);
    2049             :       }
    2050             :     }
    2051             :   }
    2052           0 :   if (!data) {
    2053           0 :     data = GetDecimalStyle();
    2054             :   }
    2055           0 :   mStyles.Put(aName, data);
    2056           0 :   return data;
    2057             : }
    2058             : 
    2059             : /* static */ CounterStyle*
    2060          87 : CounterStyleManager::GetBuiltinStyle(int32_t aStyle)
    2061             : {
    2062          87 :   MOZ_ASSERT(0 <= aStyle && aStyle < NS_STYLE_LIST_STYLE__MAX,
    2063             :              "Require a valid builtin style constant");
    2064          87 :   MOZ_ASSERT(!gBuiltinStyleTable[aStyle].IsDependentStyle(),
    2065             :              "Cannot get dependent builtin style");
    2066          87 :   return &gBuiltinStyleTable[aStyle];
    2067             : }
    2068             : 
    2069             : bool
    2070           0 : CounterStyleManager::NotifyRuleChanged()
    2071             : {
    2072           0 :   bool changed = false;
    2073           0 :   for (auto iter = mStyles.Iter(); !iter.Done(); iter.Next()) {
    2074           0 :     CounterStyle* style = iter.Data();
    2075           0 :     bool toBeUpdated = false;
    2076           0 :     bool toBeRemoved = false;
    2077           0 :     StyleSetHandle styleSet = mPresContext->StyleSet();
    2078           0 :     nsCSSCounterStyleRule* newRule = styleSet->CounterStyleRuleForName(iter.Key());
    2079           0 :     if (!newRule) {
    2080           0 :       if (style->IsCustomStyle()) {
    2081           0 :         toBeRemoved = true;
    2082             :       }
    2083             :     } else {
    2084           0 :       if (!style->IsCustomStyle()) {
    2085           0 :         toBeRemoved = true;
    2086             :       } else {
    2087           0 :         auto custom = static_cast<CustomCounterStyle*>(style);
    2088           0 :         if (custom->GetRule() != newRule) {
    2089           0 :           toBeRemoved = true;
    2090           0 :         } else if (custom->GetRuleGeneration() != newRule->GetGeneration()) {
    2091           0 :           toBeUpdated = true;
    2092           0 :           custom->ResetCachedData();
    2093             :         }
    2094             :       }
    2095             :     }
    2096           0 :     changed = changed || toBeUpdated || toBeRemoved;
    2097           0 :     if (toBeRemoved) {
    2098           0 :       if (style->IsDependentStyle()) {
    2099             :         // Add object to retired list so we can clean them up later.
    2100           0 :         mRetiredStyles.AppendElement(style);
    2101             :       }
    2102           0 :       iter.Remove();
    2103             :     }
    2104             :   }
    2105             : 
    2106           0 :   if (changed) {
    2107           0 :     for (auto iter = mStyles.Iter(); !iter.Done(); iter.Next()) {
    2108           0 :       CounterStyle* style = iter.Data();
    2109           0 :       if (style->IsCustomStyle()) {
    2110           0 :         CustomCounterStyle* custom = static_cast<CustomCounterStyle*>(style);
    2111           0 :         custom->ResetDependentData();
    2112             :       }
    2113             :       // There is no dependent data cached in DependentBuiltinCounterStyle
    2114             :       // instances, so we don't need to reset their data.
    2115             :     }
    2116             :   }
    2117           0 :   return changed;
    2118             : }
    2119             : 
    2120             : void
    2121           4 : CounterStyleManager::CleanRetiredStyles()
    2122             : {
    2123           8 :   nsTArray<CounterStyle*> list(Move(mRetiredStyles));
    2124           4 :   for (CounterStyle* style : list) {
    2125           0 :     DestroyCounterStyle(style);
    2126             :   }
    2127           4 : }
    2128             : 
    2129             : } // namespace mozilla

Generated by: LCOV version 1.13