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