Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
      5  * Copyright (C) 2006 Andrew Wellington (proton (at) wiretapped.net)
      6  * Copyright (C) 2010 Daniel Bates (dbates (at) intudata.com)
      7  *
      8  * This library is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU Library General Public
     10  * License as published by the Free Software Foundation; either
     11  * version 2 of the License, or (at your option) any later version.
     12  *
     13  * This library is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  * Library General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU Library General Public License
     19  * along with this library; see the file COPYING.LIB.  If not, write to
     20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21  * Boston, MA 02110-1301, USA.
     22  *
     23  */
     24 
     25 #include "config.h"
     26 #include "RenderListMarker.h"
     27 
     28 #include "CachedImage.h"
     29 #include "Document.h"
     30 #include "GraphicsContext.h"
     31 #include "RenderLayer.h"
     32 #include "RenderListItem.h"
     33 #include "RenderView.h"
     34 #include "TextRun.h"
     35 #include <wtf/unicode/CharacterNames.h>
     36 
     37 using namespace std;
     38 using namespace WTF;
     39 using namespace Unicode;
     40 
     41 namespace WebCore {
     42 
     43 const int cMarkerPadding = 7;
     44 
     45 enum SequenceType { NumericSequence, AlphabeticSequence };
     46 
     47 static String toRoman(int number, bool upper)
     48 {
     49     // FIXME: CSS3 describes how to make this work for much larger numbers,
     50     // using overbars and special characters. It also specifies the characters
     51     // in the range U+2160 to U+217F instead of standard ASCII ones.
     52     ASSERT(number >= 1 && number <= 3999);
     53 
     54     // Big enough to store largest roman number less than 3999 which
     55     // is 3888 (MMMDCCCLXXXVIII)
     56     const int lettersSize = 15;
     57     UChar letters[lettersSize];
     58 
     59     int length = 0;
     60     const UChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
     61     const UChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
     62     const UChar* digits = upper ? udigits : ldigits;
     63     int d = 0;
     64     do {
     65         int num = number % 10;
     66         if (num % 5 < 4)
     67             for (int i = num % 5; i > 0; i--)
     68                 letters[lettersSize - ++length] = digits[d];
     69         if (num >= 4 && num <= 8)
     70             letters[lettersSize - ++length] = digits[d + 1];
     71         if (num == 9)
     72             letters[lettersSize - ++length] = digits[d + 2];
     73         if (num % 5 == 4)
     74             letters[lettersSize - ++length] = digits[d];
     75         number /= 10;
     76         d += 2;
     77     } while (number);
     78 
     79     ASSERT(length <= lettersSize);
     80     return String(&letters[lettersSize - length], length);
     81 }
     82 
     83 static inline String toAlphabeticOrNumeric(int number, const UChar* sequence, unsigned sequenceSize, SequenceType type)
     84 {
     85     ASSERT(sequenceSize >= 2);
     86 
     87     const int lettersSize = sizeof(number) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
     88 
     89     UChar letters[lettersSize];
     90 
     91     bool isNegativeNumber = false;
     92     unsigned numberShadow = number;
     93     if (type == AlphabeticSequence) {
     94         ASSERT(number > 0);
     95         --numberShadow;
     96     } else if (number < 0) {
     97         numberShadow = -number;
     98         isNegativeNumber = true;
     99     }
    100     letters[lettersSize - 1] = sequence[numberShadow % sequenceSize];
    101     int length = 1;
    102 
    103     if (type == AlphabeticSequence) {
    104         while ((numberShadow /= sequenceSize) > 0) {
    105             --numberShadow;
    106             letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
    107         }
    108     } else {
    109         while ((numberShadow /= sequenceSize) > 0)
    110             letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
    111     }
    112     if (isNegativeNumber)
    113         letters[lettersSize - ++length] = hyphenMinus;
    114 
    115     ASSERT(length <= lettersSize);
    116     return String(&letters[lettersSize - length], length);
    117 }
    118 
    119 static String toSymbolic(int number, const UChar* symbols, unsigned symbolsSize)
    120 {
    121     ASSERT(number > 0);
    122     ASSERT(symbolsSize >= 1);
    123     unsigned numberShadow = number;
    124     --numberShadow;
    125 
    126     // The asterisks list-style-type is the worst case; we show |numberShadow| asterisks.
    127     Vector<UChar> letters;
    128     letters.append(symbols[numberShadow % symbolsSize]);
    129     unsigned numSymbols = numberShadow / symbolsSize;
    130     while (numSymbols--)
    131         letters.append(symbols[numberShadow % symbolsSize]);
    132     return String::adopt(letters);
    133 }
    134 
    135 static String toAlphabetic(int number, const UChar* alphabet, unsigned alphabetSize)
    136 {
    137     return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence);
    138 }
    139 
    140 static String toNumeric(int number, const UChar* numerals, unsigned numeralsSize)
    141 {
    142     return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence);
    143 }
    144 
    145 template <size_t size> static inline String toAlphabetic(int number, const UChar(&alphabet)[size])
    146 {
    147     return toAlphabetic(number, alphabet, size);
    148 }
    149 
    150 template <size_t size> static inline String toNumeric(int number, const UChar(&alphabet)[size])
    151 {
    152     return toNumeric(number, alphabet, size);
    153 }
    154 
    155 template <size_t size> static inline String toSymbolic(int number, const UChar(&alphabet)[size])
    156 {
    157     return toSymbolic(number, alphabet, size);
    158 }
    159 
    160 static int toHebrewUnder1000(int number, UChar letters[5])
    161 {
    162     // FIXME: CSS3 mentions various refinements not implemented here.
    163     // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
    164     ASSERT(number >= 0 && number < 1000);
    165     int length = 0;
    166     int fourHundreds = number / 400;
    167     for (int i = 0; i < fourHundreds; i++)
    168         letters[length++] = 1511 + 3;
    169     number %= 400;
    170     if (number / 100)
    171         letters[length++] = 1511 + (number / 100) - 1;
    172     number %= 100;
    173     if (number == 15 || number == 16) {
    174         letters[length++] = 1487 + 9;
    175         letters[length++] = 1487 + number - 9;
    176     } else {
    177         if (int tens = number / 10) {
    178             static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
    179             letters[length++] = hebrewTens[tens - 1];
    180         }
    181         if (int ones = number % 10)
    182             letters[length++] = 1487 + ones;
    183     }
    184     ASSERT(length <= 5);
    185     return length;
    186 }
    187 
    188 static String toHebrew(int number)
    189 {
    190     // FIXME: CSS3 mentions ways to make this work for much larger numbers.
    191     ASSERT(number >= 0 && number <= 999999);
    192 
    193     if (number == 0) {
    194         static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
    195         return String(hebrewZero, 3);
    196     }
    197 
    198     const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
    199     UChar letters[lettersSize];
    200 
    201     int length;
    202     if (number < 1000)
    203         length = 0;
    204     else {
    205         length = toHebrewUnder1000(number / 1000, letters);
    206         letters[length++] = '\'';
    207         number = number % 1000;
    208     }
    209     length += toHebrewUnder1000(number, letters + length);
    210 
    211     ASSERT(length <= lettersSize);
    212     return String(letters, length);
    213 }
    214 
    215 static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
    216 {
    217     ASSERT(number >= 0 && number < 10000);
    218     int length = 0;
    219 
    220     int lowerOffset = upper ? 0 : 0x0030;
    221 
    222     if (int thousands = number / 1000) {
    223         if (thousands == 7) {
    224             letters[length++] = 0x0548 + lowerOffset;
    225             letters[length++] = 0x0552 + lowerOffset;
    226             if (addCircumflex)
    227                 letters[length++] = 0x0302;
    228         } else {
    229             letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
    230             if (addCircumflex)
    231                 letters[length++] = 0x0302;
    232         }
    233     }
    234 
    235     if (int hundreds = (number / 100) % 10) {
    236         letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
    237         if (addCircumflex)
    238             letters[length++] = 0x0302;
    239     }
    240 
    241     if (int tens = (number / 10) % 10) {
    242         letters[length++] = (0x053A - 1 + lowerOffset) + tens;
    243         if (addCircumflex)
    244             letters[length++] = 0x0302;
    245     }
    246 
    247     if (int ones = number % 10) {
    248         letters[length++] = (0x531 - 1 + lowerOffset) + ones;
    249         if (addCircumflex)
    250             letters[length++] = 0x0302;
    251     }
    252 
    253     return length;
    254 }
    255 
    256 static String toArmenian(int number, bool upper)
    257 {
    258     ASSERT(number >= 1 && number <= 99999999);
    259 
    260     const int lettersSize = 18; // twice what toArmenianUnder10000 needs
    261     UChar letters[lettersSize];
    262 
    263     int length = toArmenianUnder10000(number / 10000, upper, true, letters);
    264     length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
    265 
    266     ASSERT(length <= lettersSize);
    267     return String(letters, length);
    268 }
    269 
    270 static String toGeorgian(int number)
    271 {
    272     ASSERT(number >= 1 && number <= 19999);
    273 
    274     const int lettersSize = 5;
    275     UChar letters[lettersSize];
    276 
    277     int length = 0;
    278 
    279     if (number > 9999)
    280         letters[length++] = 0x10F5;
    281 
    282     if (int thousands = (number / 1000) % 10) {
    283         static const UChar georgianThousands[9] = {
    284             0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
    285         };
    286         letters[length++] = georgianThousands[thousands - 1];
    287     }
    288 
    289     if (int hundreds = (number / 100) % 10) {
    290         static const UChar georgianHundreds[9] = {
    291             0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
    292         };
    293         letters[length++] = georgianHundreds[hundreds - 1];
    294     }
    295 
    296     if (int tens = (number / 10) % 10) {
    297         static const UChar georgianTens[9] = {
    298             0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
    299         };
    300         letters[length++] = georgianTens[tens - 1];
    301     }
    302 
    303     if (int ones = number % 10) {
    304         static const UChar georgianOnes[9] = {
    305             0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
    306         };
    307         letters[length++] = georgianOnes[ones - 1];
    308     }
    309 
    310     ASSERT(length <= lettersSize);
    311     return String(letters, length);
    312 }
    313 
    314 // The table uses the order from the CSS3 specification:
    315 // first 3 group markers, then 3 digit markers, then ten digits.
    316 static String toCJKIdeographic(int number, const UChar table[16])
    317 {
    318     ASSERT(number >= 0);
    319 
    320     enum AbstractCJKChar {
    321         noChar,
    322         secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
    323         secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
    324         digit0, digit1, digit2, digit3, digit4,
    325         digit5, digit6, digit7, digit8, digit9
    326     };
    327 
    328     if (number == 0)
    329         return String(&table[digit0 - 1], 1);
    330 
    331     const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker
    332     const int bufferLength = 4 * groupLength;
    333     AbstractCJKChar buffer[bufferLength] = { noChar };
    334 
    335     for (int i = 0; i < 4; ++i) {
    336         int groupValue = number % 10000;
    337         number /= 10000;
    338 
    339         // Process least-significant group first, but put it in the buffer last.
    340         AbstractCJKChar* group = &buffer[(3 - i) * groupLength];
    341 
    342         if (groupValue && i)
    343             group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i);
    344 
    345         // Put in the four digits and digit markers for any non-zero digits.
    346         group[6] = static_cast<AbstractCJKChar>(digit0 + (groupValue % 10));
    347         if (number != 0 || groupValue > 9) {
    348             int digitValue = ((groupValue / 10) % 10);
    349             group[4] = static_cast<AbstractCJKChar>(digit0 + digitValue);
    350             if (digitValue)
    351                 group[5] = secondDigitMarker;
    352         }
    353         if (number != 0 || groupValue > 99) {
    354             int digitValue = ((groupValue / 100) % 10);
    355             group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue);
    356             if (digitValue)
    357                 group[3] = thirdDigitMarker;
    358         }
    359         if (number != 0 || groupValue > 999) {
    360             int digitValue = groupValue / 1000;
    361             group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue);
    362             if (digitValue)
    363                 group[1] = fourthDigitMarker;
    364         }
    365 
    366         // Remove the tens digit, but leave the marker, for any group that has
    367         // a value of less than 20.
    368         if (groupValue < 20) {
    369             ASSERT(group[4] == noChar || group[4] == digit0 || group[4] == digit1);
    370             group[4] = noChar;
    371         }
    372 
    373         if (number == 0)
    374             break;
    375     }
    376 
    377     // Convert into characters, omitting consecutive runs of digit0 and
    378     // any trailing digit0.
    379     int length = 0;
    380     UChar characters[bufferLength];
    381     AbstractCJKChar last = noChar;
    382     for (int i = 0; i < bufferLength; ++i) {
    383         AbstractCJKChar a = buffer[i];
    384         if (a != noChar) {
    385             if (a != digit0 || last != digit0)
    386                 characters[length++] = table[a - 1];
    387             last = a;
    388         }
    389     }
    390     if (last == digit0)
    391         --length;
    392 
    393     return String(characters, length);
    394 }
    395 
    396 static EListStyleType effectiveListMarkerType(EListStyleType type, int value)
    397 {
    398     // Note, the following switch statement has been explicitly grouped
    399     // by list-style-type ordinal range.
    400     switch (type) {
    401     case ArabicIndic:
    402     case Bengali:
    403     case BinaryListStyle:
    404     case Cambodian:
    405     case Circle:
    406     case DecimalLeadingZero:
    407     case DecimalListStyle:
    408     case Devanagari:
    409     case Disc:
    410     case Gujarati:
    411     case Gurmukhi:
    412     case Kannada:
    413     case Khmer:
    414     case Lao:
    415     case LowerHexadecimal:
    416     case Malayalam:
    417     case Mongolian:
    418     case Myanmar:
    419     case NoneListStyle:
    420     case Octal:
    421     case Oriya:
    422     case Persian:
    423     case Square:
    424     case Telugu:
    425     case Thai:
    426     case Tibetan:
    427     case UpperHexadecimal:
    428     case Urdu:
    429         return type; // Can represent all ordinals.
    430     case Armenian:
    431         return (value < 1 || value > 99999999) ? DecimalListStyle : type;
    432     case CJKIdeographic:
    433         return (value < 0) ? DecimalListStyle : type;
    434     case Georgian:
    435         return (value < 1 || value > 19999) ? DecimalListStyle : type;
    436     case Hebrew:
    437         return (value < 0 || value > 999999) ? DecimalListStyle : type;
    438     case LowerRoman:
    439     case UpperRoman:
    440         return (value < 1 || value > 3999) ? DecimalListStyle : type;
    441     case Afar:
    442     case Amharic:
    443     case AmharicAbegede:
    444     case Asterisks:
    445     case CjkEarthlyBranch:
    446     case CjkHeavenlyStem:
    447     case Ethiopic:
    448     case EthiopicAbegede:
    449     case EthiopicAbegedeAmEt:
    450     case EthiopicAbegedeGez:
    451     case EthiopicAbegedeTiEr:
    452     case EthiopicAbegedeTiEt:
    453     case EthiopicHalehameAaEr:
    454     case EthiopicHalehameAaEt:
    455     case EthiopicHalehameAmEt:
    456     case EthiopicHalehameGez:
    457     case EthiopicHalehameOmEt:
    458     case EthiopicHalehameSidEt:
    459     case EthiopicHalehameSoEt:
    460     case EthiopicHalehameTiEr:
    461     case EthiopicHalehameTiEt:
    462     case EthiopicHalehameTig:
    463     case Footnotes:
    464     case Hangul:
    465     case HangulConsonant:
    466     case Hiragana:
    467     case HiraganaIroha:
    468     case Katakana:
    469     case KatakanaIroha:
    470     case LowerAlpha:
    471     case LowerArmenian:
    472     case LowerGreek:
    473     case LowerLatin:
    474     case LowerNorwegian:
    475     case Oromo:
    476     case Sidama:
    477     case Somali:
    478     case Tigre:
    479     case TigrinyaEr:
    480     case TigrinyaErAbegede:
    481     case TigrinyaEt:
    482     case TigrinyaEtAbegede:
    483     case UpperAlpha:
    484     case UpperArmenian:
    485     case UpperGreek:
    486     case UpperLatin:
    487     case UpperNorwegian:
    488         return (value < 1) ? DecimalListStyle : type;
    489     }
    490 
    491     ASSERT_NOT_REACHED();
    492     return type;
    493 }
    494 
    495 static UChar listMarkerSuffix(EListStyleType type, int value)
    496 {
    497     // If the list-style-type cannot represent |value| because it's outside its
    498     // ordinal range then we fall back to some list style that can represent |value|.
    499     EListStyleType effectiveType = effectiveListMarkerType(type, value);
    500 
    501     // Note, the following switch statement has been explicitly
    502     // grouped by list-style-type suffix.
    503     switch (effectiveType) {
    504     case Asterisks:
    505     case Circle:
    506     case Disc:
    507     case Footnotes:
    508     case NoneListStyle:
    509     case Square:
    510         return ' ';
    511     case Afar:
    512     case Amharic:
    513     case AmharicAbegede:
    514     case Ethiopic:
    515     case EthiopicAbegede:
    516     case EthiopicAbegedeAmEt:
    517     case EthiopicAbegedeGez:
    518     case EthiopicAbegedeTiEr:
    519     case EthiopicAbegedeTiEt:
    520     case EthiopicHalehameAaEr:
    521     case EthiopicHalehameAaEt:
    522     case EthiopicHalehameAmEt:
    523     case EthiopicHalehameGez:
    524     case EthiopicHalehameOmEt:
    525     case EthiopicHalehameSidEt:
    526     case EthiopicHalehameSoEt:
    527     case EthiopicHalehameTiEr:
    528     case EthiopicHalehameTiEt:
    529     case EthiopicHalehameTig:
    530     case Oromo:
    531     case Sidama:
    532     case Somali:
    533     case Tigre:
    534     case TigrinyaEr:
    535     case TigrinyaErAbegede:
    536     case TigrinyaEt:
    537     case TigrinyaEtAbegede:
    538         return ethiopicPrefaceColon;
    539     case Armenian:
    540     case ArabicIndic:
    541     case Bengali:
    542     case BinaryListStyle:
    543     case Cambodian:
    544     case CJKIdeographic:
    545     case CjkEarthlyBranch:
    546     case CjkHeavenlyStem:
    547     case DecimalLeadingZero:
    548     case DecimalListStyle:
    549     case Devanagari:
    550     case Georgian:
    551     case Gujarati:
    552     case Gurmukhi:
    553     case Hangul:
    554     case HangulConsonant:
    555     case Hebrew:
    556     case Hiragana:
    557     case HiraganaIroha:
    558     case Kannada:
    559     case Katakana:
    560     case KatakanaIroha:
    561     case Khmer:
    562     case Lao:
    563     case LowerAlpha:
    564     case LowerArmenian:
    565     case LowerGreek:
    566     case LowerHexadecimal:
    567     case LowerLatin:
    568     case LowerNorwegian:
    569     case LowerRoman:
    570     case Malayalam:
    571     case Mongolian:
    572     case Myanmar:
    573     case Octal:
    574     case Oriya:
    575     case Persian:
    576     case Telugu:
    577     case Thai:
    578     case Tibetan:
    579     case UpperAlpha:
    580     case UpperArmenian:
    581     case UpperGreek:
    582     case UpperHexadecimal:
    583     case UpperLatin:
    584     case UpperNorwegian:
    585     case UpperRoman:
    586     case Urdu:
    587         return '.';
    588     }
    589 
    590     ASSERT_NOT_REACHED();
    591     return '.';
    592 }
    593 
    594 String listMarkerText(EListStyleType type, int value)
    595 {
    596     // If the list-style-type, say hebrew, cannot represent |value| because it's outside
    597     // its ordinal range then we fallback to some list style that can represent |value|.
    598     switch (effectiveListMarkerType(type, value)) {
    599         case NoneListStyle:
    600             return "";
    601 
    602         case Asterisks: {
    603             static const UChar asterisksSymbols[1] = {
    604                 0x002A
    605             };
    606             return toSymbolic(value, asterisksSymbols);
    607         }
    608         // We use the same characters for text security.
    609         // See RenderText::setInternalString.
    610         case Circle:
    611             return String(&whiteBullet, 1);
    612         case Disc:
    613             return String(&bullet, 1);
    614         case Footnotes: {
    615             static const UChar footnotesSymbols[4] = {
    616                 0x002A, 0x2051, 0x2020, 0x2021
    617             };
    618             return toSymbolic(value, footnotesSymbols);
    619         }
    620         case Square:
    621             // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
    622             // instead, but I think this looks better.
    623             return String(&blackSquare, 1);
    624 
    625         case DecimalListStyle:
    626             return String::number(value);
    627         case DecimalLeadingZero:
    628             if (value < -9 || value > 9)
    629                 return String::number(value);
    630             if (value < 0)
    631                 return "-0" + String::number(-value); // -01 to -09
    632             return "0" + String::number(value); // 00 to 09
    633 
    634         case ArabicIndic: {
    635             static const UChar arabicIndicNumerals[10] = {
    636                 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669
    637             };
    638             return toNumeric(value, arabicIndicNumerals);
    639         }
    640         case BinaryListStyle: {
    641             static const UChar binaryNumerals[2] = {
    642                 '0', '1'
    643             };
    644             return toNumeric(value, binaryNumerals);
    645         }
    646         case Bengali: {
    647             static const UChar bengaliNumerals[10] = {
    648                 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF
    649             };
    650             return toNumeric(value, bengaliNumerals);
    651         }
    652         case Cambodian:
    653         case Khmer: {
    654             static const UChar khmerNumerals[10] = {
    655                 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9
    656             };
    657             return toNumeric(value, khmerNumerals);
    658         }
    659         case Devanagari: {
    660             static const UChar devanagariNumerals[10] = {
    661                 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F
    662             };
    663             return toNumeric(value, devanagariNumerals);
    664         }
    665         case Gujarati: {
    666             static const UChar gujaratiNumerals[10] = {
    667                 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF
    668             };
    669             return toNumeric(value, gujaratiNumerals);
    670         }
    671         case Gurmukhi: {
    672             static const UChar gurmukhiNumerals[10] = {
    673                 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F
    674             };
    675             return toNumeric(value, gurmukhiNumerals);
    676         }
    677         case Kannada: {
    678             static const UChar kannadaNumerals[10] = {
    679                 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF
    680             };
    681             return toNumeric(value, kannadaNumerals);
    682         }
    683         case LowerHexadecimal: {
    684             static const UChar lowerHexadecimalNumerals[16] = {
    685                 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
    686             };
    687             return toNumeric(value, lowerHexadecimalNumerals);
    688         }
    689         case Lao: {
    690             static const UChar laoNumerals[10] = {
    691                 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9
    692             };
    693             return toNumeric(value, laoNumerals);
    694         }
    695         case Malayalam: {
    696             static const UChar malayalamNumerals[10] = {
    697                 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F
    698             };
    699             return toNumeric(value, malayalamNumerals);
    700         }
    701         case Mongolian: {
    702             static const UChar mongolianNumerals[10] = {
    703                 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819
    704             };
    705             return toNumeric(value, mongolianNumerals);
    706         }
    707         case Myanmar: {
    708             static const UChar myanmarNumerals[10] = {
    709                 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049
    710             };
    711             return toNumeric(value, myanmarNumerals);
    712         }
    713         case Octal: {
    714             static const UChar octalNumerals[8] = {
    715                 '0', '1', '2', '3', '4', '5', '6', '7'
    716             };
    717             return toNumeric(value, octalNumerals);
    718         }
    719         case Oriya: {
    720             static const UChar oriyaNumerals[10] = {
    721                 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F
    722             };
    723             return toNumeric(value, oriyaNumerals);
    724         }
    725         case Persian:
    726         case Urdu: {
    727             static const UChar urduNumerals[10] = {
    728                 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9
    729             };
    730             return toNumeric(value, urduNumerals);
    731         }
    732         case Telugu: {
    733             static const UChar teluguNumerals[10] = {
    734                 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F
    735             };
    736             return toNumeric(value, teluguNumerals);
    737         }
    738         case Tibetan: {
    739             static const UChar tibetanNumerals[10] = {
    740                 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29
    741             };
    742             return toNumeric(value, tibetanNumerals);
    743         }
    744         case Thai: {
    745             static const UChar thaiNumerals[10] = {
    746                 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59
    747             };
    748             return toNumeric(value, thaiNumerals);
    749         }
    750         case UpperHexadecimal: {
    751             static const UChar upperHexadecimalNumerals[16] = {
    752                 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
    753             };
    754             return toNumeric(value, upperHexadecimalNumerals);
    755         }
    756 
    757         case LowerAlpha:
    758         case LowerLatin: {
    759             static const UChar lowerLatinAlphabet[26] = {
    760                 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    761                 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
    762             };
    763             return toAlphabetic(value, lowerLatinAlphabet);
    764         }
    765         case UpperAlpha:
    766         case UpperLatin: {
    767             static const UChar upperLatinAlphabet[26] = {
    768                 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
    769                 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
    770             };
    771             return toAlphabetic(value, upperLatinAlphabet);
    772         }
    773         case LowerGreek: {
    774             static const UChar lowerGreekAlphabet[24] = {
    775                 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
    776                 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
    777                 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9
    778             };
    779             return toAlphabetic(value, lowerGreekAlphabet);
    780         }
    781 
    782         case Hiragana: {
    783             // FIXME: This table comes from the CSS3 draft, and is probably
    784             // incorrect, given the comments in that draft.
    785             static const UChar hiraganaAlphabet[48] = {
    786                 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
    787                 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
    788                 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
    789                 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
    790                 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
    791                 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093
    792             };
    793             return toAlphabetic(value, hiraganaAlphabet);
    794         }
    795         case HiraganaIroha: {
    796             // FIXME: This table comes from the CSS3 draft, and is probably
    797             // incorrect, given the comments in that draft.
    798             static const UChar hiraganaIrohaAlphabet[47] = {
    799                 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061,
    800                 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F,
    801                 0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046,
    802                 0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075,
    803                 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
    804                 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059
    805             };
    806             return toAlphabetic(value, hiraganaIrohaAlphabet);
    807         }
    808         case Katakana: {
    809             // FIXME: This table comes from the CSS3 draft, and is probably
    810             // incorrect, given the comments in that draft.
    811             static const UChar katakanaAlphabet[48] = {
    812                 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF,
    813                 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF,
    814                 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD,
    815                 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF,
    816                 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
    817                 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3
    818             };
    819             return toAlphabetic(value, katakanaAlphabet);
    820         }
    821         case KatakanaIroha: {
    822             // FIXME: This table comes from the CSS3 draft, and is probably
    823             // incorrect, given the comments in that draft.
    824             static const UChar katakanaIrohaAlphabet[47] = {
    825                 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1,
    826                 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF,
    827                 0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6,
    828                 0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5,
    829                 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
    830                 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9
    831             };
    832             return toAlphabetic(value, katakanaIrohaAlphabet);
    833         }
    834 
    835         case Afar:
    836         case EthiopicHalehameAaEt:
    837         case EthiopicHalehameAaEr: {
    838             static const UChar ethiopicHalehameAaErAlphabet[18] = {
    839                 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290,
    840                 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348
    841             };
    842             return toAlphabetic(value, ethiopicHalehameAaErAlphabet);
    843         }
    844         case Amharic:
    845         case EthiopicHalehameAmEt: {
    846             static const UChar ethiopicHalehameAmEtAlphabet[33] = {
    847                 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
    848                 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8,
    849                 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320,
    850                 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
    851             };
    852             return toAlphabetic(value, ethiopicHalehameAmEtAlphabet);
    853         }
    854         case AmharicAbegede:
    855         case EthiopicAbegedeAmEt: {
    856             static const UChar ethiopicAbegedeAmEtAlphabet[33] = {
    857                 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
    858                 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
    859                 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238,
    860                 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
    861             };
    862             return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet);
    863         }
    864         case CjkEarthlyBranch: {
    865             static const UChar cjkEarthlyBranchAlphabet[12] = {
    866                 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533,
    867                 0x9149, 0x620C, 0x4EA5
    868             };
    869             return toAlphabetic(value, cjkEarthlyBranchAlphabet);
    870         }
    871         case CjkHeavenlyStem: {
    872             static const UChar cjkHeavenlyStemAlphabet[10] = {
    873                 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC,
    874                 0x7678
    875             };
    876             return toAlphabetic(value, cjkHeavenlyStemAlphabet);
    877         }
    878         case Ethiopic:
    879         case EthiopicHalehameGez: {
    880             static const UChar ethiopicHalehameGezAlphabet[26] = {
    881                 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1240, 0x1260,
    882                 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
    883                 0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
    884             };
    885             return toAlphabetic(value, ethiopicHalehameGezAlphabet);
    886         }
    887         case EthiopicAbegede:
    888         case EthiopicAbegedeGez: {
    889             static const UChar ethiopicAbegedeGezAlphabet[26] = {
    890                 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1200, 0x12C8, 0x12D8, 0x1210, 0x1320,
    891                 0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338,
    892                 0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350
    893             };
    894             return toAlphabetic(value, ethiopicAbegedeGezAlphabet);
    895         }
    896         case HangulConsonant: {
    897             static const UChar hangulConsonantAlphabet[14] = {
    898                 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148,
    899                 0x314A, 0x314B, 0x314C, 0x314D, 0x314E
    900             };
    901             return toAlphabetic(value, hangulConsonantAlphabet);
    902         }
    903         case Hangul: {
    904             static const UChar hangulAlphabet[14] = {
    905                 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790,
    906                 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558
    907             };
    908             return toAlphabetic(value, hangulAlphabet);
    909         }
    910         case Oromo:
    911         case EthiopicHalehameOmEt: {
    912             static const UChar ethiopicHalehameOmEtAlphabet[25] = {
    913                 0x1200, 0x1208, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 0x1270,
    914                 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8,
    915                 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
    916             };
    917             return toAlphabetic(value, ethiopicHalehameOmEtAlphabet);
    918         }
    919         case Sidama:
    920         case EthiopicHalehameSidEt: {
    921             static const UChar ethiopicHalehameSidEtAlphabet[26] = {
    922                 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
    923                 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0,
    924                 0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
    925             };
    926             return toAlphabetic(value, ethiopicHalehameSidEtAlphabet);
    927         }
    928         case Somali:
    929         case EthiopicHalehameSoEt: {
    930             static const UChar ethiopicHalehameSoEtAlphabet[22] = {
    931                 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
    932                 0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0,
    933                 0x1300, 0x1308, 0x1338, 0x1348
    934             };
    935             return toAlphabetic(value, ethiopicHalehameSoEtAlphabet);
    936         }
    937         case Tigre:
    938         case EthiopicHalehameTig: {
    939             static const UChar ethiopicHalehameTigAlphabet[27] = {
    940                 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
    941                 0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
    942                 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350
    943             };
    944             return toAlphabetic(value, ethiopicHalehameTigAlphabet);
    945         }
    946         case TigrinyaEr:
    947         case EthiopicHalehameTiEr: {
    948             static const UChar ethiopicHalehameTiErAlphabet[31] = {
    949                 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1250,
    950                 0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8,
    951                 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328,
    952                 0x1330, 0x1338, 0x1348, 0x1350
    953             };
    954             return toAlphabetic(value, ethiopicHalehameTiErAlphabet);
    955         }
    956         case TigrinyaErAbegede:
    957         case EthiopicAbegedeTiEr: {
    958             static const UChar ethiopicAbegedeTiErAlphabet[31] = {
    959                 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
    960                 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
    961                 0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238,
    962                 0x1270, 0x1278, 0x1330, 0x1350
    963             };
    964             return toAlphabetic(value, ethiopicAbegedeTiErAlphabet);
    965         }
    966         case TigrinyaEt:
    967         case EthiopicHalehameTiEt: {
    968             static const UChar ethiopicHalehameTiEtAlphabet[34] = {
    969                 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
    970                 0x1250, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8,
    971                 0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308,
    972                 0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
    973             };
    974             return toAlphabetic(value, ethiopicHalehameTiEtAlphabet);
    975         }
    976         case TigrinyaEtAbegede:
    977         case EthiopicAbegedeTiEt: {
    978             static const UChar ethiopicAbegedeTiEtAlphabet[34] = {
    979                 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
    980                 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
    981                 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230,
    982                 0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
    983             };
    984             return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet);
    985         }
    986         case UpperGreek: {
    987             static const UChar upperGreekAlphabet[24] = {
    988                 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399,
    989                 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3,
    990                 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9
    991             };
    992             return toAlphabetic(value, upperGreekAlphabet);
    993         }
    994         case LowerNorwegian: {
    995             static const UChar lowerNorwegianAlphabet[29] = {
    996                 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069,
    997                 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072,
    998                 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E6,
    999                 0x00F8, 0x00E5
   1000             };
   1001             return toAlphabetic(value, lowerNorwegianAlphabet);
   1002         }
   1003         case UpperNorwegian: {
   1004             static const UChar upperNorwegianAlphabet[29] = {
   1005                 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049,
   1006                 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052,
   1007                 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00C6,
   1008                 0x00D8, 0x00C5
   1009             };
   1010             return toAlphabetic(value, upperNorwegianAlphabet);
   1011         }
   1012         case CJKIdeographic: {
   1013             static const UChar traditionalChineseInformalTable[16] = {
   1014                 0x842C, 0x5104, 0x5146,
   1015                 0x5341, 0x767E, 0x5343,
   1016                 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB,
   1017                 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D
   1018             };
   1019             return toCJKIdeographic(value, traditionalChineseInformalTable);
   1020         }
   1021 
   1022         case LowerRoman:
   1023             return toRoman(value, false);
   1024         case UpperRoman:
   1025             return toRoman(value, true);
   1026 
   1027         case Armenian:
   1028         case UpperArmenian:
   1029             // CSS3 says "armenian" means "lower-armenian".
   1030             // But the CSS2.1 test suite contains uppercase test results for "armenian",
   1031             // so we'll match the test suite.
   1032             return toArmenian(value, true);
   1033         case LowerArmenian:
   1034             return toArmenian(value, false);
   1035         case Georgian:
   1036             return toGeorgian(value);
   1037         case Hebrew:
   1038             return toHebrew(value);
   1039     }
   1040 
   1041     ASSERT_NOT_REACHED();
   1042     return "";
   1043 }
   1044 
   1045 RenderListMarker::RenderListMarker(RenderListItem* item)
   1046     : RenderBox(item->document())
   1047     , m_listItem(item)
   1048 {
   1049     // init RenderObject attributes
   1050     setInline(true);   // our object is Inline
   1051     setReplaced(true); // pretend to be replaced
   1052 }
   1053 
   1054 RenderListMarker::~RenderListMarker()
   1055 {
   1056     if (m_image)
   1057         m_image->removeClient(this);
   1058 }
   1059 
   1060 void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
   1061 {
   1062     if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType()))
   1063         setNeedsLayoutAndPrefWidthsRecalc();
   1064 
   1065     RenderBox::styleWillChange(diff, newStyle);
   1066 }
   1067 
   1068 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
   1069 {
   1070     RenderBox::styleDidChange(diff, oldStyle);
   1071 
   1072     if (m_image != style()->listStyleImage()) {
   1073         if (m_image)
   1074             m_image->removeClient(this);
   1075         m_image = style()->listStyleImage();
   1076         if (m_image)
   1077             m_image->addClient(this);
   1078     }
   1079 }
   1080 
   1081 InlineBox* RenderListMarker::createInlineBox()
   1082 {
   1083     InlineBox* result = RenderBox::createInlineBox();
   1084     result->setIsText(isText());
   1085     return result;
   1086 }
   1087 
   1088 bool RenderListMarker::isImage() const
   1089 {
   1090     return m_image && !m_image->errorOccurred();
   1091 }
   1092 
   1093 IntRect RenderListMarker::localSelectionRect()
   1094 {
   1095     InlineBox* box = inlineBoxWrapper();
   1096     if (!box)
   1097         return IntRect(0, 0, width(), height());
   1098     RootInlineBox* root = m_inlineBoxWrapper->root();
   1099     int newLogicalTop = root->block()->style()->isFlippedBlocksWritingMode() ? m_inlineBoxWrapper->logicalBottom() - root->selectionBottom() : root->selectionTop() - m_inlineBoxWrapper->logicalTop();
   1100     if (root->block()->style()->isHorizontalWritingMode())
   1101         return IntRect(0, newLogicalTop, width(), root->selectionHeight());
   1102     return IntRect(newLogicalTop, 0, root->selectionHeight(), height());
   1103 }
   1104 
   1105 void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty)
   1106 {
   1107     if (paintInfo.phase != PaintPhaseForeground)
   1108         return;
   1109 
   1110     if (style()->visibility() != VISIBLE)
   1111         return;
   1112 
   1113     IntPoint boxOrigin(tx + x(), ty + y());
   1114     IntRect overflowRect(visualOverflowRect());
   1115     overflowRect.move(boxOrigin.x(), boxOrigin.y());
   1116     overflowRect.inflate(maximalOutlineSize(paintInfo.phase));
   1117 
   1118     if (!paintInfo.rect.intersects(overflowRect))
   1119         return;
   1120 
   1121     IntRect box(boxOrigin, IntSize(width(), height()));
   1122 
   1123     IntRect marker = getRelativeMarkerRect();
   1124     marker.move(boxOrigin.x(), boxOrigin.y());
   1125 
   1126     GraphicsContext* context = paintInfo.context;
   1127 
   1128     if (isImage()) {
   1129 #if PLATFORM(MAC)
   1130         if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
   1131             paintCustomHighlight(tx, ty, style()->highlight(), true);
   1132 #endif
   1133         context->drawImage(m_image->image(this, marker.size()).get(), style()->colorSpace(), marker);
   1134         if (selectionState() != SelectionNone) {
   1135             IntRect selRect = localSelectionRect();
   1136             selRect.move(boxOrigin.x(), boxOrigin.y());
   1137             context->fillRect(selRect, selectionBackgroundColor(), style()->colorSpace());
   1138         }
   1139         return;
   1140     }
   1141 
   1142 #if PLATFORM(MAC)
   1143     // FIXME: paint gap between marker and list item proper
   1144     if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
   1145         paintCustomHighlight(tx, ty, style()->highlight(), true);
   1146 #endif
   1147 
   1148     if (selectionState() != SelectionNone) {
   1149         IntRect selRect = localSelectionRect();
   1150         selRect.move(boxOrigin.x(), boxOrigin.y());
   1151         context->fillRect(selRect, selectionBackgroundColor(), style()->colorSpace());
   1152     }
   1153 
   1154     const Color color(style()->visitedDependentColor(CSSPropertyColor));
   1155     context->setStrokeColor(color, style()->colorSpace());
   1156     context->setStrokeStyle(SolidStroke);
   1157     context->setStrokeThickness(1.0f);
   1158     context->setFillColor(color, style()->colorSpace());
   1159 
   1160     EListStyleType type = style()->listStyleType();
   1161     switch (type) {
   1162         case Disc:
   1163             context->drawEllipse(marker);
   1164             return;
   1165         case Circle:
   1166             context->setFillColor(Color::transparent, ColorSpaceDeviceRGB);
   1167             context->drawEllipse(marker);
   1168             return;
   1169         case Square:
   1170             context->drawRect(marker);
   1171             return;
   1172         case NoneListStyle:
   1173             return;
   1174         case Afar:
   1175         case Amharic:
   1176         case AmharicAbegede:
   1177         case ArabicIndic:
   1178         case Armenian:
   1179         case BinaryListStyle:
   1180         case Bengali:
   1181         case Cambodian:
   1182         case CJKIdeographic:
   1183         case CjkEarthlyBranch:
   1184         case CjkHeavenlyStem:
   1185         case DecimalLeadingZero:
   1186         case DecimalListStyle:
   1187         case Devanagari:
   1188         case Ethiopic:
   1189         case EthiopicAbegede:
   1190         case EthiopicAbegedeAmEt:
   1191         case EthiopicAbegedeGez:
   1192         case EthiopicAbegedeTiEr:
   1193         case EthiopicAbegedeTiEt:
   1194         case EthiopicHalehameAaEr:
   1195         case EthiopicHalehameAaEt:
   1196         case EthiopicHalehameAmEt:
   1197         case EthiopicHalehameGez:
   1198         case EthiopicHalehameOmEt:
   1199         case EthiopicHalehameSidEt:
   1200         case EthiopicHalehameSoEt:
   1201         case EthiopicHalehameTiEr:
   1202         case EthiopicHalehameTiEt:
   1203         case EthiopicHalehameTig:
   1204         case Georgian:
   1205         case Gujarati:
   1206         case Gurmukhi:
   1207         case Hangul:
   1208         case HangulConsonant:
   1209         case Hebrew:
   1210         case Hiragana:
   1211         case HiraganaIroha:
   1212         case Kannada:
   1213         case Katakana:
   1214         case KatakanaIroha:
   1215         case Khmer:
   1216         case Lao:
   1217         case LowerAlpha:
   1218         case LowerArmenian:
   1219         case LowerGreek:
   1220         case LowerHexadecimal:
   1221         case LowerLatin:
   1222         case LowerNorwegian:
   1223         case LowerRoman:
   1224         case Malayalam:
   1225         case Mongolian:
   1226         case Myanmar:
   1227         case Octal:
   1228         case Oriya:
   1229         case Oromo:
   1230         case Persian:
   1231         case Sidama:
   1232         case Somali:
   1233         case Telugu:
   1234         case Thai:
   1235         case Tibetan:
   1236         case Tigre:
   1237         case TigrinyaEr:
   1238         case TigrinyaErAbegede:
   1239         case TigrinyaEt:
   1240         case TigrinyaEtAbegede:
   1241         case UpperAlpha:
   1242         case UpperArmenian:
   1243         case UpperGreek:
   1244         case UpperHexadecimal:
   1245         case UpperLatin:
   1246         case UpperNorwegian:
   1247         case UpperRoman:
   1248         case Urdu:
   1249         case Asterisks:
   1250         case Footnotes:
   1251             break;
   1252     }
   1253     if (m_text.isEmpty())
   1254         return;
   1255 
   1256     TextRun textRun(m_text);
   1257 
   1258     if (!style()->isHorizontalWritingMode()) {
   1259         marker.move(-boxOrigin.x(), -boxOrigin.y());
   1260         marker = marker.transposedRect();
   1261         marker.move(box.x(), box.y() - logicalHeight());
   1262         context->save();
   1263         context->translate(marker.x(), marker.maxY());
   1264         context->rotate(static_cast<float>(deg2rad(90.)));
   1265         context->translate(-marker.x(), -marker.maxY());
   1266     }
   1267 
   1268     IntPoint textOrigin = IntPoint(marker.x(), marker.y() + style()->fontMetrics().ascent());
   1269 
   1270     if (type == Asterisks || type == Footnotes)
   1271         context->drawText(style()->font(), textRun, textOrigin);
   1272     else {
   1273         // Text is not arbitrary. We can judge whether it's RTL from the first character,
   1274         // and we only need to handle the direction RightToLeft for now.
   1275         bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
   1276         Vector<UChar> reversedText;
   1277         if (textNeedsReversing) {
   1278             int length = m_text.length();
   1279             reversedText.grow(length);
   1280             for (int i = 0; i < length; ++i)
   1281                 reversedText[length - i - 1] = m_text[i];
   1282             textRun = TextRun(reversedText.data(), length);
   1283         }
   1284 
   1285         const Font& font = style()->font();
   1286         const UChar suffix = listMarkerSuffix(type, m_listItem->value());
   1287         if (style()->isLeftToRightDirection()) {
   1288             int width = font.width(textRun);
   1289             context->drawText(style()->font(), textRun, textOrigin);
   1290             UChar suffixSpace[2] = { suffix, ' ' };
   1291             context->drawText(style()->font(), TextRun(suffixSpace, 2), textOrigin + IntSize(width, 0));
   1292         } else {
   1293             UChar spaceSuffix[2] = { ' ', suffix };
   1294             TextRun spaceSuffixRun(spaceSuffix, 2);
   1295             int width = font.width(spaceSuffixRun);
   1296             context->drawText(style()->font(), spaceSuffixRun, textOrigin);
   1297             context->drawText(style()->font(), textRun, textOrigin + IntSize(width, 0));
   1298         }
   1299     }
   1300 
   1301     if (!style()->isHorizontalWritingMode())
   1302         context->restore();
   1303 }
   1304 
   1305 void RenderListMarker::layout()
   1306 {
   1307     ASSERT(needsLayout());
   1308 
   1309     if (isImage()) {
   1310         setWidth(m_image->imageSize(this, style()->effectiveZoom()).width());
   1311         setHeight(m_image->imageSize(this, style()->effectiveZoom()).height());
   1312     } else {
   1313         setLogicalWidth(minPreferredLogicalWidth());
   1314         setLogicalHeight(style()->fontMetrics().height());
   1315     }
   1316 
   1317     setMarginStart(0);
   1318     setMarginEnd(0);
   1319 
   1320     Length startMargin = style()->marginStart();
   1321     Length endMargin = style()->marginEnd();
   1322     if (startMargin.isFixed())
   1323         setMarginStart(startMargin.value());
   1324     if (endMargin.isFixed())
   1325         setMarginEnd(endMargin.value());
   1326 
   1327     setNeedsLayout(false);
   1328 }
   1329 
   1330 void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*)
   1331 {
   1332     // A list marker can't have a background or border image, so no need to call the base class method.
   1333     if (o != m_image->data())
   1334         return;
   1335 
   1336     if (width() != m_image->imageSize(this, style()->effectiveZoom()).width() || height() != m_image->imageSize(this, style()->effectiveZoom()).height() || m_image->errorOccurred())
   1337         setNeedsLayoutAndPrefWidthsRecalc();
   1338     else
   1339         repaint();
   1340 }
   1341 
   1342 void RenderListMarker::computePreferredLogicalWidths()
   1343 {
   1344     ASSERT(preferredLogicalWidthsDirty());
   1345 
   1346     m_text = "";
   1347 
   1348     const Font& font = style()->font();
   1349     const FontMetrics& fontMetrics = font.fontMetrics();
   1350 
   1351     if (isImage()) {
   1352         // FIXME: This is a somewhat arbitrary width.  Generated images for markers really won't become particularly useful
   1353         // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box.
   1354         int bulletWidth = fontMetrics.ascent() / 2;
   1355         m_image->setImageContainerSize(IntSize(bulletWidth, bulletWidth));
   1356         IntSize imageSize = m_image->imageSize(this, style()->effectiveZoom());
   1357         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = style()->isHorizontalWritingMode() ? imageSize.width() : imageSize.height();
   1358         setPreferredLogicalWidthsDirty(false);
   1359         updateMargins();
   1360         return;
   1361     }
   1362 
   1363     int logicalWidth = 0;
   1364     EListStyleType type = style()->listStyleType();
   1365     switch (type) {
   1366         case NoneListStyle:
   1367             break;
   1368         case Asterisks:
   1369         case Footnotes:
   1370             m_text = listMarkerText(type, m_listItem->value());
   1371             logicalWidth = font.width(m_text); // no suffix for these types
   1372             break;
   1373         case Circle:
   1374         case Disc:
   1375         case Square:
   1376             m_text = listMarkerText(type, 0); // value is ignored for these types
   1377             logicalWidth = (fontMetrics.ascent() * 2 / 3 + 1) / 2 + 2;
   1378             break;
   1379         case Afar:
   1380         case Amharic:
   1381         case AmharicAbegede:
   1382         case ArabicIndic:
   1383         case Armenian:
   1384         case BinaryListStyle:
   1385         case Bengali:
   1386         case Cambodian:
   1387         case CJKIdeographic:
   1388         case CjkEarthlyBranch:
   1389         case CjkHeavenlyStem:
   1390         case DecimalLeadingZero:
   1391         case DecimalListStyle:
   1392         case Devanagari:
   1393         case Ethiopic:
   1394         case EthiopicAbegede:
   1395         case EthiopicAbegedeAmEt:
   1396         case EthiopicAbegedeGez:
   1397         case EthiopicAbegedeTiEr:
   1398         case EthiopicAbegedeTiEt:
   1399         case EthiopicHalehameAaEr:
   1400         case EthiopicHalehameAaEt:
   1401         case EthiopicHalehameAmEt:
   1402         case EthiopicHalehameGez:
   1403         case EthiopicHalehameOmEt:
   1404         case EthiopicHalehameSidEt:
   1405         case EthiopicHalehameSoEt:
   1406         case EthiopicHalehameTiEr:
   1407         case EthiopicHalehameTiEt:
   1408         case EthiopicHalehameTig:
   1409         case Georgian:
   1410         case Gujarati:
   1411         case Gurmukhi:
   1412         case Hangul:
   1413         case HangulConsonant:
   1414         case Hebrew:
   1415         case Hiragana:
   1416         case HiraganaIroha:
   1417         case Kannada:
   1418         case Katakana:
   1419         case KatakanaIroha:
   1420         case Khmer:
   1421         case Lao:
   1422         case LowerAlpha:
   1423         case LowerArmenian:
   1424         case LowerGreek:
   1425         case LowerHexadecimal:
   1426         case LowerLatin:
   1427         case LowerNorwegian:
   1428         case LowerRoman:
   1429         case Malayalam:
   1430         case Mongolian:
   1431         case Myanmar:
   1432         case Octal:
   1433         case Oriya:
   1434         case Oromo:
   1435         case Persian:
   1436         case Sidama:
   1437         case Somali:
   1438         case Telugu:
   1439         case Thai:
   1440         case Tibetan:
   1441         case Tigre:
   1442         case TigrinyaEr:
   1443         case TigrinyaErAbegede:
   1444         case TigrinyaEt:
   1445         case TigrinyaEtAbegede:
   1446         case UpperAlpha:
   1447         case UpperArmenian:
   1448         case UpperGreek:
   1449         case UpperHexadecimal:
   1450         case UpperLatin:
   1451         case UpperNorwegian:
   1452         case UpperRoman:
   1453         case Urdu:
   1454             m_text = listMarkerText(type, m_listItem->value());
   1455             if (m_text.isEmpty())
   1456                 logicalWidth = 0;
   1457             else {
   1458                 int itemWidth = font.width(m_text);
   1459                 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
   1460                 int suffixSpaceWidth = font.width(TextRun(suffixSpace, 2));
   1461                 logicalWidth = itemWidth + suffixSpaceWidth;
   1462             }
   1463             break;
   1464     }
   1465 
   1466     m_minPreferredLogicalWidth = logicalWidth;
   1467     m_maxPreferredLogicalWidth = logicalWidth;
   1468 
   1469     setPreferredLogicalWidthsDirty(false);
   1470 
   1471     updateMargins();
   1472 }
   1473 
   1474 void RenderListMarker::updateMargins()
   1475 {
   1476     const FontMetrics& fontMetrics = style()->fontMetrics();
   1477 
   1478     int marginStart = 0;
   1479     int marginEnd = 0;
   1480 
   1481     if (isInside()) {
   1482         if (isImage())
   1483             marginEnd = cMarkerPadding;
   1484         else switch (style()->listStyleType()) {
   1485             case Disc:
   1486             case Circle:
   1487             case Square:
   1488                 marginStart = -1;
   1489                 marginEnd = fontMetrics.ascent() - minPreferredLogicalWidth() + 1;
   1490                 break;
   1491             default:
   1492                 break;
   1493         }
   1494     } else {
   1495         if (style()->isLeftToRightDirection()) {
   1496             if (isImage())
   1497                 marginStart = -minPreferredLogicalWidth() - cMarkerPadding;
   1498             else {
   1499                 int offset = fontMetrics.ascent() * 2 / 3;
   1500                 switch (style()->listStyleType()) {
   1501                     case Disc:
   1502                     case Circle:
   1503                     case Square:
   1504                         marginStart = -offset - cMarkerPadding - 1;
   1505                         break;
   1506                     case NoneListStyle:
   1507                         break;
   1508                     default:
   1509                         marginStart = m_text.isEmpty() ? 0 : -minPreferredLogicalWidth() - offset / 2;
   1510                 }
   1511             }
   1512             marginEnd = -marginStart - minPreferredLogicalWidth();
   1513         } else {
   1514             if (isImage())
   1515                 marginEnd = cMarkerPadding;
   1516             else {
   1517                 int offset = fontMetrics.ascent() * 2 / 3;
   1518                 switch (style()->listStyleType()) {
   1519                     case Disc:
   1520                     case Circle:
   1521                     case Square:
   1522                         marginEnd = offset + cMarkerPadding + 1 - minPreferredLogicalWidth();
   1523                         break;
   1524                     case NoneListStyle:
   1525                         break;
   1526                     default:
   1527                         marginEnd = m_text.isEmpty() ? 0 : offset / 2;
   1528                 }
   1529             }
   1530             marginStart = -marginEnd - minPreferredLogicalWidth();
   1531         }
   1532 
   1533     }
   1534 
   1535     style()->setMarginStart(Length(marginStart, Fixed));
   1536     style()->setMarginEnd(Length(marginEnd, Fixed));
   1537 }
   1538 
   1539 int RenderListMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
   1540 {
   1541     if (!isImage())
   1542         return m_listItem->lineHeight(firstLine, direction, PositionOfInteriorLineBoxes);
   1543     return RenderBox::lineHeight(firstLine, direction, linePositionMode);
   1544 }
   1545 
   1546 int RenderListMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
   1547 {
   1548     if (!isImage())
   1549         return m_listItem->baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes);
   1550     return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
   1551 }
   1552 
   1553 String RenderListMarker::suffix() const
   1554 {
   1555     EListStyleType type = style()->listStyleType();
   1556     const UChar suffix = listMarkerSuffix(type, m_listItem->value());
   1557 
   1558     Vector<UChar> resultVector;
   1559     resultVector.append(suffix);
   1560 
   1561     // If the suffix is not ' ', an extra space is needed
   1562     if (suffix != ' ') {
   1563         if (style()->isLeftToRightDirection())
   1564             resultVector.append(' ');
   1565         else
   1566             resultVector.prepend(' ');
   1567     }
   1568 
   1569     return String::adopt(resultVector);
   1570 }
   1571 
   1572 bool RenderListMarker::isInside() const
   1573 {
   1574     return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
   1575 }
   1576 
   1577 IntRect RenderListMarker::getRelativeMarkerRect()
   1578 {
   1579     if (isImage())
   1580         return IntRect(0, 0, m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height());
   1581 
   1582     IntRect relativeRect;
   1583     EListStyleType type = style()->listStyleType();
   1584     switch (type) {
   1585         case Asterisks:
   1586         case Footnotes: {
   1587             const Font& font = style()->font();
   1588             relativeRect = IntRect(0, 0, font.width(m_text), font.fontMetrics().height());
   1589             break;
   1590         }
   1591         case Disc:
   1592         case Circle:
   1593         case Square: {
   1594             // FIXME: Are these particular rounding rules necessary?
   1595             const FontMetrics& fontMetrics = style()->fontMetrics();
   1596             int ascent = fontMetrics.ascent();
   1597             int bulletWidth = (ascent * 2 / 3 + 1) / 2;
   1598             relativeRect = IntRect(1, 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
   1599             break;
   1600         }
   1601         case NoneListStyle:
   1602             return IntRect();
   1603         case Afar:
   1604         case Amharic:
   1605         case AmharicAbegede:
   1606         case ArabicIndic:
   1607         case Armenian:
   1608         case BinaryListStyle:
   1609         case Bengali:
   1610         case Cambodian:
   1611         case CJKIdeographic:
   1612         case CjkEarthlyBranch:
   1613         case CjkHeavenlyStem:
   1614         case DecimalLeadingZero:
   1615         case DecimalListStyle:
   1616         case Devanagari:
   1617         case Ethiopic:
   1618         case EthiopicAbegede:
   1619         case EthiopicAbegedeAmEt:
   1620         case EthiopicAbegedeGez:
   1621         case EthiopicAbegedeTiEr:
   1622         case EthiopicAbegedeTiEt:
   1623         case EthiopicHalehameAaEr:
   1624         case EthiopicHalehameAaEt:
   1625         case EthiopicHalehameAmEt:
   1626         case EthiopicHalehameGez:
   1627         case EthiopicHalehameOmEt:
   1628         case EthiopicHalehameSidEt:
   1629         case EthiopicHalehameSoEt:
   1630         case EthiopicHalehameTiEr:
   1631         case EthiopicHalehameTiEt:
   1632         case EthiopicHalehameTig:
   1633         case Georgian:
   1634         case Gujarati:
   1635         case Gurmukhi:
   1636         case Hangul:
   1637         case HangulConsonant:
   1638         case Hebrew:
   1639         case Hiragana:
   1640         case HiraganaIroha:
   1641         case Kannada:
   1642         case Katakana:
   1643         case KatakanaIroha:
   1644         case Khmer:
   1645         case Lao:
   1646         case LowerAlpha:
   1647         case LowerArmenian:
   1648         case LowerGreek:
   1649         case LowerHexadecimal:
   1650         case LowerLatin:
   1651         case LowerNorwegian:
   1652         case LowerRoman:
   1653         case Malayalam:
   1654         case Mongolian:
   1655         case Myanmar:
   1656         case Octal:
   1657         case Oriya:
   1658         case Oromo:
   1659         case Persian:
   1660         case Sidama:
   1661         case Somali:
   1662         case Telugu:
   1663         case Thai:
   1664         case Tibetan:
   1665         case Tigre:
   1666         case TigrinyaEr:
   1667         case TigrinyaErAbegede:
   1668         case TigrinyaEt:
   1669         case TigrinyaEtAbegede:
   1670         case UpperAlpha:
   1671         case UpperArmenian:
   1672         case UpperGreek:
   1673         case UpperHexadecimal:
   1674         case UpperLatin:
   1675         case UpperNorwegian:
   1676         case UpperRoman:
   1677         case Urdu:
   1678             if (m_text.isEmpty())
   1679                 return IntRect();
   1680             const Font& font = style()->font();
   1681             int itemWidth = font.width(m_text);
   1682             UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
   1683             int suffixSpaceWidth = font.width(TextRun(suffixSpace, 2));
   1684             relativeRect = IntRect(0, 0, itemWidth + suffixSpaceWidth, font.fontMetrics().height());
   1685     }
   1686 
   1687     if (!style()->isHorizontalWritingMode()) {
   1688         relativeRect = relativeRect.transposedRect();
   1689         relativeRect.setX(width() - relativeRect.x() - relativeRect.width());
   1690     }
   1691 
   1692     return relativeRect;
   1693 }
   1694 
   1695 void RenderListMarker::setSelectionState(SelectionState state)
   1696 {
   1697     RenderBox::setSelectionState(state);
   1698     if (InlineBox* box = inlineBoxWrapper())
   1699         if (RootInlineBox* root = box->root())
   1700             root->setHasSelectedChildren(state != SelectionNone);
   1701     containingBlock()->setSelectionState(state);
   1702 }
   1703 
   1704 IntRect RenderListMarker::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
   1705 {
   1706     ASSERT(!needsLayout());
   1707 
   1708     if (selectionState() == SelectionNone || !inlineBoxWrapper())
   1709         return IntRect();
   1710 
   1711     RootInlineBox* root = inlineBoxWrapper()->root();
   1712     IntRect rect(0, root->selectionTop() - y(), width(), root->selectionHeight());
   1713 
   1714     if (clipToVisibleContent)
   1715         computeRectForRepaint(repaintContainer, rect);
   1716     else
   1717         rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
   1718 
   1719     return rect;
   1720 }
   1721 
   1722 } // namespace WebCore
   1723