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