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