Replace WTF::move with WTFMove
[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 "FontCascade.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.appendLiteral("-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, Ref<RenderStyle>&& style)
1121     : RenderBox(listItem.document(), WTFMove(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     m_listItem.didDestroyListMarker();
1132     if (m_image)
1133         m_image->removeClient(this);
1134 }
1135
1136 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
1137 {
1138     RenderBox::styleDidChange(diff, oldStyle);
1139
1140     if (oldStyle && (style().listStylePosition() != oldStyle->listStylePosition() || style().listStyleType() != oldStyle->listStyleType()))
1141         setNeedsLayoutAndPrefWidthsRecalc();
1142
1143     if (m_image != style().listStyleImage()) {
1144         if (m_image)
1145             m_image->removeClient(this);
1146         m_image = style().listStyleImage();
1147         if (m_image)
1148             m_image->addClient(this);
1149     }
1150 }
1151
1152 std::unique_ptr<InlineElementBox> RenderListMarker::createInlineBox()
1153 {
1154     auto box = RenderBox::createInlineBox();
1155     box->setBehavesLikeText(isText());
1156     return box;
1157 }
1158
1159 bool RenderListMarker::isImage() const
1160 {
1161     return m_image && !m_image->errorOccurred();
1162 }
1163
1164 LayoutRect RenderListMarker::localSelectionRect()
1165 {
1166     InlineBox* box = inlineBoxWrapper();
1167     if (!box)
1168         return LayoutRect(LayoutPoint(), size());
1169     const RootInlineBox& rootBox = m_inlineBoxWrapper->root();
1170     LayoutUnit newLogicalTop = rootBox.blockFlow().style().isFlippedBlocksWritingMode() ? m_inlineBoxWrapper->logicalBottom() - rootBox.selectionBottom() : rootBox.selectionTop() - m_inlineBoxWrapper->logicalTop();
1171     if (rootBox.blockFlow().style().isHorizontalWritingMode())
1172         return LayoutRect(0, newLogicalTop, width(), rootBox.selectionHeight());
1173     return LayoutRect(newLogicalTop, 0, rootBox.selectionHeight(), height());
1174 }
1175
1176 void RenderListMarker::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1177 {
1178     if (paintInfo.phase != PaintPhaseForeground)
1179         return;
1180     
1181     if (style().visibility() != VISIBLE)
1182         return;
1183
1184     LayoutPoint boxOrigin(paintOffset + location());
1185     LayoutRect overflowRect(visualOverflowRect());
1186     overflowRect.moveBy(boxOrigin);
1187     adjustRectWithMaximumOutline(paintInfo.phase, overflowRect);
1188
1189     if (!paintInfo.rect.intersects(overflowRect))
1190         return;
1191
1192     LayoutRect box(boxOrigin, size());
1193     
1194     FloatRect marker = getRelativeMarkerRect();
1195     marker.moveBy(boxOrigin);
1196
1197     GraphicsContext& context = paintInfo.context();
1198
1199     if (isImage()) {
1200         if (RefPtr<Image> markerImage = m_image->image(this, marker.size()))
1201             context.drawImage(*markerImage, marker);
1202         if (selectionState() != SelectionNone) {
1203             LayoutRect selRect = localSelectionRect();
1204             selRect.moveBy(boxOrigin);
1205             context.fillRect(snappedIntRect(selRect), m_listItem.selectionBackgroundColor());
1206         }
1207         return;
1208     }
1209
1210     if (selectionState() != SelectionNone) {
1211         LayoutRect selRect = localSelectionRect();
1212         selRect.moveBy(boxOrigin);
1213         context.fillRect(snappedIntRect(selRect), m_listItem.selectionBackgroundColor());
1214     }
1215
1216     const Color color(style().visitedDependentColor(CSSPropertyColor));
1217     context.setStrokeColor(color);
1218     context.setStrokeStyle(SolidStroke);
1219     context.setStrokeThickness(1.0f);
1220     context.setFillColor(color);
1221
1222     EListStyleType type = style().listStyleType();
1223     switch (type) {
1224         case Disc:
1225             context.drawEllipse(marker);
1226             return;
1227         case Circle:
1228             context.setFillColor(Color::transparent);
1229             context.drawEllipse(marker);
1230             return;
1231         case Square:
1232             context.drawRect(marker);
1233             return;
1234         case NoneListStyle:
1235             return;
1236         case Afar:
1237         case Amharic:
1238         case AmharicAbegede:
1239         case ArabicIndic:
1240         case Armenian:
1241         case BinaryListStyle:
1242         case Bengali:
1243         case Cambodian:
1244         case CJKIdeographic:
1245         case CjkEarthlyBranch:
1246         case CjkHeavenlyStem:
1247         case DecimalLeadingZero:
1248         case DecimalListStyle:
1249         case Devanagari:
1250         case Ethiopic:
1251         case EthiopicAbegede:
1252         case EthiopicAbegedeAmEt:
1253         case EthiopicAbegedeGez:
1254         case EthiopicAbegedeTiEr:
1255         case EthiopicAbegedeTiEt:
1256         case EthiopicHalehameAaEr:
1257         case EthiopicHalehameAaEt:
1258         case EthiopicHalehameAmEt:
1259         case EthiopicHalehameGez:
1260         case EthiopicHalehameOmEt:
1261         case EthiopicHalehameSidEt:
1262         case EthiopicHalehameSoEt:
1263         case EthiopicHalehameTiEr:
1264         case EthiopicHalehameTiEt:
1265         case EthiopicHalehameTig:
1266         case Georgian:
1267         case Gujarati:
1268         case Gurmukhi:
1269         case Hangul:
1270         case HangulConsonant:
1271         case Hebrew:
1272         case Hiragana:
1273         case HiraganaIroha:
1274         case Kannada:
1275         case Katakana:
1276         case KatakanaIroha:
1277         case Khmer:
1278         case Lao:
1279         case LowerAlpha:
1280         case LowerArmenian:
1281         case LowerGreek:
1282         case LowerHexadecimal:
1283         case LowerLatin:
1284         case LowerNorwegian:
1285         case LowerRoman:
1286         case Malayalam:
1287         case Mongolian:
1288         case Myanmar:
1289         case Octal:
1290         case Oriya:
1291         case Oromo:
1292         case Persian:
1293         case Sidama:
1294         case Somali:
1295         case Telugu:
1296         case Thai:
1297         case Tibetan:
1298         case Tigre:
1299         case TigrinyaEr:
1300         case TigrinyaErAbegede:
1301         case TigrinyaEt:
1302         case TigrinyaEtAbegede:
1303         case UpperAlpha:
1304         case UpperArmenian:
1305         case UpperGreek:
1306         case UpperHexadecimal:
1307         case UpperLatin:
1308         case UpperNorwegian:
1309         case UpperRoman:
1310         case Urdu:
1311         case Asterisks:
1312         case Footnotes:
1313             break;
1314     }
1315     if (m_text.isEmpty())
1316         return;
1317
1318     const FontCascade& font = style().fontCascade();
1319     TextRun textRun = RenderBlock::constructTextRun(this, font, m_text, style());
1320
1321     GraphicsContextStateSaver stateSaver(context, false);
1322     if (!style().isHorizontalWritingMode()) {
1323         marker.moveBy(-boxOrigin);
1324         marker = marker.transposedRect();
1325         marker.moveBy(FloatPoint(box.x(), box.y() - logicalHeight()));
1326         stateSaver.save();
1327         context.translate(marker.x(), marker.maxY());
1328         context.rotate(static_cast<float>(deg2rad(90.)));
1329         context.translate(-marker.x(), -marker.maxY());
1330     }
1331
1332     FloatPoint textOrigin = FloatPoint(marker.x(), marker.y() + style().fontMetrics().ascent());
1333     textOrigin = roundPointToDevicePixels(LayoutPoint(textOrigin), document().deviceScaleFactor(), style().isLeftToRightDirection());
1334
1335     if (type == Asterisks || type == Footnotes)
1336         context.drawText(font, textRun, textOrigin);
1337     else {
1338         const UChar suffix = listMarkerSuffix(type, m_listItem.value());
1339
1340         // Text is not arbitrary. We can judge whether it's RTL from the first character,
1341         // and we only need to handle the direction U_RIGHT_TO_LEFT for now.
1342         bool textNeedsReversing = u_charDirection(m_text[0]) == U_RIGHT_TO_LEFT;
1343         String toDraw;
1344         if (textNeedsReversing) {
1345             unsigned length = m_text.length();
1346             StringBuilder buffer;
1347             buffer.reserveCapacity(length + 2);
1348             if (!style().isLeftToRightDirection()) {
1349                 buffer.append(space);
1350                 buffer.append(suffix);
1351             }
1352             for (unsigned i = 0; i < length; ++i)
1353                 buffer.append(m_text[length - i - 1]);
1354             if (style().isLeftToRightDirection()) {
1355                 buffer.append(suffix);
1356                 buffer.append(space);
1357             }
1358             toDraw = buffer.toString();
1359         } else {
1360             if (style().isLeftToRightDirection())
1361                 toDraw = m_text + String(&suffix, 1) + String(&space, 1);
1362             else
1363                 toDraw = String(&space, 1) + String(&suffix, 1) + m_text;
1364         }
1365         textRun.setText(StringView(toDraw));
1366
1367         context.drawText(font, textRun, textOrigin);
1368     }
1369 }
1370
1371 void RenderListMarker::layout()
1372 {
1373     StackStats::LayoutCheckPoint layoutCheckPoint;
1374     ASSERT(needsLayout());
1375  
1376     if (isImage()) {
1377         updateMarginsAndContent();
1378         setWidth(m_image->imageSize(this, style().effectiveZoom()).width());
1379         setHeight(m_image->imageSize(this, style().effectiveZoom()).height());
1380     } else {
1381         setLogicalWidth(minPreferredLogicalWidth());
1382         setLogicalHeight(style().fontMetrics().height());
1383     }
1384
1385     setMarginStart(0);
1386     setMarginEnd(0);
1387
1388     Length startMargin = style().marginStart();
1389     Length endMargin = style().marginEnd();
1390     if (startMargin.isFixed())
1391         setMarginStart(startMargin.value());
1392     if (endMargin.isFixed())
1393         setMarginEnd(endMargin.value());
1394
1395     clearNeedsLayout();
1396 }
1397
1398 void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*)
1399 {
1400     // A list marker can't have a background or border image, so no need to call the base class method.
1401     if (o != m_image->data())
1402         return;
1403
1404     if (width() != m_image->imageSize(this, style().effectiveZoom()).width() || height() != m_image->imageSize(this, style().effectiveZoom()).height() || m_image->errorOccurred())
1405         setNeedsLayoutAndPrefWidthsRecalc();
1406     else
1407         repaint();
1408 }
1409
1410 void RenderListMarker::updateMarginsAndContent()
1411 {
1412     updateContent();
1413     updateMargins();
1414 }
1415
1416 void RenderListMarker::updateContent()
1417 {
1418     // FIXME: This if-statement is just a performance optimization, but it's messy to use the preferredLogicalWidths dirty bit for this.
1419     // It's unclear if this is a premature optimization.
1420     if (!preferredLogicalWidthsDirty())
1421         return;
1422
1423     m_text = "";
1424
1425     if (isImage()) {
1426         // FIXME: This is a somewhat arbitrary width.  Generated images for markers really won't become particularly useful
1427         // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box.
1428         LayoutUnit bulletWidth = style().fontMetrics().ascent() / LayoutUnit(2);
1429         LayoutSize defaultBulletSize(bulletWidth, bulletWidth);
1430         LayoutSize imageSize = calculateImageIntrinsicDimensions(m_image.get(), defaultBulletSize, DoNotScaleByEffectiveZoom);
1431         m_image->setContainerSizeForRenderer(this, imageSize, style().effectiveZoom());
1432         return;
1433     }
1434
1435     EListStyleType type = style().listStyleType();
1436     switch (type) {
1437     case NoneListStyle:
1438         break;
1439     case Circle:
1440     case Disc:
1441     case Square:
1442         m_text = listMarkerText(type, 0); // value is ignored for these types
1443         break;
1444     case Asterisks:
1445     case Footnotes:
1446     case Afar:
1447     case Amharic:
1448     case AmharicAbegede:
1449     case ArabicIndic:
1450     case Armenian:
1451     case BinaryListStyle:
1452     case Bengali:
1453     case Cambodian:
1454     case CJKIdeographic:
1455     case CjkEarthlyBranch:
1456     case CjkHeavenlyStem:
1457     case DecimalLeadingZero:
1458     case DecimalListStyle:
1459     case Devanagari:
1460     case Ethiopic:
1461     case EthiopicAbegede:
1462     case EthiopicAbegedeAmEt:
1463     case EthiopicAbegedeGez:
1464     case EthiopicAbegedeTiEr:
1465     case EthiopicAbegedeTiEt:
1466     case EthiopicHalehameAaEr:
1467     case EthiopicHalehameAaEt:
1468     case EthiopicHalehameAmEt:
1469     case EthiopicHalehameGez:
1470     case EthiopicHalehameOmEt:
1471     case EthiopicHalehameSidEt:
1472     case EthiopicHalehameSoEt:
1473     case EthiopicHalehameTiEr:
1474     case EthiopicHalehameTiEt:
1475     case EthiopicHalehameTig:
1476     case Georgian:
1477     case Gujarati:
1478     case Gurmukhi:
1479     case Hangul:
1480     case HangulConsonant:
1481     case Hebrew:
1482     case Hiragana:
1483     case HiraganaIroha:
1484     case Kannada:
1485     case Katakana:
1486     case KatakanaIroha:
1487     case Khmer:
1488     case Lao:
1489     case LowerAlpha:
1490     case LowerArmenian:
1491     case LowerGreek:
1492     case LowerHexadecimal:
1493     case LowerLatin:
1494     case LowerNorwegian:
1495     case LowerRoman:
1496     case Malayalam:
1497     case Mongolian:
1498     case Myanmar:
1499     case Octal:
1500     case Oriya:
1501     case Oromo:
1502     case Persian:
1503     case Sidama:
1504     case Somali:
1505     case Telugu:
1506     case Thai:
1507     case Tibetan:
1508     case Tigre:
1509     case TigrinyaEr:
1510     case TigrinyaErAbegede:
1511     case TigrinyaEt:
1512     case TigrinyaEtAbegede:
1513     case UpperAlpha:
1514     case UpperArmenian:
1515     case UpperGreek:
1516     case UpperHexadecimal:
1517     case UpperLatin:
1518     case UpperNorwegian:
1519     case UpperRoman:
1520     case Urdu:
1521         m_text = listMarkerText(type, m_listItem.value());
1522         break;
1523     }
1524 }
1525
1526 void RenderListMarker::computePreferredLogicalWidths()
1527 {
1528     ASSERT(preferredLogicalWidthsDirty());
1529     updateContent();
1530
1531     if (isImage()) {
1532         LayoutSize imageSize = LayoutSize(m_image->imageSize(this, style().effectiveZoom()));
1533         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = style().isHorizontalWritingMode() ? imageSize.width() : imageSize.height();
1534         setPreferredLogicalWidthsDirty(false);
1535         updateMargins();
1536         return;
1537     }
1538
1539     const FontCascade& font = style().fontCascade();
1540
1541     LayoutUnit logicalWidth = 0;
1542     EListStyleType type = style().listStyleType();
1543     switch (type) {
1544         case NoneListStyle:
1545             break;
1546         case Asterisks:
1547         case Footnotes: {
1548             TextRun run = RenderBlock::constructTextRun(this, font, m_text, style(), AllowTrailingExpansion | ForbidLeadingExpansion, DefaultTextRunFlags);
1549             logicalWidth = font.width(run); // no suffix for these types
1550         }
1551             break;
1552         case Circle:
1553         case Disc:
1554         case Square:
1555             logicalWidth = (font.fontMetrics().ascent() * 2 / 3 + 1) / 2 + 2;
1556             break;
1557         case Afar:
1558         case Amharic:
1559         case AmharicAbegede:
1560         case ArabicIndic:
1561         case Armenian:
1562         case BinaryListStyle:
1563         case Bengali:
1564         case Cambodian:
1565         case CJKIdeographic:
1566         case CjkEarthlyBranch:
1567         case CjkHeavenlyStem:
1568         case DecimalLeadingZero:
1569         case DecimalListStyle:
1570         case Devanagari:
1571         case Ethiopic:
1572         case EthiopicAbegede:
1573         case EthiopicAbegedeAmEt:
1574         case EthiopicAbegedeGez:
1575         case EthiopicAbegedeTiEr:
1576         case EthiopicAbegedeTiEt:
1577         case EthiopicHalehameAaEr:
1578         case EthiopicHalehameAaEt:
1579         case EthiopicHalehameAmEt:
1580         case EthiopicHalehameGez:
1581         case EthiopicHalehameOmEt:
1582         case EthiopicHalehameSidEt:
1583         case EthiopicHalehameSoEt:
1584         case EthiopicHalehameTiEr:
1585         case EthiopicHalehameTiEt:
1586         case EthiopicHalehameTig:
1587         case Georgian:
1588         case Gujarati:
1589         case Gurmukhi:
1590         case Hangul:
1591         case HangulConsonant:
1592         case Hebrew:
1593         case Hiragana:
1594         case HiraganaIroha:
1595         case Kannada:
1596         case Katakana:
1597         case KatakanaIroha:
1598         case Khmer:
1599         case Lao:
1600         case LowerAlpha:
1601         case LowerArmenian:
1602         case LowerGreek:
1603         case LowerHexadecimal:
1604         case LowerLatin:
1605         case LowerNorwegian:
1606         case LowerRoman:
1607         case Malayalam:
1608         case Mongolian:
1609         case Myanmar:
1610         case Octal:
1611         case Oriya:
1612         case Oromo:
1613         case Persian:
1614         case Sidama:
1615         case Somali:
1616         case Telugu:
1617         case Thai:
1618         case Tibetan:
1619         case Tigre:
1620         case TigrinyaEr:
1621         case TigrinyaErAbegede:
1622         case TigrinyaEt:
1623         case TigrinyaEtAbegede:
1624         case UpperAlpha:
1625         case UpperArmenian:
1626         case UpperGreek:
1627         case UpperHexadecimal:
1628         case UpperLatin:
1629         case UpperNorwegian:
1630         case UpperRoman:
1631         case Urdu:
1632             if (m_text.isEmpty())
1633                 logicalWidth = 0;
1634             else {
1635                 TextRun run = RenderBlock::constructTextRun(this, font, m_text, style(), AllowTrailingExpansion | ForbidLeadingExpansion, DefaultTextRunFlags);
1636                 LayoutUnit itemWidth = font.width(run);
1637                 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem.value()), ' ' };
1638                 LayoutUnit suffixSpaceWidth = font.width(RenderBlock::constructTextRun(this, font, suffixSpace, 2, style()));
1639                 logicalWidth = itemWidth + suffixSpaceWidth;
1640             }
1641             break;
1642     }
1643
1644     m_minPreferredLogicalWidth = logicalWidth;
1645     m_maxPreferredLogicalWidth = logicalWidth;
1646
1647     setPreferredLogicalWidthsDirty(false);
1648
1649     updateMargins();
1650 }
1651
1652 void RenderListMarker::updateMargins()
1653 {
1654     const FontMetrics& fontMetrics = style().fontMetrics();
1655
1656     LayoutUnit marginStart = 0;
1657     LayoutUnit marginEnd = 0;
1658
1659     if (isInside()) {
1660         if (isImage())
1661             marginEnd = cMarkerPadding;
1662         else switch (style().listStyleType()) {
1663             case Disc:
1664             case Circle:
1665             case Square:
1666                 marginStart = -1;
1667                 marginEnd = fontMetrics.ascent() - minPreferredLogicalWidth() + 1;
1668                 break;
1669             default:
1670                 break;
1671         }
1672     } else {
1673         if (style().isLeftToRightDirection()) {
1674             if (isImage())
1675                 marginStart = -minPreferredLogicalWidth() - cMarkerPadding;
1676             else {
1677                 int offset = fontMetrics.ascent() * 2 / 3;
1678                 switch (style().listStyleType()) {
1679                     case Disc:
1680                     case Circle:
1681                     case Square:
1682                         marginStart = -offset - cMarkerPadding - 1;
1683                         break;
1684                     case NoneListStyle:
1685                         break;
1686                     default:
1687                         marginStart = m_text.isEmpty() ? LayoutUnit() : -minPreferredLogicalWidth() - offset / 2;
1688                 }
1689             }
1690             marginEnd = -marginStart - minPreferredLogicalWidth();
1691         } else {
1692             if (isImage())
1693                 marginEnd = cMarkerPadding;
1694             else {
1695                 int offset = fontMetrics.ascent() * 2 / 3;
1696                 switch (style().listStyleType()) {
1697                     case Disc:
1698                     case Circle:
1699                     case Square:
1700                         marginEnd = offset + cMarkerPadding + 1 - minPreferredLogicalWidth();
1701                         break;
1702                     case NoneListStyle:
1703                         break;
1704                     default:
1705                         marginEnd = m_text.isEmpty() ? 0 : offset / 2;
1706                 }
1707             }
1708             marginStart = -marginEnd - minPreferredLogicalWidth();
1709         }
1710         
1711     }
1712
1713     style().setMarginStart(Length(marginStart, Fixed));
1714     style().setMarginEnd(Length(marginEnd, Fixed));
1715 }
1716
1717 LayoutUnit RenderListMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1718 {
1719     if (!isImage())
1720         return m_listItem.lineHeight(firstLine, direction, PositionOfInteriorLineBoxes);
1721     return RenderBox::lineHeight(firstLine, direction, linePositionMode);
1722 }
1723
1724 int RenderListMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1725 {
1726     if (!isImage())
1727         return m_listItem.baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes);
1728     return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1729 }
1730
1731 String RenderListMarker::suffix() const
1732 {
1733     EListStyleType type = style().listStyleType();
1734     const UChar suffix = listMarkerSuffix(type, m_listItem.value());
1735
1736     if (suffix == ' ')
1737         return String(" ");
1738
1739     // If the suffix is not ' ', an extra space is needed
1740     UChar data[2];
1741     if (style().isLeftToRightDirection()) {
1742         data[0] = suffix;
1743         data[1] = ' ';
1744     } else {
1745         data[0] = ' ';
1746         data[1] = suffix;
1747     }
1748
1749     return String(data, 2);
1750 }
1751
1752 bool RenderListMarker::isInside() const
1753 {
1754     return m_listItem.notInList() || style().listStylePosition() == INSIDE;
1755 }
1756
1757 FloatRect RenderListMarker::getRelativeMarkerRect()
1758 {
1759     if (isImage())
1760         return FloatRect(0, 0, m_image->imageSize(this, style().effectiveZoom()).width(), m_image->imageSize(this, style().effectiveZoom()).height());
1761     
1762     FloatRect relativeRect;
1763     EListStyleType type = style().listStyleType();
1764     switch (type) {
1765         case Asterisks:
1766         case Footnotes: {
1767             const FontCascade& font = style().fontCascade();
1768             TextRun run = RenderBlock::constructTextRun(this, font, m_text, style(), AllowTrailingExpansion | ForbidLeadingExpansion, DefaultTextRunFlags);
1769             relativeRect = FloatRect(0, 0, font.width(run), font.fontMetrics().height());
1770             break;
1771         }
1772         case Disc:
1773         case Circle:
1774         case Square: {
1775             // FIXME: Are these particular rounding rules necessary?
1776             const FontMetrics& fontMetrics = style().fontMetrics();
1777             int ascent = fontMetrics.ascent();
1778             int bulletWidth = (ascent * 2 / 3 + 1) / 2;
1779             relativeRect = FloatRect(1, 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
1780             break;
1781         }
1782         case NoneListStyle:
1783             return FloatRect();
1784         case Afar:
1785         case Amharic:
1786         case AmharicAbegede:
1787         case ArabicIndic:
1788         case Armenian:
1789         case BinaryListStyle:
1790         case Bengali:
1791         case Cambodian:
1792         case CJKIdeographic:
1793         case CjkEarthlyBranch:
1794         case CjkHeavenlyStem:
1795         case DecimalLeadingZero:
1796         case DecimalListStyle:
1797         case Devanagari:
1798         case Ethiopic:
1799         case EthiopicAbegede:
1800         case EthiopicAbegedeAmEt:
1801         case EthiopicAbegedeGez:
1802         case EthiopicAbegedeTiEr:
1803         case EthiopicAbegedeTiEt:
1804         case EthiopicHalehameAaEr:
1805         case EthiopicHalehameAaEt:
1806         case EthiopicHalehameAmEt:
1807         case EthiopicHalehameGez:
1808         case EthiopicHalehameOmEt:
1809         case EthiopicHalehameSidEt:
1810         case EthiopicHalehameSoEt:
1811         case EthiopicHalehameTiEr:
1812         case EthiopicHalehameTiEt:
1813         case EthiopicHalehameTig:
1814         case Georgian:
1815         case Gujarati:
1816         case Gurmukhi:
1817         case Hangul:
1818         case HangulConsonant:
1819         case Hebrew:
1820         case Hiragana:
1821         case HiraganaIroha:
1822         case Kannada:
1823         case Katakana:
1824         case KatakanaIroha:
1825         case Khmer:
1826         case Lao:
1827         case LowerAlpha:
1828         case LowerArmenian:
1829         case LowerGreek:
1830         case LowerHexadecimal:
1831         case LowerLatin:
1832         case LowerNorwegian:
1833         case LowerRoman:
1834         case Malayalam:
1835         case Mongolian:
1836         case Myanmar:
1837         case Octal:
1838         case Oriya:
1839         case Oromo:
1840         case Persian:
1841         case Sidama:
1842         case Somali:
1843         case Telugu:
1844         case Thai:
1845         case Tibetan:
1846         case Tigre:
1847         case TigrinyaEr:
1848         case TigrinyaErAbegede:
1849         case TigrinyaEt:
1850         case TigrinyaEtAbegede:
1851         case UpperAlpha:
1852         case UpperArmenian:
1853         case UpperGreek:
1854         case UpperHexadecimal:
1855         case UpperLatin:
1856         case UpperNorwegian:
1857         case UpperRoman:
1858         case Urdu:
1859             if (m_text.isEmpty())
1860                 return FloatRect();
1861             const FontCascade& font = style().fontCascade();
1862             TextRun run = RenderBlock::constructTextRun(this, font, m_text, style(), AllowTrailingExpansion | ForbidLeadingExpansion, DefaultTextRunFlags);
1863             float itemWidth = font.width(run);
1864             UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem.value()), ' ' };
1865             float suffixSpaceWidth = font.width(RenderBlock::constructTextRun(this, font, suffixSpace, 2, style()));
1866             relativeRect = FloatRect(0, 0, itemWidth + suffixSpaceWidth, font.fontMetrics().height());
1867     }
1868
1869     if (!style().isHorizontalWritingMode()) {
1870         relativeRect = relativeRect.transposedRect();
1871         relativeRect.setX(width() - relativeRect.x() - relativeRect.width());
1872     }
1873
1874     return relativeRect;
1875 }
1876
1877 void RenderListMarker::setSelectionState(SelectionState state)
1878 {
1879     // The selection state for our containing block hierarchy is updated by the base class call.
1880     RenderBox::setSelectionState(state);
1881
1882     if (m_inlineBoxWrapper && canUpdateSelectionOnRootLineBoxes())
1883         m_inlineBoxWrapper->root().setHasSelectedChildren(state != SelectionNone);
1884 }
1885
1886 LayoutRect RenderListMarker::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent)
1887 {
1888     ASSERT(!needsLayout());
1889
1890     if (selectionState() == SelectionNone || !inlineBoxWrapper())
1891         return LayoutRect();
1892
1893     RootInlineBox& rootBox = inlineBoxWrapper()->root();
1894     LayoutRect rect(0, rootBox.selectionTop() - y(), width(), rootBox.selectionHeight());
1895             
1896     if (clipToVisibleContent)
1897         return computeRectForRepaint(rect, repaintContainer);
1898     return localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
1899 }
1900
1901 } // namespace WebCore