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