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