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 "RenderListMarker.h"
27
28#include "Document.h"
29#include "Font.h"
30#include "GraphicsContext.h"
31#include "RenderLayer.h"
32#include "RenderListItem.h"
33#include "RenderView.h"
34#include <wtf/StackStats.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 (document->renderArena()) 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    if (paintInfo.phase != PaintPhaseForeground)
1127        return;
1128
1129    if (style()->visibility() != VISIBLE)
1130        return;
1131
1132    LayoutPoint boxOrigin(paintOffset + location());
1133    LayoutRect overflowRect(visualOverflowRect());
1134    overflowRect.moveBy(boxOrigin);
1135    overflowRect.inflate(maximalOutlineSize(paintInfo.phase));
1136
1137    if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
1138        return;
1139
1140    LayoutRect box(boxOrigin, size());
1141
1142    IntRect marker = getRelativeMarkerRect();
1143    marker.moveBy(roundedIntPoint(boxOrigin));
1144
1145    GraphicsContext* context = paintInfo.context;
1146
1147    if (isImage()) {
1148#if PLATFORM(MAC)
1149        if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
1150            paintCustomHighlight(paintOffset, style()->highlight(), true);
1151#endif
1152        context->drawImage(m_image->image(this, marker.size()).get(), style()->colorSpace(), marker);
1153        if (selectionState() != SelectionNone) {
1154            LayoutRect selRect = localSelectionRect();
1155            selRect.moveBy(boxOrigin);
1156            context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor(), style()->colorSpace());
1157        }
1158        return;
1159    }
1160
1161#if PLATFORM(MAC)
1162    // FIXME: paint gap between marker and list item proper
1163    if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
1164        paintCustomHighlight(paintOffset, style()->highlight(), true);
1165#endif
1166
1167    if (selectionState() != SelectionNone) {
1168        LayoutRect selRect = localSelectionRect();
1169        selRect.moveBy(boxOrigin);
1170        context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor(), style()->colorSpace());
1171    }
1172
1173    const Color color(style()->visitedDependentColor(CSSPropertyColor));
1174    context->setStrokeColor(color, style()->colorSpace());
1175    context->setStrokeStyle(SolidStroke);
1176    context->setStrokeThickness(1.0f);
1177    context->setFillColor(color, style()->colorSpace());
1178
1179    EListStyleType type = style()->listStyleType();
1180    switch (type) {
1181        case Disc:
1182            context->drawEllipse(marker);
1183            return;
1184        case Circle:
1185            context->setFillColor(Color::transparent, ColorSpaceDeviceRGB);
1186            context->drawEllipse(marker);
1187            return;
1188        case Square:
1189            context->drawRect(marker);
1190            return;
1191        case NoneListStyle:
1192            return;
1193        case Afar:
1194        case Amharic:
1195        case AmharicAbegede:
1196        case ArabicIndic:
1197        case Armenian:
1198        case BinaryListStyle:
1199        case Bengali:
1200        case Cambodian:
1201        case CJKIdeographic:
1202        case CjkEarthlyBranch:
1203        case CjkHeavenlyStem:
1204        case DecimalLeadingZero:
1205        case DecimalListStyle:
1206        case Devanagari:
1207        case Ethiopic:
1208        case EthiopicAbegede:
1209        case EthiopicAbegedeAmEt:
1210        case EthiopicAbegedeGez:
1211        case EthiopicAbegedeTiEr:
1212        case EthiopicAbegedeTiEt:
1213        case EthiopicHalehameAaEr:
1214        case EthiopicHalehameAaEt:
1215        case EthiopicHalehameAmEt:
1216        case EthiopicHalehameGez:
1217        case EthiopicHalehameOmEt:
1218        case EthiopicHalehameSidEt:
1219        case EthiopicHalehameSoEt:
1220        case EthiopicHalehameTiEr:
1221        case EthiopicHalehameTiEt:
1222        case EthiopicHalehameTig:
1223        case Georgian:
1224        case Gujarati:
1225        case Gurmukhi:
1226        case Hangul:
1227        case HangulConsonant:
1228        case Hebrew:
1229        case Hiragana:
1230        case HiraganaIroha:
1231        case Kannada:
1232        case Katakana:
1233        case KatakanaIroha:
1234        case Khmer:
1235        case Lao:
1236        case LowerAlpha:
1237        case LowerArmenian:
1238        case LowerGreek:
1239        case LowerHexadecimal:
1240        case LowerLatin:
1241        case LowerNorwegian:
1242        case LowerRoman:
1243        case Malayalam:
1244        case Mongolian:
1245        case Myanmar:
1246        case Octal:
1247        case Oriya:
1248        case Oromo:
1249        case Persian:
1250        case Sidama:
1251        case Somali:
1252        case Telugu:
1253        case Thai:
1254        case Tibetan:
1255        case Tigre:
1256        case TigrinyaEr:
1257        case TigrinyaErAbegede:
1258        case TigrinyaEt:
1259        case TigrinyaEtAbegede:
1260        case UpperAlpha:
1261        case UpperArmenian:
1262        case UpperGreek:
1263        case UpperHexadecimal:
1264        case UpperLatin:
1265        case UpperNorwegian:
1266        case UpperRoman:
1267        case Urdu:
1268        case Asterisks:
1269        case Footnotes:
1270            break;
1271    }
1272    if (m_text.isEmpty())
1273        return;
1274
1275    const Font& font = style()->font();
1276    TextRun textRun = RenderBlock::constructTextRun(this, font, m_text, style());
1277
1278    GraphicsContextStateSaver stateSaver(*context, false);
1279    if (!style()->isHorizontalWritingMode()) {
1280        marker.moveBy(roundedIntPoint(-boxOrigin));
1281        marker = marker.transposedRect();
1282        marker.moveBy(IntPoint(roundToInt(box.x()), roundToInt(box.y() - logicalHeight())));
1283        stateSaver.save();
1284        context->translate(marker.x(), marker.maxY());
1285        context->rotate(static_cast<float>(deg2rad(90.)));
1286        context->translate(-marker.x(), -marker.maxY());
1287    }
1288
1289    IntPoint textOrigin = IntPoint(marker.x(), marker.y() + style()->fontMetrics().ascent());
1290
1291    if (type == Asterisks || type == Footnotes)
1292        context->drawText(font, textRun, textOrigin);
1293    else {
1294        // Text is not arbitrary. We can judge whether it's RTL from the first character,
1295        // and we only need to handle the direction RightToLeft for now.
1296        bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
1297        StringBuilder reversedText;
1298        if (textNeedsReversing) {
1299            int length = m_text.length();
1300            reversedText.reserveCapacity(length);
1301            for (int i = length - 1; i >= 0; --i)
1302                reversedText.append(m_text[i]);
1303            textRun.setText(reversedText.characters(), length);
1304        }
1305
1306        const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1307        if (style()->isLeftToRightDirection()) {
1308            int width = font.width(textRun);
1309            context->drawText(font, textRun, textOrigin);
1310            UChar suffixSpace[2] = { suffix, ' ' };
1311            context->drawText(font, RenderBlock::constructTextRun(this, font, suffixSpace, 2, style()), textOrigin + IntSize(width, 0));
1312        } else {
1313            UChar spaceSuffix[2] = { ' ', suffix };
1314            TextRun spaceSuffixRun = RenderBlock::constructTextRun(this, font, spaceSuffix, 2, style());
1315            int width = font.width(spaceSuffixRun);
1316            context->drawText(font, spaceSuffixRun, textOrigin);
1317            context->drawText(font, textRun, textOrigin + IntSize(width, 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    setNeedsLayout(false);
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()) {
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    if (!isImage())
1673        return m_listItem->baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes);
1674    return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1675}
1676
1677String RenderListMarker::suffix() const
1678{
1679    EListStyleType type = style()->listStyleType();
1680    const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1681
1682    if (suffix == ' ')
1683        return String(" ");
1684
1685    // If the suffix is not ' ', an extra space is needed
1686    UChar data[2];
1687    if (style()->isLeftToRightDirection()) {
1688        data[0] = suffix;
1689        data[1] = ' ';
1690    } else {
1691        data[0] = ' ';
1692        data[1] = suffix;
1693    }
1694
1695    return String(data, 2);
1696}
1697
1698bool RenderListMarker::isInside() const
1699{
1700    return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
1701}
1702
1703IntRect RenderListMarker::getRelativeMarkerRect()
1704{
1705    if (isImage())
1706        return IntRect(0, 0, m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height());
1707
1708    IntRect relativeRect;
1709    EListStyleType type = style()->listStyleType();
1710    switch (type) {
1711        case Asterisks:
1712        case Footnotes: {
1713            const Font& font = style()->font();
1714            relativeRect = IntRect(0, 0, font.width(m_text), font.fontMetrics().height());
1715            break;
1716        }
1717        case Disc:
1718        case Circle:
1719        case Square: {
1720            // FIXME: Are these particular rounding rules necessary?
1721            const FontMetrics& fontMetrics = style()->fontMetrics();
1722            int ascent = fontMetrics.ascent();
1723            int bulletWidth = (ascent * 2 / 3 + 1) / 2;
1724            relativeRect = IntRect(1, 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
1725            break;
1726        }
1727        case NoneListStyle:
1728            return IntRect();
1729        case Afar:
1730        case Amharic:
1731        case AmharicAbegede:
1732        case ArabicIndic:
1733        case Armenian:
1734        case BinaryListStyle:
1735        case Bengali:
1736        case Cambodian:
1737        case CJKIdeographic:
1738        case CjkEarthlyBranch:
1739        case CjkHeavenlyStem:
1740        case DecimalLeadingZero:
1741        case DecimalListStyle:
1742        case Devanagari:
1743        case Ethiopic:
1744        case EthiopicAbegede:
1745        case EthiopicAbegedeAmEt:
1746        case EthiopicAbegedeGez:
1747        case EthiopicAbegedeTiEr:
1748        case EthiopicAbegedeTiEt:
1749        case EthiopicHalehameAaEr:
1750        case EthiopicHalehameAaEt:
1751        case EthiopicHalehameAmEt:
1752        case EthiopicHalehameGez:
1753        case EthiopicHalehameOmEt:
1754        case EthiopicHalehameSidEt:
1755        case EthiopicHalehameSoEt:
1756        case EthiopicHalehameTiEr:
1757        case EthiopicHalehameTiEt:
1758        case EthiopicHalehameTig:
1759        case Georgian:
1760        case Gujarati:
1761        case Gurmukhi:
1762        case Hangul:
1763        case HangulConsonant:
1764        case Hebrew:
1765        case Hiragana:
1766        case HiraganaIroha:
1767        case Kannada:
1768        case Katakana:
1769        case KatakanaIroha:
1770        case Khmer:
1771        case Lao:
1772        case LowerAlpha:
1773        case LowerArmenian:
1774        case LowerGreek:
1775        case LowerHexadecimal:
1776        case LowerLatin:
1777        case LowerNorwegian:
1778        case LowerRoman:
1779        case Malayalam:
1780        case Mongolian:
1781        case Myanmar:
1782        case Octal:
1783        case Oriya:
1784        case Oromo:
1785        case Persian:
1786        case Sidama:
1787        case Somali:
1788        case Telugu:
1789        case Thai:
1790        case Tibetan:
1791        case Tigre:
1792        case TigrinyaEr:
1793        case TigrinyaErAbegede:
1794        case TigrinyaEt:
1795        case TigrinyaEtAbegede:
1796        case UpperAlpha:
1797        case UpperArmenian:
1798        case UpperGreek:
1799        case UpperHexadecimal:
1800        case UpperLatin:
1801        case UpperNorwegian:
1802        case UpperRoman:
1803        case Urdu:
1804            if (m_text.isEmpty())
1805                return IntRect();
1806            const Font& font = style()->font();
1807            int itemWidth = font.width(m_text);
1808            UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
1809            int suffixSpaceWidth = font.width(RenderBlock::constructTextRun(this, font, suffixSpace, 2, style()));
1810            relativeRect = IntRect(0, 0, itemWidth + suffixSpaceWidth, font.fontMetrics().height());
1811    }
1812
1813    if (!style()->isHorizontalWritingMode()) {
1814        relativeRect = relativeRect.transposedRect();
1815        relativeRect.setX(width() - relativeRect.x() - relativeRect.width());
1816    }
1817
1818    return relativeRect;
1819}
1820
1821void RenderListMarker::setSelectionState(SelectionState state)
1822{
1823    // The selection state for our containing block hierarchy is updated by the base class call.
1824    RenderBox::setSelectionState(state);
1825
1826    if (m_inlineBoxWrapper && canUpdateSelectionOnRootLineBoxes())
1827        if (RootInlineBox* root = m_inlineBoxWrapper->root())
1828            root->setHasSelectedChildren(state != SelectionNone);
1829}
1830
1831LayoutRect RenderListMarker::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent)
1832{
1833    ASSERT(!needsLayout());
1834
1835    if (selectionState() == SelectionNone || !inlineBoxWrapper())
1836        return LayoutRect();
1837
1838    RootInlineBox* root = inlineBoxWrapper()->root();
1839    LayoutRect rect(0, root->selectionTop() - y(), width(), root->selectionHeight());
1840
1841    if (clipToVisibleContent)
1842        computeRectForRepaint(repaintContainer, rect);
1843    else
1844        rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
1845
1846    return rect;
1847}
1848
1849} // namespace WebCore
1850