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