Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / 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 "core/rendering/RenderListMarker.h"
27
28 #include "core/dom/Document.h"
29 #include "core/fetch/ImageResource.h"
30 #include "core/rendering/GraphicsContextAnnotator.h"
31 #include "core/rendering/RenderLayer.h"
32 #include "core/rendering/RenderListItem.h"
33 #include "core/rendering/RenderView.h"
34 #include "platform/fonts/Font.h"
35 #include "platform/graphics/GraphicsContextStateSaver.h"
36 #include "wtf/text/StringBuilder.h"
37 #include "wtf/unicode/CharacterNames.h"
38
39 using namespace std;
40 using namespace WTF;
41 using namespace Unicode;
42
43 namespace WebCore {
44
45 const int cMarkerPadding = 7;
46
47 enum SequenceType { NumericSequence, AlphabeticSequence };
48
49 static String toRoman(int number, bool upper)
50 {
51     // FIXME: CSS3 describes how to make this work for much larger numbers,
52     // using overbars and special characters. It also specifies the characters
53     // in the range U+2160 to U+217F instead of standard ASCII ones.
54     ASSERT(number >= 1 && number <= 3999);
55
56     // Big enough to store largest roman number less than 3999 which
57     // is 3888 (MMMDCCCLXXXVIII)
58     const int lettersSize = 15;
59     LChar letters[lettersSize];
60
61     int length = 0;
62     const LChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
63     const LChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
64     const LChar* digits = upper ? udigits : ldigits;
65     int d = 0;
66     do {
67         int num = number % 10;
68         if (num % 5 < 4)
69             for (int i = num % 5; i > 0; i--)
70                 letters[lettersSize - ++length] = digits[d];
71         if (num >= 4 && num <= 8)
72             letters[lettersSize - ++length] = digits[d + 1];
73         if (num == 9)
74             letters[lettersSize - ++length] = digits[d + 2];
75         if (num % 5 == 4)
76             letters[lettersSize - ++length] = digits[d];
77         number /= 10;
78         d += 2;
79     } while (number);
80
81     ASSERT(length <= lettersSize);
82     return String(&letters[lettersSize - length], length);
83 }
84
85 // The typedef is needed because taking sizeof(number) in the const expression below doesn't work with some compilers.
86 // This is likely the case because of the template.
87 typedef int numberType;
88
89 template <typename CharacterType>
90 static inline String toAlphabeticOrNumeric(numberType number, const CharacterType* sequence, unsigned sequenceSize, SequenceType type)
91 {
92     ASSERT(sequenceSize >= 2);
93
94     const int lettersSize = sizeof(numberType) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
95
96     CharacterType letters[lettersSize];
97
98     bool isNegativeNumber = false;
99     unsigned numberShadow = number;
100     if (type == AlphabeticSequence) {
101         ASSERT(number > 0);
102         --numberShadow;
103     } else if (number < 0) {
104         numberShadow = -number;
105         isNegativeNumber = true;
106     }
107     letters[lettersSize - 1] = sequence[numberShadow % sequenceSize];
108     int length = 1;
109
110     if (type == AlphabeticSequence) {
111         while ((numberShadow /= sequenceSize) > 0) {
112             --numberShadow;
113             letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
114         }
115     } else {
116         while ((numberShadow /= sequenceSize) > 0)
117             letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
118     }
119     if (isNegativeNumber)
120         letters[lettersSize - ++length] = hyphenMinus;
121
122     ASSERT(length <= lettersSize);
123     return String(&letters[lettersSize - length], length);
124 }
125
126 template <typename CharacterType>
127 static String toSymbolic(int number, const CharacterType* symbols, unsigned symbolsSize)
128 {
129     ASSERT(number > 0);
130     ASSERT(symbolsSize >= 1);
131     unsigned numberShadow = number;
132     --numberShadow;
133
134     // The asterisks list-style-type is the worst case; we show |numberShadow| asterisks.
135     StringBuilder letters;
136     letters.append(symbols[numberShadow % symbolsSize]);
137     unsigned numSymbols = numberShadow / symbolsSize;
138     while (numSymbols--)
139         letters.append(symbols[numberShadow % symbolsSize]);
140     return letters.toString();
141 }
142
143 template <typename CharacterType>
144 static String toAlphabetic(int number, const CharacterType* alphabet, unsigned alphabetSize)
145 {
146     return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence);
147 }
148
149 template <typename CharacterType>
150 static String toNumeric(int number, const CharacterType* numerals, unsigned numeralsSize)
151 {
152     return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence);
153 }
154
155 template <typename CharacterType, size_t size>
156 static inline String toAlphabetic(int number, const CharacterType(&alphabet)[size])
157 {
158     return toAlphabetic(number, alphabet, size);
159 }
160
161 template <typename CharacterType, size_t size>
162 static inline String toNumeric(int number, const CharacterType(&alphabet)[size])
163 {
164     return toNumeric(number, alphabet, size);
165 }
166
167 template <typename CharacterType, size_t size>
168 static inline String toSymbolic(int number, const CharacterType(&alphabet)[size])
169 {
170     return toSymbolic(number, alphabet, size);
171 }
172
173 static int toHebrewUnder1000(int number, UChar letters[5])
174 {
175     // FIXME: CSS3 mentions various refinements not implemented here.
176     // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
177     ASSERT(number >= 0 && number < 1000);
178     int length = 0;
179     int fourHundreds = number / 400;
180     for (int i = 0; i < fourHundreds; i++)
181         letters[length++] = 1511 + 3;
182     number %= 400;
183     if (number / 100)
184         letters[length++] = 1511 + (number / 100) - 1;
185     number %= 100;
186     if (number == 15 || number == 16) {
187         letters[length++] = 1487 + 9;
188         letters[length++] = 1487 + number - 9;
189     } else {
190         if (int tens = number / 10) {
191             static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
192             letters[length++] = hebrewTens[tens - 1];
193         }
194         if (int ones = number % 10)
195             letters[length++] = 1487 + ones;
196     }
197     ASSERT(length <= 5);
198     return length;
199 }
200
201 static String toHebrew(int number)
202 {
203     // FIXME: CSS3 mentions ways to make this work for much larger numbers.
204     ASSERT(number >= 0 && number <= 999999);
205
206     if (number == 0) {
207         static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
208         return String(hebrewZero, 3);
209     }
210
211     const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
212     UChar letters[lettersSize];
213
214     int length;
215     if (number < 1000)
216         length = 0;
217     else {
218         length = toHebrewUnder1000(number / 1000, letters);
219         letters[length++] = '\'';
220         number = number % 1000;
221     }
222     length += toHebrewUnder1000(number, letters + length);
223
224     ASSERT(length <= lettersSize);
225     return String(letters, length);
226 }
227
228 static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
229 {
230     ASSERT(number >= 0 && number < 10000);
231     int length = 0;
232
233     int lowerOffset = upper ? 0 : 0x0030;
234
235     if (int thousands = number / 1000) {
236         if (thousands == 7) {
237             letters[length++] = 0x0552 + lowerOffset;
238             if (addCircumflex)
239                 letters[length++] = 0x0302;
240         } else {
241             letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
242             if (addCircumflex)
243                 letters[length++] = 0x0302;
244         }
245     }
246
247     if (int hundreds = (number / 100) % 10) {
248         letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
249         if (addCircumflex)
250             letters[length++] = 0x0302;
251     }
252
253     if (int tens = (number / 10) % 10) {
254         letters[length++] = (0x053A - 1 + lowerOffset) + tens;
255         if (addCircumflex)
256             letters[length++] = 0x0302;
257     }
258
259     if (int ones = number % 10) {
260         letters[length++] = (0x531 - 1 + lowerOffset) + ones;
261         if (addCircumflex)
262             letters[length++] = 0x0302;
263     }
264
265     return length;
266 }
267
268 static String toArmenian(int number, bool upper)
269 {
270     ASSERT(number >= 1 && number <= 99999999);
271
272     const int lettersSize = 18; // twice what toArmenianUnder10000 needs
273     UChar letters[lettersSize];
274
275     int length = toArmenianUnder10000(number / 10000, upper, true, letters);
276     length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
277
278     ASSERT(length <= lettersSize);
279     return String(letters, length);
280 }
281
282 static String toGeorgian(int number)
283 {
284     ASSERT(number >= 1 && number <= 19999);
285
286     const int lettersSize = 5;
287     UChar letters[lettersSize];
288
289     int length = 0;
290
291     if (number > 9999)
292         letters[length++] = 0x10F5;
293
294     if (int thousands = (number / 1000) % 10) {
295         static const UChar georgianThousands[9] = {
296             0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
297         };
298         letters[length++] = georgianThousands[thousands - 1];
299     }
300
301     if (int hundreds = (number / 100) % 10) {
302         static const UChar georgianHundreds[9] = {
303             0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
304         };
305         letters[length++] = georgianHundreds[hundreds - 1];
306     }
307
308     if (int tens = (number / 10) % 10) {
309         static const UChar georgianTens[9] = {
310             0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
311         };
312         letters[length++] = georgianTens[tens - 1];
313     }
314
315     if (int ones = number % 10) {
316         static const UChar georgianOnes[9] = {
317             0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
318         };
319         letters[length++] = georgianOnes[ones - 1];
320     }
321
322     ASSERT(length <= lettersSize);
323     return String(letters, length);
324 }
325
326 // The table uses the order from the CSS3 specification:
327 // first 3 group markers, then 3 digit markers, then ten digits.
328 static String toCJKIdeographic(int number, const UChar table[16])
329 {
330     ASSERT(number >= 0);
331
332     enum AbstractCJKChar {
333         noChar,
334         secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
335         secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
336         digit0, digit1, digit2, digit3, digit4,
337         digit5, digit6, digit7, digit8, digit9
338     };
339
340     if (number == 0)
341         return String(&table[digit0 - 1], 1);
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     return String(characters, length);
406 }
407
408 static EListStyleType effectiveListMarkerType(EListStyleType 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 ArabicIndic:
414     case Bengali:
415     case BinaryListStyle:
416     case Cambodian:
417     case Circle:
418     case DecimalLeadingZero:
419     case DecimalListStyle:
420     case Devanagari:
421     case Disc:
422     case Gujarati:
423     case Gurmukhi:
424     case Kannada:
425     case Khmer:
426     case Lao:
427     case LowerHexadecimal:
428     case Malayalam:
429     case Mongolian:
430     case Myanmar:
431     case NoneListStyle:
432     case Octal:
433     case Oriya:
434     case Persian:
435     case Square:
436     case Telugu:
437     case Thai:
438     case Tibetan:
439     case UpperHexadecimal:
440     case Urdu:
441         return type; // Can represent all ordinals.
442     case Armenian:
443         return (value < 1 || value > 99999999) ? DecimalListStyle : type;
444     case CJKIdeographic:
445         return (value < 0) ? DecimalListStyle : type;
446     case Georgian:
447         return (value < 1 || value > 19999) ? DecimalListStyle : type;
448     case Hebrew:
449         return (value < 0 || value > 999999) ? DecimalListStyle : type;
450     case LowerRoman:
451     case UpperRoman:
452         return (value < 1 || value > 3999) ? DecimalListStyle : type;
453     case Afar:
454     case Amharic:
455     case AmharicAbegede:
456     case Asterisks:
457     case CjkEarthlyBranch:
458     case CjkHeavenlyStem:
459     case Ethiopic:
460     case EthiopicAbegede:
461     case EthiopicAbegedeAmEt:
462     case EthiopicAbegedeGez:
463     case EthiopicAbegedeTiEr:
464     case EthiopicAbegedeTiEt:
465     case EthiopicHalehameAaEr:
466     case EthiopicHalehameAaEt:
467     case EthiopicHalehameAmEt:
468     case EthiopicHalehameGez:
469     case EthiopicHalehameOmEt:
470     case EthiopicHalehameSidEt:
471     case EthiopicHalehameSoEt:
472     case EthiopicHalehameTiEr:
473     case EthiopicHalehameTiEt:
474     case EthiopicHalehameTig:
475     case Footnotes:
476     case Hangul:
477     case HangulConsonant:
478     case Hiragana:
479     case HiraganaIroha:
480     case Katakana:
481     case KatakanaIroha:
482     case LowerAlpha:
483     case LowerArmenian:
484     case LowerGreek:
485     case LowerLatin:
486     case LowerNorwegian:
487     case Oromo:
488     case Sidama:
489     case Somali:
490     case Tigre:
491     case TigrinyaEr:
492     case TigrinyaErAbegede:
493     case TigrinyaEt:
494     case TigrinyaEtAbegede:
495     case UpperAlpha:
496     case UpperArmenian:
497     case UpperGreek:
498     case UpperLatin:
499     case UpperNorwegian:
500         return (value < 1) ? DecimalListStyle : type;
501     }
502
503     ASSERT_NOT_REACHED();
504     return type;
505 }
506
507 static UChar listMarkerSuffix(EListStyleType 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     EListStyleType 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 Asterisks:
517     case Circle:
518     case Disc:
519     case Footnotes:
520     case NoneListStyle:
521     case Square:
522         return ' ';
523     case Afar:
524     case Amharic:
525     case AmharicAbegede:
526     case Ethiopic:
527     case EthiopicAbegede:
528     case EthiopicAbegedeAmEt:
529     case EthiopicAbegedeGez:
530     case EthiopicAbegedeTiEr:
531     case EthiopicAbegedeTiEt:
532     case EthiopicHalehameAaEr:
533     case EthiopicHalehameAaEt:
534     case EthiopicHalehameAmEt:
535     case EthiopicHalehameGez:
536     case EthiopicHalehameOmEt:
537     case EthiopicHalehameSidEt:
538     case EthiopicHalehameSoEt:
539     case EthiopicHalehameTiEr:
540     case EthiopicHalehameTiEt:
541     case EthiopicHalehameTig:
542     case Oromo:
543     case Sidama:
544     case Somali:
545     case Tigre:
546     case TigrinyaEr:
547     case TigrinyaErAbegede:
548     case TigrinyaEt:
549     case TigrinyaEtAbegede:
550         return ethiopicPrefaceColon;
551     case Armenian:
552     case ArabicIndic:
553     case Bengali:
554     case BinaryListStyle:
555     case Cambodian:
556     case CJKIdeographic:
557     case CjkEarthlyBranch:
558     case CjkHeavenlyStem:
559     case DecimalLeadingZero:
560     case DecimalListStyle:
561     case Devanagari:
562     case Georgian:
563     case Gujarati:
564     case Gurmukhi:
565     case Hangul:
566     case HangulConsonant:
567     case Hebrew:
568     case Hiragana:
569     case HiraganaIroha:
570     case Kannada:
571     case Katakana:
572     case KatakanaIroha:
573     case Khmer:
574     case Lao:
575     case LowerAlpha:
576     case LowerArmenian:
577     case LowerGreek:
578     case LowerHexadecimal:
579     case LowerLatin:
580     case LowerNorwegian:
581     case LowerRoman:
582     case Malayalam:
583     case Mongolian:
584     case Myanmar:
585     case Octal:
586     case Oriya:
587     case Persian:
588     case Telugu:
589     case Thai:
590     case Tibetan:
591     case UpperAlpha:
592     case UpperArmenian:
593     case UpperGreek:
594     case UpperHexadecimal:
595     case UpperLatin:
596     case UpperNorwegian:
597     case UpperRoman:
598     case Urdu:
599         return '.';
600     }
601
602     ASSERT_NOT_REACHED();
603     return '.';
604 }
605
606 String listMarkerText(EListStyleType type, int value)
607 {
608     // If the list-style-type, say hebrew, cannot represent |value| because it's outside
609     // its ordinal range then we fallback to some list style that can represent |value|.
610     switch (effectiveListMarkerType(type, value)) {
611         case NoneListStyle:
612             return "";
613
614         case Asterisks: {
615             static const LChar asterisksSymbols[1] = {
616                 0x2A
617             };
618             return toSymbolic(value, asterisksSymbols);
619         }
620         // We use the same characters for text security.
621         // See RenderText::setInternalString.
622         case Circle:
623             return String(&whiteBullet, 1);
624         case Disc:
625             return String(&bullet, 1);
626         case Footnotes: {
627             static const UChar footnotesSymbols[4] = {
628                 0x002A, 0x2051, 0x2020, 0x2021
629             };
630             return toSymbolic(value, footnotesSymbols);
631         }
632         case Square:
633             // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
634             // instead, but I think this looks better.
635             return String(&blackSquare, 1);
636
637         case DecimalListStyle:
638             return String::number(value);
639         case DecimalLeadingZero:
640             if (value < -9 || value > 9)
641                 return String::number(value);
642             if (value < 0)
643                 return "-0" + String::number(-value); // -01 to -09
644             return "0" + String::number(value); // 00 to 09
645
646         case ArabicIndic: {
647             static const UChar arabicIndicNumerals[10] = {
648                 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669
649             };
650             return toNumeric(value, arabicIndicNumerals);
651         }
652         case BinaryListStyle: {
653             static const LChar binaryNumerals[2] = {
654                 '0', '1'
655             };
656             return toNumeric(value, binaryNumerals);
657         }
658         case Bengali: {
659             static const UChar bengaliNumerals[10] = {
660                 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF
661             };
662             return toNumeric(value, bengaliNumerals);
663         }
664         case Cambodian:
665         case 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 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 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 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 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 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 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 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 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 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 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 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 Persian:
738         case 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 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 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 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 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 LowerAlpha:
770         case 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 UpperAlpha:
778         case 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 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 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 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 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 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 Afar:
848         case EthiopicHalehameAaEt:
849         case 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 Amharic:
857         case 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 AmharicAbegede:
867         case 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 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 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 Ethiopic:
891         case 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 EthiopicAbegede:
900         case 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 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 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 Oromo:
923         case 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 Sidama:
932         case 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 Somali:
941         case 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 Tigre:
950         case 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 TigrinyaEr:
959         case 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 TigrinyaErAbegede:
969         case 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 TigrinyaEt:
979         case 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 TigrinyaEtAbegede:
989         case 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 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 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 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 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 LowerRoman:
1035             return toRoman(value, false);
1036         case UpperRoman:
1037             return toRoman(value, true);
1038
1039         case Armenian:
1040         case UpperArmenian:
1041             // CSS3 says "armenian" means "lower-armenian".
1042             // But the CSS2.1 test suite contains uppercase test results for "armenian",
1043             // so we'll match the test suite.
1044             return toArmenian(value, true);
1045         case LowerArmenian:
1046             return toArmenian(value, false);
1047         case Georgian:
1048             return toGeorgian(value);
1049         case Hebrew:
1050             return toHebrew(value);
1051     }
1052
1053     ASSERT_NOT_REACHED();
1054     return "";
1055 }
1056
1057 RenderListMarker::RenderListMarker(RenderListItem* item)
1058     : RenderBox(0)
1059     , m_listItem(item)
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     if (m_image)
1069         m_image->removeClient(this);
1070 }
1071
1072 RenderListMarker* RenderListMarker::createAnonymous(RenderListItem* item)
1073 {
1074     Document& document = item->document();
1075     RenderListMarker* renderer = new RenderListMarker(item);
1076     renderer->setDocumentForAnonymous(&document);
1077     return renderer;
1078 }
1079
1080 void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
1081 {
1082     if (style() && (newStyle.listStylePosition() != style()->listStylePosition() || newStyle.listStyleType() != style()->listStyleType()))
1083         setNeedsLayoutAndPrefWidthsRecalc();
1084
1085     RenderBox::styleWillChange(diff, newStyle);
1086 }
1087
1088 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
1089 {
1090     RenderBox::styleDidChange(diff, oldStyle);
1091
1092     if (m_image != style()->listStyleImage()) {
1093         if (m_image)
1094             m_image->removeClient(this);
1095         m_image = style()->listStyleImage();
1096         if (m_image)
1097             m_image->addClient(this);
1098     }
1099 }
1100
1101 InlineBox* RenderListMarker::createInlineBox()
1102 {
1103     InlineBox* result = RenderBox::createInlineBox();
1104     result->setIsText(isText());
1105     return result;
1106 }
1107
1108 bool RenderListMarker::isImage() const
1109 {
1110     return m_image && !m_image->errorOccurred();
1111 }
1112
1113 LayoutRect RenderListMarker::localSelectionRect()
1114 {
1115     InlineBox* box = inlineBoxWrapper();
1116     if (!box)
1117         return LayoutRect(LayoutPoint(), size());
1118     RootInlineBox& root = inlineBoxWrapper()->root();
1119     LayoutUnit newLogicalTop = root.block().style()->isFlippedBlocksWritingMode() ? inlineBoxWrapper()->logicalBottom() - root.selectionBottom() : root.selectionTop() - inlineBoxWrapper()->logicalTop();
1120     if (root.block().style()->isHorizontalWritingMode())
1121         return LayoutRect(0, newLogicalTop, width(), root.selectionHeight());
1122     return LayoutRect(newLogicalTop, 0, root.selectionHeight(), height());
1123 }
1124
1125 void RenderListMarker::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1126 {
1127     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
1128
1129     if (paintInfo.phase != PaintPhaseForeground)
1130         return;
1131
1132     if (style()->visibility() != VISIBLE)
1133         return;
1134
1135     LayoutPoint boxOrigin(paintOffset + location());
1136     LayoutRect overflowRect(visualOverflowRect());
1137     overflowRect.moveBy(boxOrigin);
1138
1139     if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
1140         return;
1141
1142     LayoutRect box(boxOrigin, size());
1143
1144     IntRect marker = getRelativeMarkerRect();
1145     marker.moveBy(roundedIntPoint(boxOrigin));
1146
1147     GraphicsContext* context = paintInfo.context;
1148
1149     if (isImage()) {
1150         context->drawImage(m_image->image(this, marker.size()).get(), marker);
1151         if (selectionState() != SelectionNone) {
1152             LayoutRect selRect = localSelectionRect();
1153             selRect.moveBy(boxOrigin);
1154             context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor());
1155         }
1156         return;
1157     }
1158
1159     if (selectionState() != SelectionNone) {
1160         LayoutRect selRect = localSelectionRect();
1161         selRect.moveBy(boxOrigin);
1162         context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor());
1163     }
1164
1165     const Color color(resolveColor(CSSPropertyColor));
1166     context->setStrokeColor(color);
1167     context->setStrokeStyle(SolidStroke);
1168     context->setStrokeThickness(1.0f);
1169     context->setFillColor(color);
1170
1171     EListStyleType type = style()->listStyleType();
1172     switch (type) {
1173         case Disc:
1174             context->fillEllipse(marker);
1175             return;
1176         case Circle:
1177             context->strokeEllipse(marker);
1178             return;
1179         case Square:
1180             context->fillRect(marker);
1181             return;
1182         case NoneListStyle:
1183             return;
1184         case Afar:
1185         case Amharic:
1186         case AmharicAbegede:
1187         case ArabicIndic:
1188         case Armenian:
1189         case BinaryListStyle:
1190         case Bengali:
1191         case Cambodian:
1192         case CJKIdeographic:
1193         case CjkEarthlyBranch:
1194         case CjkHeavenlyStem:
1195         case DecimalLeadingZero:
1196         case DecimalListStyle:
1197         case Devanagari:
1198         case Ethiopic:
1199         case EthiopicAbegede:
1200         case EthiopicAbegedeAmEt:
1201         case EthiopicAbegedeGez:
1202         case EthiopicAbegedeTiEr:
1203         case EthiopicAbegedeTiEt:
1204         case EthiopicHalehameAaEr:
1205         case EthiopicHalehameAaEt:
1206         case EthiopicHalehameAmEt:
1207         case EthiopicHalehameGez:
1208         case EthiopicHalehameOmEt:
1209         case EthiopicHalehameSidEt:
1210         case EthiopicHalehameSoEt:
1211         case EthiopicHalehameTiEr:
1212         case EthiopicHalehameTiEt:
1213         case EthiopicHalehameTig:
1214         case Georgian:
1215         case Gujarati:
1216         case Gurmukhi:
1217         case Hangul:
1218         case HangulConsonant:
1219         case Hebrew:
1220         case Hiragana:
1221         case HiraganaIroha:
1222         case Kannada:
1223         case Katakana:
1224         case KatakanaIroha:
1225         case Khmer:
1226         case Lao:
1227         case LowerAlpha:
1228         case LowerArmenian:
1229         case LowerGreek:
1230         case LowerHexadecimal:
1231         case LowerLatin:
1232         case LowerNorwegian:
1233         case LowerRoman:
1234         case Malayalam:
1235         case Mongolian:
1236         case Myanmar:
1237         case Octal:
1238         case Oriya:
1239         case Oromo:
1240         case Persian:
1241         case Sidama:
1242         case Somali:
1243         case Telugu:
1244         case Thai:
1245         case Tibetan:
1246         case Tigre:
1247         case TigrinyaEr:
1248         case TigrinyaErAbegede:
1249         case TigrinyaEt:
1250         case TigrinyaEtAbegede:
1251         case UpperAlpha:
1252         case UpperArmenian:
1253         case UpperGreek:
1254         case UpperHexadecimal:
1255         case UpperLatin:
1256         case UpperNorwegian:
1257         case UpperRoman:
1258         case Urdu:
1259         case Asterisks:
1260         case Footnotes:
1261             break;
1262     }
1263     if (m_text.isEmpty())
1264         return;
1265
1266     const Font& font = style()->font();
1267     TextRun textRun = RenderBlockFlow::constructTextRun(this, font, m_text, style());
1268
1269     GraphicsContextStateSaver stateSaver(*context, false);
1270     if (!style()->isHorizontalWritingMode()) {
1271         marker.moveBy(roundedIntPoint(-boxOrigin));
1272         marker = marker.transposedRect();
1273         marker.moveBy(IntPoint(roundToInt(box.x()), roundToInt(box.y() - logicalHeight())));
1274         stateSaver.save();
1275         context->translate(marker.x(), marker.maxY());
1276         context->rotate(static_cast<float>(deg2rad(90.)));
1277         context->translate(-marker.x(), -marker.maxY());
1278     }
1279
1280     TextRunPaintInfo textRunPaintInfo(textRun);
1281     textRunPaintInfo.bounds = marker;
1282     IntPoint textOrigin = IntPoint(marker.x(), marker.y() + style()->fontMetrics().ascent());
1283
1284     if (type == Asterisks || type == Footnotes) {
1285         context->drawText(font, textRunPaintInfo, textOrigin);
1286     }
1287     else {
1288         // Text is not arbitrary. We can judge whether it's RTL from the first character,
1289         // and we only need to handle the direction RightToLeft for now.
1290         bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
1291         StringBuilder reversedText;
1292         if (textNeedsReversing) {
1293             int length = m_text.length();
1294             reversedText.reserveCapacity(length);
1295             for (int i = length - 1; i >= 0; --i)
1296                 reversedText.append(m_text[i]);
1297             ASSERT(reversedText.length() == reversedText.capacity());
1298             textRun.setText(reversedText.toString());
1299         }
1300
1301         const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1302         UChar suffixStr[2] = {
1303             style()->isLeftToRightDirection() ? suffix : ' ',
1304             style()->isLeftToRightDirection() ? ' ' : suffix
1305         };
1306         TextRun suffixRun = RenderBlockFlow::constructTextRun(this, font, suffixStr, 2, style(), style()->direction());
1307         TextRunPaintInfo suffixRunInfo(suffixRun);
1308         suffixRunInfo.bounds = marker;
1309
1310         if (style()->isLeftToRightDirection()) {
1311             context->drawText(font, textRunPaintInfo, textOrigin);
1312             context->drawText(font, suffixRunInfo, textOrigin + IntSize(font.width(textRun), 0));
1313         } else {
1314             context->drawText(font, suffixRunInfo, textOrigin);
1315             context->drawText(font, textRunPaintInfo, textOrigin + IntSize(font.width(suffixRun), 0));
1316         }
1317     }
1318 }
1319
1320 void RenderListMarker::layout()
1321 {
1322     ASSERT(needsLayout());
1323
1324     if (isImage()) {
1325         updateMarginsAndContent();
1326         setWidth(m_image->imageSize(this, style()->effectiveZoom()).width());
1327         setHeight(m_image->imageSize(this, style()->effectiveZoom()).height());
1328     } else {
1329         setLogicalWidth(minPreferredLogicalWidth());
1330         setLogicalHeight(style()->fontMetrics().height());
1331     }
1332
1333     setMarginStart(0);
1334     setMarginEnd(0);
1335
1336     Length startMargin = style()->marginStart();
1337     Length endMargin = style()->marginEnd();
1338     if (startMargin.isFixed())
1339         setMarginStart(startMargin.value());
1340     if (endMargin.isFixed())
1341         setMarginEnd(endMargin.value());
1342
1343     clearNeedsLayout();
1344 }
1345
1346 void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*)
1347 {
1348     // A list marker can't have a background or border image, so no need to call the base class method.
1349     if (o != m_image->data())
1350         return;
1351
1352     if (width() != m_image->imageSize(this, style()->effectiveZoom()).width() || height() != m_image->imageSize(this, style()->effectiveZoom()).height() || m_image->errorOccurred())
1353         setNeedsLayoutAndPrefWidthsRecalc();
1354     else
1355         repaint();
1356 }
1357
1358 void RenderListMarker::updateMarginsAndContent()
1359 {
1360     updateContent();
1361     updateMargins();
1362 }
1363
1364 void RenderListMarker::updateContent()
1365 {
1366     // FIXME: This if-statement is just a performance optimization, but it's messy to use the preferredLogicalWidths dirty bit for this.
1367     // It's unclear if this is a premature optimization.
1368     if (!preferredLogicalWidthsDirty())
1369         return;
1370
1371     m_text = "";
1372
1373     if (isImage()) {
1374         // FIXME: This is a somewhat arbitrary width.  Generated images for markers really won't become particularly useful
1375         // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box.
1376         int bulletWidth = style()->fontMetrics().ascent() / 2;
1377         IntSize defaultBulletSize(bulletWidth, bulletWidth);
1378         IntSize imageSize = calculateImageIntrinsicDimensions(m_image.get(), defaultBulletSize, DoNotScaleByEffectiveZoom);
1379         m_image->setContainerSizeForRenderer(this, imageSize, style()->effectiveZoom());
1380         return;
1381     }
1382
1383     EListStyleType type = style()->listStyleType();
1384     switch (type) {
1385     case NoneListStyle:
1386         break;
1387     case Circle:
1388     case Disc:
1389     case Square:
1390         m_text = listMarkerText(type, 0); // value is ignored for these types
1391         break;
1392     case Asterisks:
1393     case Footnotes:
1394     case Afar:
1395     case Amharic:
1396     case AmharicAbegede:
1397     case ArabicIndic:
1398     case Armenian:
1399     case BinaryListStyle:
1400     case Bengali:
1401     case Cambodian:
1402     case CJKIdeographic:
1403     case CjkEarthlyBranch:
1404     case CjkHeavenlyStem:
1405     case DecimalLeadingZero:
1406     case DecimalListStyle:
1407     case Devanagari:
1408     case Ethiopic:
1409     case EthiopicAbegede:
1410     case EthiopicAbegedeAmEt:
1411     case EthiopicAbegedeGez:
1412     case EthiopicAbegedeTiEr:
1413     case EthiopicAbegedeTiEt:
1414     case EthiopicHalehameAaEr:
1415     case EthiopicHalehameAaEt:
1416     case EthiopicHalehameAmEt:
1417     case EthiopicHalehameGez:
1418     case EthiopicHalehameOmEt:
1419     case EthiopicHalehameSidEt:
1420     case EthiopicHalehameSoEt:
1421     case EthiopicHalehameTiEr:
1422     case EthiopicHalehameTiEt:
1423     case EthiopicHalehameTig:
1424     case Georgian:
1425     case Gujarati:
1426     case Gurmukhi:
1427     case Hangul:
1428     case HangulConsonant:
1429     case Hebrew:
1430     case Hiragana:
1431     case HiraganaIroha:
1432     case Kannada:
1433     case Katakana:
1434     case KatakanaIroha:
1435     case Khmer:
1436     case Lao:
1437     case LowerAlpha:
1438     case LowerArmenian:
1439     case LowerGreek:
1440     case LowerHexadecimal:
1441     case LowerLatin:
1442     case LowerNorwegian:
1443     case LowerRoman:
1444     case Malayalam:
1445     case Mongolian:
1446     case Myanmar:
1447     case Octal:
1448     case Oriya:
1449     case Oromo:
1450     case Persian:
1451     case Sidama:
1452     case Somali:
1453     case Telugu:
1454     case Thai:
1455     case Tibetan:
1456     case Tigre:
1457     case TigrinyaEr:
1458     case TigrinyaErAbegede:
1459     case TigrinyaEt:
1460     case TigrinyaEtAbegede:
1461     case UpperAlpha:
1462     case UpperArmenian:
1463     case UpperGreek:
1464     case UpperHexadecimal:
1465     case UpperLatin:
1466     case UpperNorwegian:
1467     case UpperRoman:
1468     case Urdu:
1469         m_text = listMarkerText(type, m_listItem->value());
1470         break;
1471     }
1472 }
1473
1474 void RenderListMarker::computePreferredLogicalWidths()
1475 {
1476     ASSERT(preferredLogicalWidthsDirty());
1477     updateContent();
1478
1479     if (isImage()) {
1480         LayoutSize imageSize = m_image->imageSize(this, style()->effectiveZoom());
1481         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = style()->isHorizontalWritingMode() ? imageSize.width() : imageSize.height();
1482         clearPreferredLogicalWidthsDirty();
1483         updateMargins();
1484         return;
1485     }
1486
1487     const Font& font = style()->font();
1488
1489     LayoutUnit logicalWidth = 0;
1490     EListStyleType type = style()->listStyleType();
1491     switch (type) {
1492         case NoneListStyle:
1493             break;
1494         case Asterisks:
1495         case Footnotes:
1496             logicalWidth = font.width(m_text); // no suffix for these types
1497             break;
1498         case Circle:
1499         case Disc:
1500         case Square:
1501             logicalWidth = (font.fontMetrics().ascent() * 2 / 3 + 1) / 2 + 2;
1502             break;
1503         case Afar:
1504         case Amharic:
1505         case AmharicAbegede:
1506         case ArabicIndic:
1507         case Armenian:
1508         case BinaryListStyle:
1509         case Bengali:
1510         case Cambodian:
1511         case CJKIdeographic:
1512         case CjkEarthlyBranch:
1513         case CjkHeavenlyStem:
1514         case DecimalLeadingZero:
1515         case DecimalListStyle:
1516         case Devanagari:
1517         case Ethiopic:
1518         case EthiopicAbegede:
1519         case EthiopicAbegedeAmEt:
1520         case EthiopicAbegedeGez:
1521         case EthiopicAbegedeTiEr:
1522         case EthiopicAbegedeTiEt:
1523         case EthiopicHalehameAaEr:
1524         case EthiopicHalehameAaEt:
1525         case EthiopicHalehameAmEt:
1526         case EthiopicHalehameGez:
1527         case EthiopicHalehameOmEt:
1528         case EthiopicHalehameSidEt:
1529         case EthiopicHalehameSoEt:
1530         case EthiopicHalehameTiEr:
1531         case EthiopicHalehameTiEt:
1532         case EthiopicHalehameTig:
1533         case Georgian:
1534         case Gujarati:
1535         case Gurmukhi:
1536         case Hangul:
1537         case HangulConsonant:
1538         case Hebrew:
1539         case Hiragana:
1540         case HiraganaIroha:
1541         case Kannada:
1542         case Katakana:
1543         case KatakanaIroha:
1544         case Khmer:
1545         case Lao:
1546         case LowerAlpha:
1547         case LowerArmenian:
1548         case LowerGreek:
1549         case LowerHexadecimal:
1550         case LowerLatin:
1551         case LowerNorwegian:
1552         case LowerRoman:
1553         case Malayalam:
1554         case Mongolian:
1555         case Myanmar:
1556         case Octal:
1557         case Oriya:
1558         case Oromo:
1559         case Persian:
1560         case Sidama:
1561         case Somali:
1562         case Telugu:
1563         case Thai:
1564         case Tibetan:
1565         case Tigre:
1566         case TigrinyaEr:
1567         case TigrinyaErAbegede:
1568         case TigrinyaEt:
1569         case TigrinyaEtAbegede:
1570         case UpperAlpha:
1571         case UpperArmenian:
1572         case UpperGreek:
1573         case UpperHexadecimal:
1574         case UpperLatin:
1575         case UpperNorwegian:
1576         case UpperRoman:
1577         case Urdu:
1578             if (m_text.isEmpty())
1579                 logicalWidth = 0;
1580             else {
1581                 LayoutUnit itemWidth = font.width(m_text);
1582                 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
1583                 LayoutUnit suffixSpaceWidth = font.width(RenderBlockFlow::constructTextRun(this, font, suffixSpace, 2, style(), style()->direction()));
1584                 logicalWidth = itemWidth + suffixSpaceWidth;
1585             }
1586             break;
1587     }
1588
1589     m_minPreferredLogicalWidth = logicalWidth;
1590     m_maxPreferredLogicalWidth = logicalWidth;
1591
1592     clearPreferredLogicalWidthsDirty();
1593
1594     updateMargins();
1595 }
1596
1597 void RenderListMarker::updateMargins()
1598 {
1599     const FontMetrics& fontMetrics = style()->fontMetrics();
1600
1601     LayoutUnit marginStart = 0;
1602     LayoutUnit marginEnd = 0;
1603
1604     if (isInside()) {
1605         if (isImage())
1606             marginEnd = cMarkerPadding;
1607         else switch (style()->listStyleType()) {
1608             case Disc:
1609             case Circle:
1610             case Square:
1611                 marginStart = -1;
1612                 marginEnd = fontMetrics.ascent() - minPreferredLogicalWidth() + 1;
1613                 break;
1614             default:
1615                 break;
1616         }
1617     } else {
1618         if (style()->isLeftToRightDirection()) {
1619             if (isImage())
1620                 marginStart = -minPreferredLogicalWidth() - cMarkerPadding;
1621             else {
1622                 int offset = fontMetrics.ascent() * 2 / 3;
1623                 switch (style()->listStyleType()) {
1624                     case Disc:
1625                     case Circle:
1626                     case Square:
1627                         marginStart = -offset - cMarkerPadding - 1;
1628                         break;
1629                     case NoneListStyle:
1630                         break;
1631                     default:
1632                         marginStart = m_text.isEmpty() ? LayoutUnit() : -minPreferredLogicalWidth() - offset / 2;
1633                 }
1634             }
1635             marginEnd = -marginStart - minPreferredLogicalWidth();
1636         } else {
1637             if (isImage())
1638                 marginEnd = cMarkerPadding;
1639             else {
1640                 int offset = fontMetrics.ascent() * 2 / 3;
1641                 switch (style()->listStyleType()) {
1642                     case Disc:
1643                     case Circle:
1644                     case Square:
1645                         marginEnd = offset + cMarkerPadding + 1 - minPreferredLogicalWidth();
1646                         break;
1647                     case NoneListStyle:
1648                         break;
1649                     default:
1650                         marginEnd = m_text.isEmpty() ? 0 : offset / 2;
1651                 }
1652             }
1653             marginStart = -marginEnd - minPreferredLogicalWidth();
1654         }
1655
1656     }
1657
1658     style()->setMarginStart(Length(marginStart, Fixed));
1659     style()->setMarginEnd(Length(marginEnd, Fixed));
1660 }
1661
1662 LayoutUnit RenderListMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1663 {
1664     if (!isImage())
1665         return m_listItem->lineHeight(firstLine, direction, PositionOfInteriorLineBoxes);
1666     return RenderBox::lineHeight(firstLine, direction, linePositionMode);
1667 }
1668
1669 int RenderListMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1670 {
1671     ASSERT(linePositionMode == PositionOnContainingLine);
1672     if (!isImage())
1673         return m_listItem->baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes);
1674     return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1675 }
1676
1677 String RenderListMarker::suffix() const
1678 {
1679     EListStyleType type = style()->listStyleType();
1680     const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1681
1682     if (suffix == ' ')
1683         return String(" ");
1684
1685     // If the suffix is not ' ', an extra space is needed
1686     UChar data[2];
1687     if (style()->isLeftToRightDirection()) {
1688         data[0] = suffix;
1689         data[1] = ' ';
1690     } else {
1691         data[0] = ' ';
1692         data[1] = suffix;
1693     }
1694
1695     return String(data, 2);
1696 }
1697
1698 bool RenderListMarker::isInside() const
1699 {
1700     return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
1701 }
1702
1703 IntRect RenderListMarker::getRelativeMarkerRect()
1704 {
1705     if (isImage())
1706         return IntRect(0, 0, m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height());
1707
1708     IntRect relativeRect;
1709     EListStyleType type = style()->listStyleType();
1710     switch (type) {
1711         case Asterisks:
1712         case Footnotes: {
1713             const Font& font = style()->font();
1714             relativeRect = IntRect(0, 0, font.width(m_text), font.fontMetrics().height());
1715             break;
1716         }
1717         case Disc:
1718         case Circle:
1719         case Square: {
1720             // FIXME: Are these particular rounding rules necessary?
1721             const FontMetrics& fontMetrics = style()->fontMetrics();
1722             int ascent = fontMetrics.ascent();
1723             int bulletWidth = (ascent * 2 / 3 + 1) / 2;
1724             relativeRect = IntRect(1, 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
1725             break;
1726         }
1727         case NoneListStyle:
1728             return IntRect();
1729         case Afar:
1730         case Amharic:
1731         case AmharicAbegede:
1732         case ArabicIndic:
1733         case Armenian:
1734         case BinaryListStyle:
1735         case Bengali:
1736         case Cambodian:
1737         case CJKIdeographic:
1738         case CjkEarthlyBranch:
1739         case CjkHeavenlyStem:
1740         case DecimalLeadingZero:
1741         case DecimalListStyle:
1742         case Devanagari:
1743         case Ethiopic:
1744         case EthiopicAbegede:
1745         case EthiopicAbegedeAmEt:
1746         case EthiopicAbegedeGez:
1747         case EthiopicAbegedeTiEr:
1748         case EthiopicAbegedeTiEt:
1749         case EthiopicHalehameAaEr:
1750         case EthiopicHalehameAaEt:
1751         case EthiopicHalehameAmEt:
1752         case EthiopicHalehameGez:
1753         case EthiopicHalehameOmEt:
1754         case EthiopicHalehameSidEt:
1755         case EthiopicHalehameSoEt:
1756         case EthiopicHalehameTiEr:
1757         case EthiopicHalehameTiEt:
1758         case EthiopicHalehameTig:
1759         case Georgian:
1760         case Gujarati:
1761         case Gurmukhi:
1762         case Hangul:
1763         case HangulConsonant:
1764         case Hebrew:
1765         case Hiragana:
1766         case HiraganaIroha:
1767         case Kannada:
1768         case Katakana:
1769         case KatakanaIroha:
1770         case Khmer:
1771         case Lao:
1772         case LowerAlpha:
1773         case LowerArmenian:
1774         case LowerGreek:
1775         case LowerHexadecimal:
1776         case LowerLatin:
1777         case LowerNorwegian:
1778         case LowerRoman:
1779         case Malayalam:
1780         case Mongolian:
1781         case Myanmar:
1782         case Octal:
1783         case Oriya:
1784         case Oromo:
1785         case Persian:
1786         case Sidama:
1787         case Somali:
1788         case Telugu:
1789         case Thai:
1790         case Tibetan:
1791         case Tigre:
1792         case TigrinyaEr:
1793         case TigrinyaErAbegede:
1794         case TigrinyaEt:
1795         case TigrinyaEtAbegede:
1796         case UpperAlpha:
1797         case UpperArmenian:
1798         case UpperGreek:
1799         case UpperHexadecimal:
1800         case UpperLatin:
1801         case UpperNorwegian:
1802         case UpperRoman:
1803         case Urdu:
1804             if (m_text.isEmpty())
1805                 return IntRect();
1806             const Font& font = style()->font();
1807             int itemWidth = font.width(m_text);
1808             UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
1809             int suffixSpaceWidth = font.width(RenderBlockFlow::constructTextRun(this, font, suffixSpace, 2, style(), style()->direction()));
1810             relativeRect = IntRect(0, 0, itemWidth + suffixSpaceWidth, font.fontMetrics().height());
1811     }
1812
1813     if (!style()->isHorizontalWritingMode()) {
1814         relativeRect = relativeRect.transposedRect();
1815         relativeRect.setX(width() - relativeRect.x() - relativeRect.width());
1816     }
1817
1818     return relativeRect;
1819 }
1820
1821 void RenderListMarker::setSelectionState(SelectionState state)
1822 {
1823     // The selection state for our containing block hierarchy is updated by the base class call.
1824     RenderBox::setSelectionState(state);
1825
1826     if (inlineBoxWrapper() && canUpdateSelectionOnRootLineBoxes())
1827         inlineBoxWrapper()->root().setHasSelectedChildren(state != SelectionNone);
1828 }
1829
1830 LayoutRect RenderListMarker::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent)
1831 {
1832     ASSERT(!needsLayout());
1833
1834     if (selectionState() == SelectionNone || !inlineBoxWrapper())
1835         return LayoutRect();
1836
1837     RootInlineBox& root = inlineBoxWrapper()->root();
1838     LayoutRect rect(0, root.selectionTop() - y(), width(), root.selectionHeight());
1839
1840     if (clipToVisibleContent)
1841         computeRectForRepaint(repaintContainer, rect);
1842     else
1843         rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
1844
1845     return rect;
1846 }
1847
1848 } // namespace WebCore