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