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