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