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