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