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