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