~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/rendering/RenderListMarker.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

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