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