Upstream version 5.34.104.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/LayoutRectRecorder.h"
32 #include "core/rendering/RenderLayer.h"
33 #include "core/rendering/RenderListItem.h"
34 #include "core/rendering/RenderView.h"
35 #include "platform/fonts/Font.h"
36 #include "platform/graphics/GraphicsContextStateSaver.h"
37 #include "wtf/text/StringBuilder.h"
38 #include "wtf/unicode/CharacterNames.h"
39
40 using namespace std;
41 using namespace WTF;
42 using namespace Unicode;
43
44 namespace WebCore {
45
46 const int cMarkerPadding = 7;
47
48 enum SequenceType { NumericSequence, AlphabeticSequence };
49
50 static String toRoman(int number, bool upper)
51 {
52     // FIXME: CSS3 describes how to make this work for much larger numbers,
53     // using overbars and special characters. It also specifies the characters
54     // in the range U+2160 to U+217F instead of standard ASCII ones.
55     ASSERT(number >= 1 && number <= 3999);
56
57     // Big enough to store largest roman number less than 3999 which
58     // is 3888 (MMMDCCCLXXXVIII)
59     const int lettersSize = 15;
60     LChar letters[lettersSize];
61
62     int length = 0;
63     const LChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
64     const LChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
65     const LChar* digits = upper ? udigits : ldigits;
66     int d = 0;
67     do {
68         int num = number % 10;
69         if (num % 5 < 4)
70             for (int i = num % 5; i > 0; i--)
71                 letters[lettersSize - ++length] = digits[d];
72         if (num >= 4 && num <= 8)
73             letters[lettersSize - ++length] = digits[d + 1];
74         if (num == 9)
75             letters[lettersSize - ++length] = digits[d + 2];
76         if (num % 5 == 4)
77             letters[lettersSize - ++length] = digits[d];
78         number /= 10;
79         d += 2;
80     } while (number);
81
82     ASSERT(length <= lettersSize);
83     return String(&letters[lettersSize - length], length);
84 }
85
86 // The typedef is needed because taking sizeof(number) in the const expression below doesn't work with some compilers.
87 // This is likely the case because of the template.
88 typedef int numberType;
89
90 template <typename CharacterType>
91 static inline String toAlphabeticOrNumeric(numberType number, const CharacterType* sequence, unsigned sequenceSize, SequenceType type)
92 {
93     ASSERT(sequenceSize >= 2);
94
95     const int lettersSize = sizeof(numberType) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
96
97     CharacterType letters[lettersSize];
98
99     bool isNegativeNumber = false;
100     unsigned numberShadow = number;
101     if (type == AlphabeticSequence) {
102         ASSERT(number > 0);
103         --numberShadow;
104     } else if (number < 0) {
105         numberShadow = -number;
106         isNegativeNumber = true;
107     }
108     letters[lettersSize - 1] = sequence[numberShadow % sequenceSize];
109     int length = 1;
110
111     if (type == AlphabeticSequence) {
112         while ((numberShadow /= sequenceSize) > 0) {
113             --numberShadow;
114             letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
115         }
116     } else {
117         while ((numberShadow /= sequenceSize) > 0)
118             letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
119     }
120     if (isNegativeNumber)
121         letters[lettersSize - ++length] = hyphenMinus;
122
123     ASSERT(length <= lettersSize);
124     return String(&letters[lettersSize - length], length);
125 }
126
127 template <typename CharacterType>
128 static String toSymbolic(int number, const CharacterType* symbols, unsigned symbolsSize)
129 {
130     ASSERT(number > 0);
131     ASSERT(symbolsSize >= 1);
132     unsigned numberShadow = number;
133     --numberShadow;
134
135     // The asterisks list-style-type is the worst case; we show |numberShadow| asterisks.
136     StringBuilder letters;
137     letters.append(symbols[numberShadow % symbolsSize]);
138     unsigned numSymbols = numberShadow / symbolsSize;
139     while (numSymbols--)
140         letters.append(symbols[numberShadow % symbolsSize]);
141     return letters.toString();
142 }
143
144 template <typename CharacterType>
145 static String toAlphabetic(int number, const CharacterType* alphabet, unsigned alphabetSize)
146 {
147     return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence);
148 }
149
150 template <typename CharacterType>
151 static String toNumeric(int number, const CharacterType* numerals, unsigned numeralsSize)
152 {
153     return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence);
154 }
155
156 template <typename CharacterType, size_t size>
157 static inline String toAlphabetic(int number, const CharacterType(&alphabet)[size])
158 {
159     return toAlphabetic(number, alphabet, size);
160 }
161
162 template <typename CharacterType, size_t size>
163 static inline String toNumeric(int number, const CharacterType(&alphabet)[size])
164 {
165     return toNumeric(number, alphabet, size);
166 }
167
168 template <typename CharacterType, size_t size>
169 static inline String toSymbolic(int number, const CharacterType(&alphabet)[size])
170 {
171     return toSymbolic(number, alphabet, size);
172 }
173
174 static int toHebrewUnder1000(int number, UChar letters[5])
175 {
176     // FIXME: CSS3 mentions various refinements not implemented here.
177     // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
178     ASSERT(number >= 0 && number < 1000);
179     int length = 0;
180     int fourHundreds = number / 400;
181     for (int i = 0; i < fourHundreds; i++)
182         letters[length++] = 1511 + 3;
183     number %= 400;
184     if (number / 100)
185         letters[length++] = 1511 + (number / 100) - 1;
186     number %= 100;
187     if (number == 15 || number == 16) {
188         letters[length++] = 1487 + 9;
189         letters[length++] = 1487 + number - 9;
190     } else {
191         if (int tens = number / 10) {
192             static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
193             letters[length++] = hebrewTens[tens - 1];
194         }
195         if (int ones = number % 10)
196             letters[length++] = 1487 + ones;
197     }
198     ASSERT(length <= 5);
199     return length;
200 }
201
202 static String toHebrew(int number)
203 {
204     // FIXME: CSS3 mentions ways to make this work for much larger numbers.
205     ASSERT(number >= 0 && number <= 999999);
206
207     if (number == 0) {
208         static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
209         return String(hebrewZero, 3);
210     }
211
212     const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
213     UChar letters[lettersSize];
214
215     int length;
216     if (number < 1000)
217         length = 0;
218     else {
219         length = toHebrewUnder1000(number / 1000, letters);
220         letters[length++] = '\'';
221         number = number % 1000;
222     }
223     length += toHebrewUnder1000(number, letters + length);
224
225     ASSERT(length <= lettersSize);
226     return String(letters, length);
227 }
228
229 static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
230 {
231     ASSERT(number >= 0 && number < 10000);
232     int length = 0;
233
234     int lowerOffset = upper ? 0 : 0x0030;
235
236     if (int thousands = number / 1000) {
237         if (thousands == 7) {
238             letters[length++] = 0x0552 + lowerOffset;
239             if (addCircumflex)
240                 letters[length++] = 0x0302;
241         } else {
242             letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
243             if (addCircumflex)
244                 letters[length++] = 0x0302;
245         }
246     }
247
248     if (int hundreds = (number / 100) % 10) {
249         letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
250         if (addCircumflex)
251             letters[length++] = 0x0302;
252     }
253
254     if (int tens = (number / 10) % 10) {
255         letters[length++] = (0x053A - 1 + lowerOffset) + tens;
256         if (addCircumflex)
257             letters[length++] = 0x0302;
258     }
259
260     if (int ones = number % 10) {
261         letters[length++] = (0x531 - 1 + lowerOffset) + ones;
262         if (addCircumflex)
263             letters[length++] = 0x0302;
264     }
265
266     return length;
267 }
268
269 static String toArmenian(int number, bool upper)
270 {
271     ASSERT(number >= 1 && number <= 99999999);
272
273     const int lettersSize = 18; // twice what toArmenianUnder10000 needs
274     UChar letters[lettersSize];
275
276     int length = toArmenianUnder10000(number / 10000, upper, true, letters);
277     length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
278
279     ASSERT(length <= lettersSize);
280     return String(letters, length);
281 }
282
283 static String toGeorgian(int number)
284 {
285     ASSERT(number >= 1 && number <= 19999);
286
287     const int lettersSize = 5;
288     UChar letters[lettersSize];
289
290     int length = 0;
291
292     if (number > 9999)
293         letters[length++] = 0x10F5;
294
295     if (int thousands = (number / 1000) % 10) {
296         static const UChar georgianThousands[9] = {
297             0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
298         };
299         letters[length++] = georgianThousands[thousands - 1];
300     }
301
302     if (int hundreds = (number / 100) % 10) {
303         static const UChar georgianHundreds[9] = {
304             0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
305         };
306         letters[length++] = georgianHundreds[hundreds - 1];
307     }
308
309     if (int tens = (number / 10) % 10) {
310         static const UChar georgianTens[9] = {
311             0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
312         };
313         letters[length++] = georgianTens[tens - 1];
314     }
315
316     if (int ones = number % 10) {
317         static const UChar georgianOnes[9] = {
318             0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
319         };
320         letters[length++] = georgianOnes[ones - 1];
321     }
322
323     ASSERT(length <= lettersSize);
324     return String(letters, length);
325 }
326
327 // The table uses the order from the CSS3 specification:
328 // first 3 group markers, then 3 digit markers, then ten digits.
329 static String toCJKIdeographic(int number, const UChar table[16])
330 {
331     ASSERT(number >= 0);
332
333     enum AbstractCJKChar {
334         noChar,
335         secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
336         secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
337         digit0, digit1, digit2, digit3, digit4,
338         digit5, digit6, digit7, digit8, digit9
339     };
340
341     if (number == 0)
342         return String(&table[digit0 - 1], 1);
343
344     const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker
345     const int bufferLength = 4 * groupLength;
346     AbstractCJKChar buffer[bufferLength] = { noChar };
347
348     for (int i = 0; i < 4; ++i) {
349         int groupValue = number % 10000;
350         number /= 10000;
351
352         // Process least-significant group first, but put it in the buffer last.
353         AbstractCJKChar* group = &buffer[(3 - i) * groupLength];
354
355         if (groupValue && i)
356             group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i);
357
358         // Put in the four digits and digit markers for any non-zero digits.
359         group[6] = static_cast<AbstractCJKChar>(digit0 + (groupValue % 10));
360         if (number != 0 || groupValue > 9) {
361             int digitValue = ((groupValue / 10) % 10);
362             group[4] = static_cast<AbstractCJKChar>(digit0 + digitValue);
363             if (digitValue)
364                 group[5] = secondDigitMarker;
365         }
366         if (number != 0 || groupValue > 99) {
367             int digitValue = ((groupValue / 100) % 10);
368             group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue);
369             if (digitValue)
370                 group[3] = thirdDigitMarker;
371         }
372         if (number != 0 || groupValue > 999) {
373             int digitValue = groupValue / 1000;
374             group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue);
375             if (digitValue)
376                 group[1] = fourthDigitMarker;
377         }
378
379         // Remove the tens digit, but leave the marker, for any group that has
380         // a value of less than 20.
381         if (groupValue < 20) {
382             ASSERT(group[4] == noChar || group[4] == digit0 || group[4] == digit1);
383             group[4] = noChar;
384         }
385
386         if (number == 0)
387             break;
388     }
389
390     // Convert into characters, omitting consecutive runs of digit0 and
391     // any trailing digit0.
392     int length = 0;
393     UChar characters[bufferLength];
394     AbstractCJKChar last = noChar;
395     for (int i = 0; i < bufferLength; ++i) {
396         AbstractCJKChar a = buffer[i];
397         if (a != noChar) {
398             if (a != digit0 || last != digit0)
399                 characters[length++] = table[a - 1];
400             last = a;
401         }
402     }
403     if (last == digit0)
404         --length;
405
406     return String(characters, length);
407 }
408
409 static EListStyleType effectiveListMarkerType(EListStyleType type, int value)
410 {
411     // Note, the following switch statement has been explicitly grouped
412     // by list-style-type ordinal range.
413     switch (type) {
414     case ArabicIndic:
415     case Bengali:
416     case BinaryListStyle:
417     case Cambodian:
418     case Circle:
419     case DecimalLeadingZero:
420     case DecimalListStyle:
421     case Devanagari:
422     case Disc:
423     case Gujarati:
424     case Gurmukhi:
425     case Kannada:
426     case Khmer:
427     case Lao:
428     case LowerHexadecimal:
429     case Malayalam:
430     case Mongolian:
431     case Myanmar:
432     case NoneListStyle:
433     case Octal:
434     case Oriya:
435     case Persian:
436     case Square:
437     case Telugu:
438     case Thai:
439     case Tibetan:
440     case UpperHexadecimal:
441     case Urdu:
442         return type; // Can represent all ordinals.
443     case Armenian:
444         return (value < 1 || value > 99999999) ? DecimalListStyle : type;
445     case CJKIdeographic:
446         return (value < 0) ? DecimalListStyle : type;
447     case Georgian:
448         return (value < 1 || value > 19999) ? DecimalListStyle : type;
449     case Hebrew:
450         return (value < 0 || value > 999999) ? DecimalListStyle : type;
451     case LowerRoman:
452     case UpperRoman:
453         return (value < 1 || value > 3999) ? DecimalListStyle : type;
454     case Afar:
455     case Amharic:
456     case AmharicAbegede:
457     case Asterisks:
458     case CjkEarthlyBranch:
459     case CjkHeavenlyStem:
460     case Ethiopic:
461     case EthiopicAbegede:
462     case EthiopicAbegedeAmEt:
463     case EthiopicAbegedeGez:
464     case EthiopicAbegedeTiEr:
465     case EthiopicAbegedeTiEt:
466     case EthiopicHalehameAaEr:
467     case EthiopicHalehameAaEt:
468     case EthiopicHalehameAmEt:
469     case EthiopicHalehameGez:
470     case EthiopicHalehameOmEt:
471     case EthiopicHalehameSidEt:
472     case EthiopicHalehameSoEt:
473     case EthiopicHalehameTiEr:
474     case EthiopicHalehameTiEt:
475     case EthiopicHalehameTig:
476     case Footnotes:
477     case Hangul:
478     case HangulConsonant:
479     case Hiragana:
480     case HiraganaIroha:
481     case Katakana:
482     case KatakanaIroha:
483     case LowerAlpha:
484     case LowerArmenian:
485     case LowerGreek:
486     case LowerLatin:
487     case LowerNorwegian:
488     case Oromo:
489     case Sidama:
490     case Somali:
491     case Tigre:
492     case TigrinyaEr:
493     case TigrinyaErAbegede:
494     case TigrinyaEt:
495     case TigrinyaEtAbegede:
496     case UpperAlpha:
497     case UpperArmenian:
498     case UpperGreek:
499     case UpperLatin:
500     case UpperNorwegian:
501         return (value < 1) ? DecimalListStyle : type;
502     }
503
504     ASSERT_NOT_REACHED();
505     return type;
506 }
507
508 static UChar listMarkerSuffix(EListStyleType type, int value)
509 {
510     // If the list-style-type cannot represent |value| because it's outside its
511     // ordinal range then we fall back to some list style that can represent |value|.
512     EListStyleType effectiveType = effectiveListMarkerType(type, value);
513
514     // Note, the following switch statement has been explicitly
515     // grouped by list-style-type suffix.
516     switch (effectiveType) {
517     case Asterisks:
518     case Circle:
519     case Disc:
520     case Footnotes:
521     case NoneListStyle:
522     case Square:
523         return ' ';
524     case Afar:
525     case Amharic:
526     case AmharicAbegede:
527     case Ethiopic:
528     case EthiopicAbegede:
529     case EthiopicAbegedeAmEt:
530     case EthiopicAbegedeGez:
531     case EthiopicAbegedeTiEr:
532     case EthiopicAbegedeTiEt:
533     case EthiopicHalehameAaEr:
534     case EthiopicHalehameAaEt:
535     case EthiopicHalehameAmEt:
536     case EthiopicHalehameGez:
537     case EthiopicHalehameOmEt:
538     case EthiopicHalehameSidEt:
539     case EthiopicHalehameSoEt:
540     case EthiopicHalehameTiEr:
541     case EthiopicHalehameTiEt:
542     case EthiopicHalehameTig:
543     case Oromo:
544     case Sidama:
545     case Somali:
546     case Tigre:
547     case TigrinyaEr:
548     case TigrinyaErAbegede:
549     case TigrinyaEt:
550     case TigrinyaEtAbegede:
551         return ethiopicPrefaceColon;
552     case Armenian:
553     case ArabicIndic:
554     case Bengali:
555     case BinaryListStyle:
556     case Cambodian:
557     case CJKIdeographic:
558     case CjkEarthlyBranch:
559     case CjkHeavenlyStem:
560     case DecimalLeadingZero:
561     case DecimalListStyle:
562     case Devanagari:
563     case Georgian:
564     case Gujarati:
565     case Gurmukhi:
566     case Hangul:
567     case HangulConsonant:
568     case Hebrew:
569     case Hiragana:
570     case HiraganaIroha:
571     case Kannada:
572     case Katakana:
573     case KatakanaIroha:
574     case Khmer:
575     case Lao:
576     case LowerAlpha:
577     case LowerArmenian:
578     case LowerGreek:
579     case LowerHexadecimal:
580     case LowerLatin:
581     case LowerNorwegian:
582     case LowerRoman:
583     case Malayalam:
584     case Mongolian:
585     case Myanmar:
586     case Octal:
587     case Oriya:
588     case Persian:
589     case Telugu:
590     case Thai:
591     case Tibetan:
592     case UpperAlpha:
593     case UpperArmenian:
594     case UpperGreek:
595     case UpperHexadecimal:
596     case UpperLatin:
597     case UpperNorwegian:
598     case UpperRoman:
599     case Urdu:
600         return '.';
601     }
602
603     ASSERT_NOT_REACHED();
604     return '.';
605 }
606
607 String listMarkerText(EListStyleType type, int value)
608 {
609     // If the list-style-type, say hebrew, cannot represent |value| because it's outside
610     // its ordinal range then we fallback to some list style that can represent |value|.
611     switch (effectiveListMarkerType(type, value)) {
612         case NoneListStyle:
613             return "";
614
615         case Asterisks: {
616             static const LChar asterisksSymbols[1] = {
617                 0x2A
618             };
619             return toSymbolic(value, asterisksSymbols);
620         }
621         // We use the same characters for text security.
622         // See RenderText::setInternalString.
623         case Circle:
624             return String(&whiteBullet, 1);
625         case Disc:
626             return String(&bullet, 1);
627         case Footnotes: {
628             static const UChar footnotesSymbols[4] = {
629                 0x002A, 0x2051, 0x2020, 0x2021
630             };
631             return toSymbolic(value, footnotesSymbols);
632         }
633         case Square:
634             // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
635             // instead, but I think this looks better.
636             return String(&blackSquare, 1);
637
638         case DecimalListStyle:
639             return String::number(value);
640         case DecimalLeadingZero:
641             if (value < -9 || value > 9)
642                 return String::number(value);
643             if (value < 0)
644                 return "-0" + String::number(-value); // -01 to -09
645             return "0" + String::number(value); // 00 to 09
646
647         case ArabicIndic: {
648             static const UChar arabicIndicNumerals[10] = {
649                 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669
650             };
651             return toNumeric(value, arabicIndicNumerals);
652         }
653         case BinaryListStyle: {
654             static const LChar binaryNumerals[2] = {
655                 '0', '1'
656             };
657             return toNumeric(value, binaryNumerals);
658         }
659         case Bengali: {
660             static const UChar bengaliNumerals[10] = {
661                 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF
662             };
663             return toNumeric(value, bengaliNumerals);
664         }
665         case Cambodian:
666         case Khmer: {
667             static const UChar khmerNumerals[10] = {
668                 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9
669             };
670             return toNumeric(value, khmerNumerals);
671         }
672         case Devanagari: {
673             static const UChar devanagariNumerals[10] = {
674                 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F
675             };
676             return toNumeric(value, devanagariNumerals);
677         }
678         case Gujarati: {
679             static const UChar gujaratiNumerals[10] = {
680                 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF
681             };
682             return toNumeric(value, gujaratiNumerals);
683         }
684         case Gurmukhi: {
685             static const UChar gurmukhiNumerals[10] = {
686                 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F
687             };
688             return toNumeric(value, gurmukhiNumerals);
689         }
690         case Kannada: {
691             static const UChar kannadaNumerals[10] = {
692                 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF
693             };
694             return toNumeric(value, kannadaNumerals);
695         }
696         case LowerHexadecimal: {
697             static const LChar lowerHexadecimalNumerals[16] = {
698                 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
699             };
700             return toNumeric(value, lowerHexadecimalNumerals);
701         }
702         case Lao: {
703             static const UChar laoNumerals[10] = {
704                 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9
705             };
706             return toNumeric(value, laoNumerals);
707         }
708         case Malayalam: {
709             static const UChar malayalamNumerals[10] = {
710                 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F
711             };
712             return toNumeric(value, malayalamNumerals);
713         }
714         case Mongolian: {
715             static const UChar mongolianNumerals[10] = {
716                 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819
717             };
718             return toNumeric(value, mongolianNumerals);
719         }
720         case Myanmar: {
721             static const UChar myanmarNumerals[10] = {
722                 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049
723             };
724             return toNumeric(value, myanmarNumerals);
725         }
726         case Octal: {
727             static const LChar octalNumerals[8] = {
728                 '0', '1', '2', '3', '4', '5', '6', '7'
729             };
730             return toNumeric(value, octalNumerals);
731         }
732         case Oriya: {
733             static const UChar oriyaNumerals[10] = {
734                 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F
735             };
736             return toNumeric(value, oriyaNumerals);
737         }
738         case Persian:
739         case Urdu: {
740             static const UChar urduNumerals[10] = {
741                 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9
742             };
743             return toNumeric(value, urduNumerals);
744         }
745         case Telugu: {
746             static const UChar teluguNumerals[10] = {
747                 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F
748             };
749             return toNumeric(value, teluguNumerals);
750         }
751         case Tibetan: {
752             static const UChar tibetanNumerals[10] = {
753                 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29
754             };
755             return toNumeric(value, tibetanNumerals);
756         }
757         case Thai: {
758             static const UChar thaiNumerals[10] = {
759                 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59
760             };
761             return toNumeric(value, thaiNumerals);
762         }
763         case UpperHexadecimal: {
764             static const LChar upperHexadecimalNumerals[16] = {
765                 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
766             };
767             return toNumeric(value, upperHexadecimalNumerals);
768         }
769
770         case LowerAlpha:
771         case LowerLatin: {
772             static const LChar lowerLatinAlphabet[26] = {
773                 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
774                 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
775             };
776             return toAlphabetic(value, lowerLatinAlphabet);
777         }
778         case UpperAlpha:
779         case UpperLatin: {
780             static const LChar upperLatinAlphabet[26] = {
781                 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
782                 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
783             };
784             return toAlphabetic(value, upperLatinAlphabet);
785         }
786         case LowerGreek: {
787             static const UChar lowerGreekAlphabet[24] = {
788                 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
789                 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
790                 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9
791             };
792             return toAlphabetic(value, lowerGreekAlphabet);
793         }
794
795         case Hiragana: {
796             // FIXME: This table comes from the CSS3 draft, and is probably
797             // incorrect, given the comments in that draft.
798             static const UChar hiraganaAlphabet[48] = {
799                 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
800                 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
801                 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
802                 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
803                 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
804                 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093
805             };
806             return toAlphabetic(value, hiraganaAlphabet);
807         }
808         case HiraganaIroha: {
809             // FIXME: This table comes from the CSS3 draft, and is probably
810             // incorrect, given the comments in that draft.
811             static const UChar hiraganaIrohaAlphabet[47] = {
812                 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061,
813                 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F,
814                 0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046,
815                 0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075,
816                 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
817                 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059
818             };
819             return toAlphabetic(value, hiraganaIrohaAlphabet);
820         }
821         case Katakana: {
822             // FIXME: This table comes from the CSS3 draft, and is probably
823             // incorrect, given the comments in that draft.
824             static const UChar katakanaAlphabet[48] = {
825                 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF,
826                 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF,
827                 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD,
828                 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF,
829                 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
830                 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3
831             };
832             return toAlphabetic(value, katakanaAlphabet);
833         }
834         case KatakanaIroha: {
835             // FIXME: This table comes from the CSS3 draft, and is probably
836             // incorrect, given the comments in that draft.
837             static const UChar katakanaIrohaAlphabet[47] = {
838                 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1,
839                 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF,
840                 0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6,
841                 0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5,
842                 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
843                 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9
844             };
845             return toAlphabetic(value, katakanaIrohaAlphabet);
846         }
847
848         case Afar:
849         case EthiopicHalehameAaEt:
850         case EthiopicHalehameAaEr: {
851             static const UChar ethiopicHalehameAaErAlphabet[18] = {
852                 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290,
853                 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348
854             };
855             return toAlphabetic(value, ethiopicHalehameAaErAlphabet);
856         }
857         case Amharic:
858         case EthiopicHalehameAmEt: {
859             static const UChar ethiopicHalehameAmEtAlphabet[33] = {
860                 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
861                 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8,
862                 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320,
863                 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
864             };
865             return toAlphabetic(value, ethiopicHalehameAmEtAlphabet);
866         }
867         case AmharicAbegede:
868         case EthiopicAbegedeAmEt: {
869             static const UChar ethiopicAbegedeAmEtAlphabet[33] = {
870                 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
871                 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
872                 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238,
873                 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
874             };
875             return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet);
876         }
877         case CjkEarthlyBranch: {
878             static const UChar cjkEarthlyBranchAlphabet[12] = {
879                 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533,
880                 0x9149, 0x620C, 0x4EA5
881             };
882             return toAlphabetic(value, cjkEarthlyBranchAlphabet);
883         }
884         case CjkHeavenlyStem: {
885             static const UChar cjkHeavenlyStemAlphabet[10] = {
886                 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC,
887                 0x7678
888             };
889             return toAlphabetic(value, cjkHeavenlyStemAlphabet);
890         }
891         case Ethiopic:
892         case EthiopicHalehameGez: {
893             static const UChar ethiopicHalehameGezAlphabet[26] = {
894                 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1240, 0x1260,
895                 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
896                 0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
897             };
898             return toAlphabetic(value, ethiopicHalehameGezAlphabet);
899         }
900         case EthiopicAbegede:
901         case EthiopicAbegedeGez: {
902             static const UChar ethiopicAbegedeGezAlphabet[26] = {
903                 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1200, 0x12C8, 0x12D8, 0x1210, 0x1320,
904                 0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338,
905                 0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350
906             };
907             return toAlphabetic(value, ethiopicAbegedeGezAlphabet);
908         }
909         case HangulConsonant: {
910             static const UChar hangulConsonantAlphabet[14] = {
911                 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148,
912                 0x314A, 0x314B, 0x314C, 0x314D, 0x314E
913             };
914             return toAlphabetic(value, hangulConsonantAlphabet);
915         }
916         case Hangul: {
917             static const UChar hangulAlphabet[14] = {
918                 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790,
919                 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558
920             };
921             return toAlphabetic(value, hangulAlphabet);
922         }
923         case Oromo:
924         case EthiopicHalehameOmEt: {
925             static const UChar ethiopicHalehameOmEtAlphabet[25] = {
926                 0x1200, 0x1208, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 0x1270,
927                 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8,
928                 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
929             };
930             return toAlphabetic(value, ethiopicHalehameOmEtAlphabet);
931         }
932         case Sidama:
933         case EthiopicHalehameSidEt: {
934             static const UChar ethiopicHalehameSidEtAlphabet[26] = {
935                 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
936                 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0,
937                 0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
938             };
939             return toAlphabetic(value, ethiopicHalehameSidEtAlphabet);
940         }
941         case Somali:
942         case EthiopicHalehameSoEt: {
943             static const UChar ethiopicHalehameSoEtAlphabet[22] = {
944                 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
945                 0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0,
946                 0x1300, 0x1308, 0x1338, 0x1348
947             };
948             return toAlphabetic(value, ethiopicHalehameSoEtAlphabet);
949         }
950         case Tigre:
951         case EthiopicHalehameTig: {
952             static const UChar ethiopicHalehameTigAlphabet[27] = {
953                 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
954                 0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
955                 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350
956             };
957             return toAlphabetic(value, ethiopicHalehameTigAlphabet);
958         }
959         case TigrinyaEr:
960         case EthiopicHalehameTiEr: {
961             static const UChar ethiopicHalehameTiErAlphabet[31] = {
962                 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1250,
963                 0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8,
964                 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328,
965                 0x1330, 0x1338, 0x1348, 0x1350
966             };
967             return toAlphabetic(value, ethiopicHalehameTiErAlphabet);
968         }
969         case TigrinyaErAbegede:
970         case EthiopicAbegedeTiEr: {
971             static const UChar ethiopicAbegedeTiErAlphabet[31] = {
972                 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
973                 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
974                 0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238,
975                 0x1270, 0x1278, 0x1330, 0x1350
976             };
977             return toAlphabetic(value, ethiopicAbegedeTiErAlphabet);
978         }
979         case TigrinyaEt:
980         case EthiopicHalehameTiEt: {
981             static const UChar ethiopicHalehameTiEtAlphabet[34] = {
982                 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
983                 0x1250, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8,
984                 0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308,
985                 0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
986             };
987             return toAlphabetic(value, ethiopicHalehameTiEtAlphabet);
988         }
989         case TigrinyaEtAbegede:
990         case EthiopicAbegedeTiEt: {
991             static const UChar ethiopicAbegedeTiEtAlphabet[34] = {
992                 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
993                 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
994                 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230,
995                 0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
996             };
997             return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet);
998         }
999         case UpperGreek: {
1000             static const UChar upperGreekAlphabet[24] = {
1001                 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399,
1002                 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3,
1003                 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9
1004             };
1005             return toAlphabetic(value, upperGreekAlphabet);
1006         }
1007         case LowerNorwegian: {
1008             static const LChar lowerNorwegianAlphabet[29] = {
1009                 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
1010                 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
1011                 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xE6,
1012                 0xF8, 0xE5
1013             };
1014             return toAlphabetic(value, lowerNorwegianAlphabet);
1015         }
1016         case UpperNorwegian: {
1017             static const LChar upperNorwegianAlphabet[29] = {
1018                 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
1019                 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
1020                 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xC6,
1021                 0xD8, 0xC5
1022             };
1023             return toAlphabetic(value, upperNorwegianAlphabet);
1024         }
1025         case CJKIdeographic: {
1026             static const UChar traditionalChineseInformalTable[16] = {
1027                 0x842C, 0x5104, 0x5146,
1028                 0x5341, 0x767E, 0x5343,
1029                 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB,
1030                 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D
1031             };
1032             return toCJKIdeographic(value, traditionalChineseInformalTable);
1033         }
1034
1035         case LowerRoman:
1036             return toRoman(value, false);
1037         case UpperRoman:
1038             return toRoman(value, true);
1039
1040         case Armenian:
1041         case UpperArmenian:
1042             // CSS3 says "armenian" means "lower-armenian".
1043             // But the CSS2.1 test suite contains uppercase test results for "armenian",
1044             // so we'll match the test suite.
1045             return toArmenian(value, true);
1046         case LowerArmenian:
1047             return toArmenian(value, false);
1048         case Georgian:
1049             return toGeorgian(value);
1050         case Hebrew:
1051             return toHebrew(value);
1052     }
1053
1054     ASSERT_NOT_REACHED();
1055     return "";
1056 }
1057
1058 RenderListMarker::RenderListMarker(RenderListItem* item)
1059     : RenderBox(0)
1060     , m_listItem(item)
1061 {
1062     // init RenderObject attributes
1063     setInline(true);   // our object is Inline
1064     setReplaced(true); // pretend to be replaced
1065 }
1066
1067 RenderListMarker::~RenderListMarker()
1068 {
1069     if (m_image)
1070         m_image->removeClient(this);
1071 }
1072
1073 RenderListMarker* RenderListMarker::createAnonymous(RenderListItem* item)
1074 {
1075     Document& document = item->document();
1076     RenderListMarker* renderer = new RenderListMarker(item);
1077     renderer->setDocumentForAnonymous(&document);
1078     return renderer;
1079 }
1080
1081 void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
1082 {
1083     if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType()))
1084         setNeedsLayoutAndPrefWidthsRecalc();
1085
1086     RenderBox::styleWillChange(diff, newStyle);
1087 }
1088
1089 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
1090 {
1091     RenderBox::styleDidChange(diff, oldStyle);
1092
1093     if (m_image != style()->listStyleImage()) {
1094         if (m_image)
1095             m_image->removeClient(this);
1096         m_image = style()->listStyleImage();
1097         if (m_image)
1098             m_image->addClient(this);
1099     }
1100 }
1101
1102 InlineBox* RenderListMarker::createInlineBox()
1103 {
1104     InlineBox* result = RenderBox::createInlineBox();
1105     result->setIsText(isText());
1106     return result;
1107 }
1108
1109 bool RenderListMarker::isImage() const
1110 {
1111     return m_image && !m_image->errorOccurred();
1112 }
1113
1114 LayoutRect RenderListMarker::localSelectionRect()
1115 {
1116     InlineBox* box = inlineBoxWrapper();
1117     if (!box)
1118         return LayoutRect(LayoutPoint(), size());
1119     RootInlineBox* root = inlineBoxWrapper()->root();
1120     LayoutUnit newLogicalTop = root->block()->style()->isFlippedBlocksWritingMode() ? inlineBoxWrapper()->logicalBottom() - root->selectionBottom() : root->selectionTop() - inlineBoxWrapper()->logicalTop();
1121     if (root->block()->style()->isHorizontalWritingMode())
1122         return LayoutRect(0, newLogicalTop, width(), root->selectionHeight());
1123     return LayoutRect(newLogicalTop, 0, root->selectionHeight(), height());
1124 }
1125
1126 void RenderListMarker::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1127 {
1128     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
1129
1130     if (paintInfo.phase != PaintPhaseForeground)
1131         return;
1132
1133     if (style()->visibility() != VISIBLE)
1134         return;
1135
1136     LayoutPoint boxOrigin(paintOffset + location());
1137     LayoutRect overflowRect(visualOverflowRect());
1138     overflowRect.moveBy(boxOrigin);
1139     overflowRect.inflate(maximalOutlineSize(paintInfo.phase));
1140
1141     if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
1142         return;
1143
1144     LayoutRect box(boxOrigin, size());
1145
1146     IntRect marker = getRelativeMarkerRect();
1147     marker.moveBy(roundedIntPoint(boxOrigin));
1148
1149     GraphicsContext* context = paintInfo.context;
1150
1151     if (isImage()) {
1152         context->drawImage(m_image->image(this, marker.size()).get(), marker);
1153         if (selectionState() != SelectionNone) {
1154             LayoutRect selRect = localSelectionRect();
1155             selRect.moveBy(boxOrigin);
1156             context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor());
1157         }
1158         return;
1159     }
1160
1161     if (selectionState() != SelectionNone) {
1162         LayoutRect selRect = localSelectionRect();
1163         selRect.moveBy(boxOrigin);
1164         context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor());
1165     }
1166
1167     const Color color(resolveColor(CSSPropertyColor));
1168     context->setStrokeColor(color);
1169     context->setStrokeStyle(SolidStroke);
1170     context->setStrokeThickness(1.0f);
1171     context->setFillColor(color);
1172
1173     EListStyleType type = style()->listStyleType();
1174     switch (type) {
1175         case Disc:
1176             context->fillEllipse(marker);
1177             return;
1178         case Circle:
1179             context->strokeEllipse(marker);
1180             return;
1181         case Square:
1182             context->fillRect(marker);
1183             return;
1184         case NoneListStyle:
1185             return;
1186         case Afar:
1187         case Amharic:
1188         case AmharicAbegede:
1189         case ArabicIndic:
1190         case Armenian:
1191         case BinaryListStyle:
1192         case Bengali:
1193         case Cambodian:
1194         case CJKIdeographic:
1195         case CjkEarthlyBranch:
1196         case CjkHeavenlyStem:
1197         case DecimalLeadingZero:
1198         case DecimalListStyle:
1199         case Devanagari:
1200         case Ethiopic:
1201         case EthiopicAbegede:
1202         case EthiopicAbegedeAmEt:
1203         case EthiopicAbegedeGez:
1204         case EthiopicAbegedeTiEr:
1205         case EthiopicAbegedeTiEt:
1206         case EthiopicHalehameAaEr:
1207         case EthiopicHalehameAaEt:
1208         case EthiopicHalehameAmEt:
1209         case EthiopicHalehameGez:
1210         case EthiopicHalehameOmEt:
1211         case EthiopicHalehameSidEt:
1212         case EthiopicHalehameSoEt:
1213         case EthiopicHalehameTiEr:
1214         case EthiopicHalehameTiEt:
1215         case EthiopicHalehameTig:
1216         case Georgian:
1217         case Gujarati:
1218         case Gurmukhi:
1219         case Hangul:
1220         case HangulConsonant:
1221         case Hebrew:
1222         case Hiragana:
1223         case HiraganaIroha:
1224         case Kannada:
1225         case Katakana:
1226         case KatakanaIroha:
1227         case Khmer:
1228         case Lao:
1229         case LowerAlpha:
1230         case LowerArmenian:
1231         case LowerGreek:
1232         case LowerHexadecimal:
1233         case LowerLatin:
1234         case LowerNorwegian:
1235         case LowerRoman:
1236         case Malayalam:
1237         case Mongolian:
1238         case Myanmar:
1239         case Octal:
1240         case Oriya:
1241         case Oromo:
1242         case Persian:
1243         case Sidama:
1244         case Somali:
1245         case Telugu:
1246         case Thai:
1247         case Tibetan:
1248         case Tigre:
1249         case TigrinyaEr:
1250         case TigrinyaErAbegede:
1251         case TigrinyaEt:
1252         case TigrinyaEtAbegede:
1253         case UpperAlpha:
1254         case UpperArmenian:
1255         case UpperGreek:
1256         case UpperHexadecimal:
1257         case UpperLatin:
1258         case UpperNorwegian:
1259         case UpperRoman:
1260         case Urdu:
1261         case Asterisks:
1262         case Footnotes:
1263             break;
1264     }
1265     if (m_text.isEmpty())
1266         return;
1267
1268     const Font& font = style()->font();
1269     TextRun textRun = RenderBlockFlow::constructTextRun(this, font, m_text, style());
1270
1271     GraphicsContextStateSaver stateSaver(*context, false);
1272     if (!style()->isHorizontalWritingMode()) {
1273         marker.moveBy(roundedIntPoint(-boxOrigin));
1274         marker = marker.transposedRect();
1275         marker.moveBy(IntPoint(roundToInt(box.x()), roundToInt(box.y() - logicalHeight())));
1276         stateSaver.save();
1277         context->translate(marker.x(), marker.maxY());
1278         context->rotate(static_cast<float>(deg2rad(90.)));
1279         context->translate(-marker.x(), -marker.maxY());
1280     }
1281
1282     TextRunPaintInfo textRunPaintInfo(textRun);
1283     textRunPaintInfo.bounds = marker;
1284     IntPoint textOrigin = IntPoint(marker.x(), marker.y() + style()->fontMetrics().ascent());
1285
1286     if (type == Asterisks || type == Footnotes) {
1287         context->drawText(font, textRunPaintInfo, textOrigin);
1288     }
1289     else {
1290         // Text is not arbitrary. We can judge whether it's RTL from the first character,
1291         // and we only need to handle the direction RightToLeft for now.
1292         bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
1293         StringBuilder reversedText;
1294         if (textNeedsReversing) {
1295             int length = m_text.length();
1296             reversedText.reserveCapacity(length);
1297             for (int i = length - 1; i >= 0; --i)
1298                 reversedText.append(m_text[i]);
1299             ASSERT(reversedText.length() == reversedText.capacity());
1300             textRun.setText(reversedText.toString());
1301         }
1302
1303         const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1304         UChar suffixStr[2] = {
1305             style()->isLeftToRightDirection() ? suffix : ' ',
1306             style()->isLeftToRightDirection() ? ' ' : suffix
1307         };
1308         TextRun suffixRun = RenderBlockFlow::constructTextRun(this, font, suffixStr, 2, style(), style()->direction());
1309         TextRunPaintInfo suffixRunInfo(suffixRun);
1310         suffixRunInfo.bounds = marker;
1311
1312         if (style()->isLeftToRightDirection()) {
1313             context->drawText(font, textRunPaintInfo, textOrigin);
1314             context->drawText(font, suffixRunInfo, textOrigin + IntSize(font.width(textRun), 0));
1315         } else {
1316             context->drawText(font, suffixRunInfo, textOrigin);
1317             context->drawText(font, textRunPaintInfo, textOrigin + IntSize(font.width(suffixRun), 0));
1318         }
1319     }
1320 }
1321
1322 void RenderListMarker::layout()
1323 {
1324     ASSERT(needsLayout());
1325
1326     LayoutRectRecorder recorder(*this);
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(), style()->direction()));
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(), style()->direction()));
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 (inlineBoxWrapper() && canUpdateSelectionOnRootLineBoxes())
1828         if (RootInlineBox* root = 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