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