Add WTF::move()
[WebKit-https.git] / Source / WebCore / rendering / RenderListMarker.cpp
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
39 using namespace WTF;
40 using namespace Unicode;
41
42 namespace WebCore {
43
44 const int cMarkerPadding = 7;
45
46 enum SequenceType { NumericSequence, AlphabeticSequence };
47
48 static 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
84 template <typename CharacterType>
85 static 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
122 template <typename CharacterType>
123 static 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
137 template <typename CharacterType>
138 static NEVER_INLINE void toAlphabetic(StringBuilder& builder, int number, const CharacterType* alphabet, unsigned alphabetSize)
139 {
140     toAlphabeticOrNumeric(builder, number, alphabet, alphabetSize, AlphabeticSequence);
141 }
142
143 template <typename CharacterType>
144 static NEVER_INLINE void toNumeric(StringBuilder& builder, int number, const CharacterType* numerals, unsigned numeralsSize)
145 {
146     toAlphabeticOrNumeric(builder, number, numerals, numeralsSize, NumericSequence);
147 }
148
149 template <typename CharacterType, size_t size>
150 static inline void toAlphabetic(StringBuilder& builder, int number, const CharacterType(&alphabet)[size])
151 {
152     toAlphabetic(builder, number, alphabet, size);
153 }
154
155 template <typename CharacterType, size_t size>
156 static inline void toNumeric(StringBuilder& builder, int number, const CharacterType(&alphabet)[size])
157 {
158     toNumeric(builder, number, alphabet, size);
159 }
160
161 template <typename CharacterType, size_t size>
162 static inline void toSymbolic(StringBuilder& builder, int number, const CharacterType(&alphabet)[size])
163 {    
164     toSymbolic(builder, number, alphabet, size);
165 }
166
167 static 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
195 static 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
223 static 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
263 static 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
277 static 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.
323 static 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
405 static 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
504 static 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
603 String 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
1120 RenderListMarker::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
1129 RenderListMarker::~RenderListMarker()
1130 {
1131     if (m_image)
1132         m_image->removeClient(this);
1133 }
1134
1135 void 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
1151 std::unique_ptr<InlineElementBox> RenderListMarker::createInlineBox()
1152 {
1153     auto box = RenderBox::createInlineBox();
1154     box->setBehavesLikeText(isText());
1155     return box;
1156 }
1157
1158 bool RenderListMarker::isImage() const
1159 {
1160     return m_image && !m_image->errorOccurred();
1161 }
1162
1163 LayoutRect 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
1175 void 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
1365 void 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
1392 void 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
1404 void RenderListMarker::updateMarginsAndContent()
1405 {
1406     updateContent();
1407     updateMargins();
1408 }
1409
1410 void 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
1518 void 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
1644 void 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
1709 LayoutUnit 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
1716 int 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
1723 String 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
1744 bool RenderListMarker::isInside() const
1745 {
1746     return m_listItem.notInList() || style().listStylePosition() == INSIDE;
1747 }
1748
1749 IntRect 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
1869 void 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
1878 LayoutRect 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