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)
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.
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.
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.
26 #include "core/rendering/RenderListMarker.h"
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"
38 const int cMarkerPadding = 7;
40 enum SequenceType { NumericSequence, AlphabeticSequence };
42 static String toRoman(int number, bool upper)
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);
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];
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;
60 int num = number % 10;
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];
67 letters[lettersSize - ++length] = digits[d + 2];
69 letters[lettersSize - ++length] = digits[d];
74 ASSERT(length <= lettersSize);
75 return String(&letters[lettersSize - length], length);
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;
82 template <typename CharacterType>
83 static inline String toAlphabeticOrNumeric(numberType number, const CharacterType* sequence, unsigned sequenceSize, SequenceType type)
85 ASSERT(sequenceSize >= 2);
87 const int lettersSize = sizeof(numberType) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
89 CharacterType letters[lettersSize];
91 bool isNegativeNumber = false;
92 unsigned numberShadow = number;
93 if (type == AlphabeticSequence) {
96 } else if (number < 0) {
97 numberShadow = -number;
98 isNegativeNumber = true;
100 letters[lettersSize - 1] = sequence[numberShadow % sequenceSize];
103 if (type == AlphabeticSequence) {
104 while ((numberShadow /= sequenceSize) > 0) {
106 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
109 while ((numberShadow /= sequenceSize) > 0)
110 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
112 if (isNegativeNumber)
113 letters[lettersSize - ++length] = hyphenMinus;
115 ASSERT(length <= lettersSize);
116 return String(&letters[lettersSize - length], length);
119 template <typename CharacterType>
120 static String toSymbolic(int number, const CharacterType* symbols, unsigned symbolsSize)
123 ASSERT(symbolsSize >= 1);
124 unsigned numberShadow = number;
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;
132 letters.append(symbols[numberShadow % symbolsSize]);
133 return letters.toString();
136 template <typename CharacterType>
137 static String toAlphabetic(int number, const CharacterType* alphabet, unsigned alphabetSize)
139 return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence);
142 template <typename CharacterType>
143 static String toNumeric(int number, const CharacterType* numerals, unsigned numeralsSize)
145 return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence);
148 template <typename CharacterType, size_t size>
149 static inline String toAlphabetic(int number, const CharacterType(&alphabet)[size])
151 return toAlphabetic(number, alphabet, size);
154 template <typename CharacterType, size_t size>
155 static inline String toNumeric(int number, const CharacterType(&alphabet)[size])
157 return toNumeric(number, alphabet, size);
160 template <typename CharacterType, size_t size>
161 static inline String toSymbolic(int number, const CharacterType(&alphabet)[size])
163 return toSymbolic(number, alphabet, size);
166 static int toHebrewUnder1000(int number, UChar letters[5])
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);
172 int fourHundreds = number / 400;
173 for (int i = 0; i < fourHundreds; i++)
174 letters[length++] = 1511 + 3;
177 letters[length++] = 1511 + (number / 100) - 1;
179 if (number == 15 || number == 16) {
180 letters[length++] = 1487 + 9;
181 letters[length++] = 1487 + number - 9;
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];
187 if (int ones = number % 10)
188 letters[length++] = 1487 + ones;
194 static String toHebrew(int number)
196 // FIXME: CSS3 mentions ways to make this work for much larger numbers.
197 ASSERT(number >= 0 && number <= 999999);
200 static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
201 return String(hebrewZero, 3);
204 const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
205 UChar letters[lettersSize];
211 length = toHebrewUnder1000(number / 1000, letters);
212 letters[length++] = '\'';
213 number = number % 1000;
215 length += toHebrewUnder1000(number, letters + length);
217 ASSERT(length <= lettersSize);
218 return String(letters, length);
221 static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
223 ASSERT(number >= 0 && number < 10000);
226 int lowerOffset = upper ? 0 : 0x0030;
228 if (int thousands = number / 1000) {
229 if (thousands == 7) {
230 letters[length++] = 0x0552 + lowerOffset;
232 letters[length++] = 0x0302;
234 letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
236 letters[length++] = 0x0302;
240 if (int hundreds = (number / 100) % 10) {
241 letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
243 letters[length++] = 0x0302;
246 if (int tens = (number / 10) % 10) {
247 letters[length++] = (0x053A - 1 + lowerOffset) + tens;
249 letters[length++] = 0x0302;
252 if (int ones = number % 10) {
253 letters[length++] = (0x531 - 1 + lowerOffset) + ones;
255 letters[length++] = 0x0302;
261 static String toArmenian(int number, bool upper)
263 ASSERT(number >= 1 && number <= 99999999);
265 const int lettersSize = 18; // twice what toArmenianUnder10000 needs
266 UChar letters[lettersSize];
268 int length = toArmenianUnder10000(number / 10000, upper, true, letters);
269 length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
271 ASSERT(length <= lettersSize);
272 return String(letters, length);
275 static String toGeorgian(int number)
277 ASSERT(number >= 1 && number <= 19999);
279 const int lettersSize = 5;
280 UChar letters[lettersSize];
285 letters[length++] = 0x10F5;
287 if (int thousands = (number / 1000) % 10) {
288 static const UChar georgianThousands[9] = {
289 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
291 letters[length++] = georgianThousands[thousands - 1];
294 if (int hundreds = (number / 100) % 10) {
295 static const UChar georgianHundreds[9] = {
296 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
298 letters[length++] = georgianHundreds[hundreds - 1];
301 if (int tens = (number / 10) % 10) {
302 static const UChar georgianTens[9] = {
303 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
305 letters[length++] = georgianTens[tens - 1];
308 if (int ones = number % 10) {
309 static const UChar georgianOnes[9] = {
310 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
312 letters[length++] = georgianOnes[ones - 1];
315 ASSERT(length <= lettersSize);
316 return String(letters, length);
319 // The table uses the order from the CSS3 specification:
320 // first 3 group markers, then 3 digit markers, then ten digits.
321 static String toCJKIdeographic(int number, const UChar table[16])
325 enum AbstractCJKChar {
327 secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
328 secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
329 digit0, digit1, digit2, digit3, digit4,
330 digit5, digit6, digit7, digit8, digit9
334 return String(&table[digit0 - 1], 1);
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 };
340 for (int i = 0; i < 4; ++i) {
341 int groupValue = number % 10000;
344 // Process least-significant group first, but put it in the buffer last.
345 AbstractCJKChar* group = &buffer[(3 - i) * groupLength];
348 group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i);
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);
356 group[5] = secondDigitMarker;
358 if (number != 0 || groupValue > 99) {
359 int digitValue = ((groupValue / 100) % 10);
360 group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue);
362 group[3] = thirdDigitMarker;
364 if (number != 0 || groupValue > 999) {
365 int digitValue = groupValue / 1000;
366 group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue);
368 group[1] = fourthDigitMarker;
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);
382 // Convert into characters, omitting consecutive runs of digit0 and
383 // any trailing digit0.
385 UChar characters[bufferLength];
386 AbstractCJKChar last = noChar;
387 for (int i = 0; i < bufferLength; ++i) {
388 AbstractCJKChar a = buffer[i];
390 if (a != digit0 || last != digit0)
391 characters[length++] = table[a - 1];
398 return String(characters, length);
401 static EListStyleType effectiveListMarkerType(EListStyleType type, int value)
403 // Note, the following switch statement has been explicitly grouped
404 // by list-style-type ordinal range.
408 case BinaryListStyle:
411 case DecimalLeadingZero:
412 case DecimalListStyle:
420 case LowerHexadecimal:
432 case UpperHexadecimal:
434 return type; // Can represent all ordinals.
436 return (value < 1 || value > 99999999) ? DecimalListStyle : type;
438 return (value < 0) ? DecimalListStyle : type;
440 return (value < 1 || value > 19999) ? DecimalListStyle : type;
442 return (value < 0 || value > 999999) ? DecimalListStyle : type;
445 return (value < 1 || value > 3999) ? DecimalListStyle : type;
450 case CjkEarthlyBranch:
451 case CjkHeavenlyStem:
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:
470 case HangulConsonant:
485 case TigrinyaErAbegede:
487 case TigrinyaEtAbegede:
493 return (value < 1) ? DecimalListStyle : type;
496 ASSERT_NOT_REACHED();
500 UChar RenderListMarker::listMarkerSuffix(EListStyleType type, int value)
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);
506 // Note, the following switch statement has been explicitly
507 // grouped by list-style-type suffix.
508 switch (effectiveType) {
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:
540 case TigrinyaErAbegede:
542 case TigrinyaEtAbegede:
543 return ethiopicPrefaceColon;
547 case BinaryListStyle:
550 case CjkEarthlyBranch:
551 case CjkHeavenlyStem:
552 case DecimalLeadingZero:
553 case DecimalListStyle:
559 case HangulConsonant:
571 case LowerHexadecimal:
587 case UpperHexadecimal:
595 ASSERT_NOT_REACHED();
599 String listMarkerText(EListStyleType type, int value)
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)) {
608 static const LChar asterisksSymbols[1] = {
611 return toSymbolic(value, asterisksSymbols);
613 // We use the same characters for text security.
614 // See RenderText::setInternalString.
616 return String(&whiteBullet, 1);
618 return String(&bullet, 1);
620 static const UChar footnotesSymbols[4] = {
621 0x002A, 0x2051, 0x2020, 0x2021
623 return toSymbolic(value, footnotesSymbols);
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);
630 case DecimalListStyle:
631 return String::number(value);
632 case DecimalLeadingZero:
633 if (value < -9 || value > 9)
634 return String::number(value);
636 return "-0" + String::number(-value); // -01 to -09
637 return "0" + String::number(value); // 00 to 09
640 static const UChar arabicIndicNumerals[10] = {
641 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669
643 return toNumeric(value, arabicIndicNumerals);
645 case BinaryListStyle: {
646 static const LChar binaryNumerals[2] = {
649 return toNumeric(value, binaryNumerals);
652 static const UChar bengaliNumerals[10] = {
653 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF
655 return toNumeric(value, bengaliNumerals);
659 static const UChar khmerNumerals[10] = {
660 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9
662 return toNumeric(value, khmerNumerals);
665 static const UChar devanagariNumerals[10] = {
666 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F
668 return toNumeric(value, devanagariNumerals);
671 static const UChar gujaratiNumerals[10] = {
672 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF
674 return toNumeric(value, gujaratiNumerals);
677 static const UChar gurmukhiNumerals[10] = {
678 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F
680 return toNumeric(value, gurmukhiNumerals);
683 static const UChar kannadaNumerals[10] = {
684 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF
686 return toNumeric(value, kannadaNumerals);
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'
692 return toNumeric(value, lowerHexadecimalNumerals);
695 static const UChar laoNumerals[10] = {
696 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9
698 return toNumeric(value, laoNumerals);
701 static const UChar malayalamNumerals[10] = {
702 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F
704 return toNumeric(value, malayalamNumerals);
707 static const UChar mongolianNumerals[10] = {
708 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819
710 return toNumeric(value, mongolianNumerals);
713 static const UChar myanmarNumerals[10] = {
714 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049
716 return toNumeric(value, myanmarNumerals);
719 static const LChar octalNumerals[8] = {
720 '0', '1', '2', '3', '4', '5', '6', '7'
722 return toNumeric(value, octalNumerals);
725 static const UChar oriyaNumerals[10] = {
726 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F
728 return toNumeric(value, oriyaNumerals);
732 static const UChar urduNumerals[10] = {
733 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9
735 return toNumeric(value, urduNumerals);
738 static const UChar teluguNumerals[10] = {
739 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F
741 return toNumeric(value, teluguNumerals);
744 static const UChar tibetanNumerals[10] = {
745 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29
747 return toNumeric(value, tibetanNumerals);
750 static const UChar thaiNumerals[10] = {
751 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59
753 return toNumeric(value, thaiNumerals);
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'
759 return toNumeric(value, upperHexadecimalNumerals);
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'
768 return toAlphabetic(value, lowerLatinAlphabet);
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'
776 return toAlphabetic(value, upperLatinAlphabet);
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
784 return toAlphabetic(value, lowerGreekAlphabet);
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
798 return toAlphabetic(value, hiraganaAlphabet);
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
811 return toAlphabetic(value, hiraganaIrohaAlphabet);
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
824 return toAlphabetic(value, katakanaAlphabet);
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
837 return toAlphabetic(value, katakanaIrohaAlphabet);
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
847 return toAlphabetic(value, ethiopicHalehameAaErAlphabet);
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
857 return toAlphabetic(value, ethiopicHalehameAmEtAlphabet);
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
867 return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet);
869 case CjkEarthlyBranch: {
870 static const UChar cjkEarthlyBranchAlphabet[12] = {
871 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533,
872 0x9149, 0x620C, 0x4EA5
874 return toAlphabetic(value, cjkEarthlyBranchAlphabet);
876 case CjkHeavenlyStem: {
877 static const UChar cjkHeavenlyStemAlphabet[10] = {
878 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC,
881 return toAlphabetic(value, cjkHeavenlyStemAlphabet);
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
890 return toAlphabetic(value, ethiopicHalehameGezAlphabet);
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
899 return toAlphabetic(value, ethiopicAbegedeGezAlphabet);
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
906 return toAlphabetic(value, hangulConsonantAlphabet);
909 static const UChar hangulAlphabet[14] = {
910 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790,
911 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558
913 return toAlphabetic(value, hangulAlphabet);
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
922 return toAlphabetic(value, ethiopicHalehameOmEtAlphabet);
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
931 return toAlphabetic(value, ethiopicHalehameSidEtAlphabet);
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
940 return toAlphabetic(value, ethiopicHalehameSoEtAlphabet);
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
949 return toAlphabetic(value, ethiopicHalehameTigAlphabet);
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
959 return toAlphabetic(value, ethiopicHalehameTiErAlphabet);
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
969 return toAlphabetic(value, ethiopicAbegedeTiErAlphabet);
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
979 return toAlphabetic(value, ethiopicHalehameTiEtAlphabet);
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
989 return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet);
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
997 return toAlphabetic(value, upperGreekAlphabet);
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,
1006 return toAlphabetic(value, lowerNorwegianAlphabet);
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,
1015 return toAlphabetic(value, upperNorwegianAlphabet);
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
1024 return toCJKIdeographic(value, traditionalChineseInformalTable);
1028 return toRoman(value, false);
1030 return toRoman(value, true);
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);
1039 return toArmenian(value, false);
1041 return toGeorgian(value);
1043 return toHebrew(value);
1046 ASSERT_NOT_REACHED();
1050 RenderListMarker::RenderListMarker(RenderListItem* item)
1054 // init RenderObject attributes
1055 setInline(true); // our object is Inline
1056 setReplaced(true); // pretend to be replaced
1059 RenderListMarker::~RenderListMarker()
1063 void RenderListMarker::destroy()
1066 m_image->removeClient(this);
1067 RenderBox::destroy();
1070 void RenderListMarker::trace(Visitor* visitor)
1072 visitor->trace(m_listItem);
1073 RenderBox::trace(visitor);
1076 RenderListMarker* RenderListMarker::createAnonymous(RenderListItem* item)
1078 Document& document = item->document();
1079 RenderListMarker* renderer = new RenderListMarker(item);
1080 renderer->setDocumentForAnonymous(&document);
1084 void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
1086 if (style() && (newStyle.listStylePosition() != style()->listStylePosition() || newStyle.listStyleType() != style()->listStyleType()))
1087 setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
1089 RenderBox::styleWillChange(diff, newStyle);
1092 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
1094 RenderBox::styleDidChange(diff, oldStyle);
1096 if (m_image != style()->listStyleImage()) {
1098 m_image->removeClient(this);
1099 m_image = style()->listStyleImage();
1101 m_image->addClient(this);
1105 InlineBox* RenderListMarker::createInlineBox()
1107 InlineBox* result = RenderBox::createInlineBox();
1108 result->setIsText(isText());
1112 bool RenderListMarker::isImage() const
1114 return m_image && !m_image->errorOccurred();
1117 LayoutRect RenderListMarker::localSelectionRect()
1119 InlineBox* box = inlineBoxWrapper();
1121 return LayoutRect(LayoutPoint(), size());
1122 RootInlineBox& root = inlineBoxWrapper()->root();
1123 LayoutUnit newLogicalTop = root.block().style()->slowIsFlippedBlocksWritingMode() ? 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());
1129 void RenderListMarker::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1131 ListMarkerPainter(*this).paint(paintInfo, paintOffset);
1134 void RenderListMarker::layout()
1136 ASSERT(needsLayout());
1139 updateMarginsAndContent();
1140 setWidth(m_image->imageSize(this, style()->effectiveZoom()).width());
1141 setHeight(m_image->imageSize(this, style()->effectiveZoom()).height());
1143 setLogicalWidth(minPreferredLogicalWidth());
1144 setLogicalHeight(style()->fontMetrics().height());
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());
1160 void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*)
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())
1166 if (width() != m_image->imageSize(this, style()->effectiveZoom()).width() || height() != m_image->imageSize(this, style()->effectiveZoom()).height() || m_image->errorOccurred())
1167 setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
1169 setShouldDoFullPaintInvalidation();
1172 void RenderListMarker::updateMarginsAndContent()
1178 void RenderListMarker::updateContent()
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())
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());
1197 EListStyleType type = style()->listStyleType();
1204 m_text = listMarkerText(type, 0); // value is ignored for these types
1210 case AmharicAbegede:
1213 case BinaryListStyle:
1216 case CJKIdeographic:
1217 case CjkEarthlyBranch:
1218 case CjkHeavenlyStem:
1219 case DecimalLeadingZero:
1220 case DecimalListStyle:
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:
1242 case HangulConsonant:
1254 case LowerHexadecimal:
1256 case LowerNorwegian:
1272 case TigrinyaErAbegede:
1274 case TigrinyaEtAbegede:
1278 case UpperHexadecimal:
1280 case UpperNorwegian:
1283 m_text = listMarkerText(type, m_listItem->value());
1288 void RenderListMarker::computePreferredLogicalWidths()
1290 ASSERT(preferredLogicalWidthsDirty());
1294 LayoutSize imageSize = m_image->imageSize(this, style()->effectiveZoom());
1295 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = style()->isHorizontalWritingMode() ? imageSize.width() : imageSize.height();
1296 clearPreferredLogicalWidthsDirty();
1301 const Font& font = style()->font();
1303 LayoutUnit logicalWidth = 0;
1304 EListStyleType type = style()->listStyleType();
1310 logicalWidth = font.width(m_text); // no suffix for these types
1315 logicalWidth = (font.fontMetrics().ascent() * 2 / 3 + 1) / 2 + 2;
1319 case AmharicAbegede:
1322 case BinaryListStyle:
1325 case CJKIdeographic:
1326 case CjkEarthlyBranch:
1327 case CjkHeavenlyStem:
1328 case DecimalLeadingZero:
1329 case DecimalListStyle:
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:
1351 case HangulConsonant:
1363 case LowerHexadecimal:
1365 case LowerNorwegian:
1381 case TigrinyaErAbegede:
1383 case TigrinyaEtAbegede:
1387 case UpperHexadecimal:
1389 case UpperNorwegian:
1392 if (m_text.isEmpty())
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;
1403 m_minPreferredLogicalWidth = logicalWidth;
1404 m_maxPreferredLogicalWidth = logicalWidth;
1406 clearPreferredLogicalWidthsDirty();
1411 void RenderListMarker::updateMargins()
1413 const FontMetrics& fontMetrics = style()->fontMetrics();
1415 LayoutUnit marginStart = 0;
1416 LayoutUnit marginEnd = 0;
1420 marginEnd = cMarkerPadding;
1421 else switch (style()->listStyleType()) {
1426 marginEnd = fontMetrics.ascent() - minPreferredLogicalWidth() + 1;
1432 if (style()->isLeftToRightDirection()) {
1434 marginStart = -minPreferredLogicalWidth() - cMarkerPadding;
1436 int offset = fontMetrics.ascent() * 2 / 3;
1437 switch (style()->listStyleType()) {
1441 marginStart = -offset - cMarkerPadding - 1;
1446 marginStart = m_text.isEmpty() ? LayoutUnit() : -minPreferredLogicalWidth() - offset / 2;
1449 marginEnd = -marginStart - minPreferredLogicalWidth();
1452 marginEnd = cMarkerPadding;
1454 int offset = fontMetrics.ascent() * 2 / 3;
1455 switch (style()->listStyleType()) {
1459 marginEnd = offset + cMarkerPadding + 1 - minPreferredLogicalWidth();
1464 marginEnd = m_text.isEmpty() ? 0 : offset / 2;
1467 marginStart = -marginEnd - minPreferredLogicalWidth();
1472 style()->setMarginStart(Length(marginStart, Fixed));
1473 style()->setMarginEnd(Length(marginEnd, Fixed));
1476 LayoutUnit RenderListMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1479 return m_listItem->lineHeight(firstLine, direction, PositionOfInteriorLineBoxes);
1480 return RenderBox::lineHeight(firstLine, direction, linePositionMode);
1483 int RenderListMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1485 ASSERT(linePositionMode == PositionOnContainingLine);
1487 return m_listItem->baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes);
1488 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1491 bool RenderListMarker::isInside() const
1493 return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
1496 IntRect RenderListMarker::getRelativeMarkerRect()
1499 return IntRect(0, 0, m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height());
1501 IntRect relativeRect;
1502 EListStyleType type = style()->listStyleType();
1506 const Font& font = style()->font();
1507 relativeRect = IntRect(0, 0, font.width(m_text), font.fontMetrics().height());
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);
1524 case AmharicAbegede:
1527 case BinaryListStyle:
1530 case CJKIdeographic:
1531 case CjkEarthlyBranch:
1532 case CjkHeavenlyStem:
1533 case DecimalLeadingZero:
1534 case DecimalListStyle:
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:
1556 case HangulConsonant:
1568 case LowerHexadecimal:
1570 case LowerNorwegian:
1586 case TigrinyaErAbegede:
1588 case TigrinyaEtAbegede:
1592 case UpperHexadecimal:
1594 case UpperNorwegian:
1597 if (m_text.isEmpty())
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());
1606 if (!style()->isHorizontalWritingMode()) {
1607 relativeRect = relativeRect.transposedRect();
1608 relativeRect.setX(width() - relativeRect.x() - relativeRect.width());
1611 return relativeRect;
1614 void RenderListMarker::setSelectionState(SelectionState state)
1616 // The selection state for our containing block hierarchy is updated by the base class call.
1617 RenderBox::setSelectionState(state);
1619 if (inlineBoxWrapper() && canUpdateSelectionOnRootLineBoxes())
1620 inlineBoxWrapper()->root().setHasSelectedChildren(state != SelectionNone);
1623 LayoutRect RenderListMarker::selectionRectForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer) const
1625 ASSERT(!needsLayout());
1627 if (selectionState() == SelectionNone || !inlineBoxWrapper())
1628 return LayoutRect();
1630 RootInlineBox& root = inlineBoxWrapper()->root();
1631 LayoutRect rect(0, root.selectionTop() - y(), width(), root.selectionHeight());
1632 mapRectToPaintInvalidationBacking(paintInvalidationContainer, rect, 0);
1636 void RenderListMarker::listItemStyleDidChange()
1638 RefPtr<RenderStyle> newStyle = RenderStyle::create();
1639 // The marker always inherits from the list item, regardless of where it might end
1640 // up (e.g., in some deeply nested line box). See CSS3 spec.
1641 newStyle->inheritFrom(m_listItem->style());
1643 // Reuse the current margins. Otherwise resetting the margins to initial values
1644 // would trigger unnecessary layout.
1645 newStyle->setMarginStart(style()->marginStart());
1646 newStyle->setMarginEnd(style()->marginRight());
1648 setStyle(newStyle.release());
1651 } // namespace blink