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