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/dom/Document.h"
29 #include "core/fetch/ImageResource.h"
30 #include "core/platform/graphics/Font.h"
31 #include "core/platform/graphics/GraphicsContextStateSaver.h"
32 #include "core/rendering/GraphicsContextAnnotator.h"
33 #include "core/rendering/RenderLayer.h"
34 #include "core/rendering/RenderListItem.h"
35 #include "core/rendering/RenderView.h"
36 #include "wtf/text/StringBuilder.h"
37 #include "wtf/unicode/CharacterNames.h"
41 using namespace Unicode;
45 const int cMarkerPadding = 7;
47 enum SequenceType { NumericSequence, AlphabeticSequence };
49 static String toRoman(int number, bool upper)
51 // FIXME: CSS3 describes how to make this work for much larger numbers,
52 // using overbars and special characters. It also specifies the characters
53 // in the range U+2160 to U+217F instead of standard ASCII ones.
54 ASSERT(number >= 1 && number <= 3999);
56 // Big enough to store largest roman number less than 3999 which
57 // is 3888 (MMMDCCCLXXXVIII)
58 const int lettersSize = 15;
59 LChar letters[lettersSize];
62 const LChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
63 const LChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
64 const LChar* digits = upper ? udigits : ldigits;
67 int num = number % 10;
69 for (int i = num % 5; i > 0; i--)
70 letters[lettersSize - ++length] = digits[d];
71 if (num >= 4 && num <= 8)
72 letters[lettersSize - ++length] = digits[d + 1];
74 letters[lettersSize - ++length] = digits[d + 2];
76 letters[lettersSize - ++length] = digits[d];
81 ASSERT(length <= lettersSize);
82 return String(&letters[lettersSize - length], length);
85 // The typedef is needed because taking sizeof(number) in the const expression below doesn't work with some compilers.
86 // This is likely the case because of the template.
87 typedef int numberType;
89 template <typename CharacterType>
90 static inline String toAlphabeticOrNumeric(numberType number, const CharacterType* sequence, unsigned sequenceSize, SequenceType type)
92 ASSERT(sequenceSize >= 2);
94 const int lettersSize = sizeof(numberType) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
96 CharacterType letters[lettersSize];
98 bool isNegativeNumber = false;
99 unsigned numberShadow = number;
100 if (type == AlphabeticSequence) {
103 } else if (number < 0) {
104 numberShadow = -number;
105 isNegativeNumber = true;
107 letters[lettersSize - 1] = sequence[numberShadow % sequenceSize];
110 if (type == AlphabeticSequence) {
111 while ((numberShadow /= sequenceSize) > 0) {
113 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
116 while ((numberShadow /= sequenceSize) > 0)
117 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
119 if (isNegativeNumber)
120 letters[lettersSize - ++length] = hyphenMinus;
122 ASSERT(length <= lettersSize);
123 return String(&letters[lettersSize - length], length);
126 template <typename CharacterType>
127 static String toSymbolic(int number, const CharacterType* symbols, unsigned symbolsSize)
130 ASSERT(symbolsSize >= 1);
131 unsigned numberShadow = number;
134 // The asterisks list-style-type is the worst case; we show |numberShadow| asterisks.
135 StringBuilder letters;
136 letters.append(symbols[numberShadow % symbolsSize]);
137 unsigned numSymbols = numberShadow / symbolsSize;
139 letters.append(symbols[numberShadow % symbolsSize]);
140 return letters.toString();
143 template <typename CharacterType>
144 static String toAlphabetic(int number, const CharacterType* alphabet, unsigned alphabetSize)
146 return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence);
149 template <typename CharacterType>
150 static String toNumeric(int number, const CharacterType* numerals, unsigned numeralsSize)
152 return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence);
155 template <typename CharacterType, size_t size>
156 static inline String toAlphabetic(int number, const CharacterType(&alphabet)[size])
158 return toAlphabetic(number, alphabet, size);
161 template <typename CharacterType, size_t size>
162 static inline String toNumeric(int number, const CharacterType(&alphabet)[size])
164 return toNumeric(number, alphabet, size);
167 template <typename CharacterType, size_t size>
168 static inline String toSymbolic(int number, const CharacterType(&alphabet)[size])
170 return toSymbolic(number, alphabet, size);
173 static int toHebrewUnder1000(int number, UChar letters[5])
175 // FIXME: CSS3 mentions various refinements not implemented here.
176 // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
177 ASSERT(number >= 0 && number < 1000);
179 int fourHundreds = number / 400;
180 for (int i = 0; i < fourHundreds; i++)
181 letters[length++] = 1511 + 3;
184 letters[length++] = 1511 + (number / 100) - 1;
186 if (number == 15 || number == 16) {
187 letters[length++] = 1487 + 9;
188 letters[length++] = 1487 + number - 9;
190 if (int tens = number / 10) {
191 static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
192 letters[length++] = hebrewTens[tens - 1];
194 if (int ones = number % 10)
195 letters[length++] = 1487 + ones;
201 static String toHebrew(int number)
203 // FIXME: CSS3 mentions ways to make this work for much larger numbers.
204 ASSERT(number >= 0 && number <= 999999);
207 static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
208 return String(hebrewZero, 3);
211 const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
212 UChar letters[lettersSize];
218 length = toHebrewUnder1000(number / 1000, letters);
219 letters[length++] = '\'';
220 number = number % 1000;
222 length += toHebrewUnder1000(number, letters + length);
224 ASSERT(length <= lettersSize);
225 return String(letters, length);
228 static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
230 ASSERT(number >= 0 && number < 10000);
233 int lowerOffset = upper ? 0 : 0x0030;
235 if (int thousands = number / 1000) {
236 if (thousands == 7) {
237 letters[length++] = 0x0552 + lowerOffset;
239 letters[length++] = 0x0302;
241 letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
243 letters[length++] = 0x0302;
247 if (int hundreds = (number / 100) % 10) {
248 letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
250 letters[length++] = 0x0302;
253 if (int tens = (number / 10) % 10) {
254 letters[length++] = (0x053A - 1 + lowerOffset) + tens;
256 letters[length++] = 0x0302;
259 if (int ones = number % 10) {
260 letters[length++] = (0x531 - 1 + lowerOffset) + ones;
262 letters[length++] = 0x0302;
268 static String toArmenian(int number, bool upper)
270 ASSERT(number >= 1 && number <= 99999999);
272 const int lettersSize = 18; // twice what toArmenianUnder10000 needs
273 UChar letters[lettersSize];
275 int length = toArmenianUnder10000(number / 10000, upper, true, letters);
276 length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
278 ASSERT(length <= lettersSize);
279 return String(letters, length);
282 static String toGeorgian(int number)
284 ASSERT(number >= 1 && number <= 19999);
286 const int lettersSize = 5;
287 UChar letters[lettersSize];
292 letters[length++] = 0x10F5;
294 if (int thousands = (number / 1000) % 10) {
295 static const UChar georgianThousands[9] = {
296 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
298 letters[length++] = georgianThousands[thousands - 1];
301 if (int hundreds = (number / 100) % 10) {
302 static const UChar georgianHundreds[9] = {
303 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
305 letters[length++] = georgianHundreds[hundreds - 1];
308 if (int tens = (number / 10) % 10) {
309 static const UChar georgianTens[9] = {
310 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
312 letters[length++] = georgianTens[tens - 1];
315 if (int ones = number % 10) {
316 static const UChar georgianOnes[9] = {
317 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
319 letters[length++] = georgianOnes[ones - 1];
322 ASSERT(length <= lettersSize);
323 return String(letters, length);
326 // The table uses the order from the CSS3 specification:
327 // first 3 group markers, then 3 digit markers, then ten digits.
328 static String toCJKIdeographic(int number, const UChar table[16])
332 enum AbstractCJKChar {
334 secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
335 secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
336 digit0, digit1, digit2, digit3, digit4,
337 digit5, digit6, digit7, digit8, digit9
341 return String(&table[digit0 - 1], 1);
343 const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker
344 const int bufferLength = 4 * groupLength;
345 AbstractCJKChar buffer[bufferLength] = { noChar };
347 for (int i = 0; i < 4; ++i) {
348 int groupValue = number % 10000;
351 // Process least-significant group first, but put it in the buffer last.
352 AbstractCJKChar* group = &buffer[(3 - i) * groupLength];
355 group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i);
357 // Put in the four digits and digit markers for any non-zero digits.
358 group[6] = static_cast<AbstractCJKChar>(digit0 + (groupValue % 10));
359 if (number != 0 || groupValue > 9) {
360 int digitValue = ((groupValue / 10) % 10);
361 group[4] = static_cast<AbstractCJKChar>(digit0 + digitValue);
363 group[5] = secondDigitMarker;
365 if (number != 0 || groupValue > 99) {
366 int digitValue = ((groupValue / 100) % 10);
367 group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue);
369 group[3] = thirdDigitMarker;
371 if (number != 0 || groupValue > 999) {
372 int digitValue = groupValue / 1000;
373 group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue);
375 group[1] = fourthDigitMarker;
378 // Remove the tens digit, but leave the marker, for any group that has
379 // a value of less than 20.
380 if (groupValue < 20) {
381 ASSERT(group[4] == noChar || group[4] == digit0 || group[4] == digit1);
389 // Convert into characters, omitting consecutive runs of digit0 and
390 // any trailing digit0.
392 UChar characters[bufferLength];
393 AbstractCJKChar last = noChar;
394 for (int i = 0; i < bufferLength; ++i) {
395 AbstractCJKChar a = buffer[i];
397 if (a != digit0 || last != digit0)
398 characters[length++] = table[a - 1];
405 return String(characters, length);
408 static EListStyleType effectiveListMarkerType(EListStyleType type, int value)
410 // Note, the following switch statement has been explicitly grouped
411 // by list-style-type ordinal range.
415 case BinaryListStyle:
418 case DecimalLeadingZero:
419 case DecimalListStyle:
427 case LowerHexadecimal:
439 case UpperHexadecimal:
441 return type; // Can represent all ordinals.
443 return (value < 1 || value > 99999999) ? DecimalListStyle : type;
445 return (value < 0) ? DecimalListStyle : type;
447 return (value < 1 || value > 19999) ? DecimalListStyle : type;
449 return (value < 0 || value > 999999) ? DecimalListStyle : type;
452 return (value < 1 || value > 3999) ? DecimalListStyle : type;
457 case CjkEarthlyBranch:
458 case CjkHeavenlyStem:
460 case EthiopicAbegede:
461 case EthiopicAbegedeAmEt:
462 case EthiopicAbegedeGez:
463 case EthiopicAbegedeTiEr:
464 case EthiopicAbegedeTiEt:
465 case EthiopicHalehameAaEr:
466 case EthiopicHalehameAaEt:
467 case EthiopicHalehameAmEt:
468 case EthiopicHalehameGez:
469 case EthiopicHalehameOmEt:
470 case EthiopicHalehameSidEt:
471 case EthiopicHalehameSoEt:
472 case EthiopicHalehameTiEr:
473 case EthiopicHalehameTiEt:
474 case EthiopicHalehameTig:
477 case HangulConsonant:
492 case TigrinyaErAbegede:
494 case TigrinyaEtAbegede:
500 return (value < 1) ? DecimalListStyle : type;
503 ASSERT_NOT_REACHED();
507 static UChar listMarkerSuffix(EListStyleType type, int value)
509 // If the list-style-type cannot represent |value| because it's outside its
510 // ordinal range then we fall back to some list style that can represent |value|.
511 EListStyleType effectiveType = effectiveListMarkerType(type, value);
513 // Note, the following switch statement has been explicitly
514 // grouped by list-style-type suffix.
515 switch (effectiveType) {
527 case EthiopicAbegede:
528 case EthiopicAbegedeAmEt:
529 case EthiopicAbegedeGez:
530 case EthiopicAbegedeTiEr:
531 case EthiopicAbegedeTiEt:
532 case EthiopicHalehameAaEr:
533 case EthiopicHalehameAaEt:
534 case EthiopicHalehameAmEt:
535 case EthiopicHalehameGez:
536 case EthiopicHalehameOmEt:
537 case EthiopicHalehameSidEt:
538 case EthiopicHalehameSoEt:
539 case EthiopicHalehameTiEr:
540 case EthiopicHalehameTiEt:
541 case EthiopicHalehameTig:
547 case TigrinyaErAbegede:
549 case TigrinyaEtAbegede:
550 return ethiopicPrefaceColon;
554 case BinaryListStyle:
557 case CjkEarthlyBranch:
558 case CjkHeavenlyStem:
559 case DecimalLeadingZero:
560 case DecimalListStyle:
566 case HangulConsonant:
578 case LowerHexadecimal:
594 case UpperHexadecimal:
602 ASSERT_NOT_REACHED();
606 String listMarkerText(EListStyleType type, int value)
608 // If the list-style-type, say hebrew, cannot represent |value| because it's outside
609 // its ordinal range then we fallback to some list style that can represent |value|.
610 switch (effectiveListMarkerType(type, value)) {
615 static const LChar asterisksSymbols[1] = {
618 return toSymbolic(value, asterisksSymbols);
620 // We use the same characters for text security.
621 // See RenderText::setInternalString.
623 return String(&whiteBullet, 1);
625 return String(&bullet, 1);
627 static const UChar footnotesSymbols[4] = {
628 0x002A, 0x2051, 0x2020, 0x2021
630 return toSymbolic(value, footnotesSymbols);
633 // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
634 // instead, but I think this looks better.
635 return String(&blackSquare, 1);
637 case DecimalListStyle:
638 return String::number(value);
639 case DecimalLeadingZero:
640 if (value < -9 || value > 9)
641 return String::number(value);
643 return "-0" + String::number(-value); // -01 to -09
644 return "0" + String::number(value); // 00 to 09
647 static const UChar arabicIndicNumerals[10] = {
648 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669
650 return toNumeric(value, arabicIndicNumerals);
652 case BinaryListStyle: {
653 static const LChar binaryNumerals[2] = {
656 return toNumeric(value, binaryNumerals);
659 static const UChar bengaliNumerals[10] = {
660 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF
662 return toNumeric(value, bengaliNumerals);
666 static const UChar khmerNumerals[10] = {
667 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9
669 return toNumeric(value, khmerNumerals);
672 static const UChar devanagariNumerals[10] = {
673 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F
675 return toNumeric(value, devanagariNumerals);
678 static const UChar gujaratiNumerals[10] = {
679 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF
681 return toNumeric(value, gujaratiNumerals);
684 static const UChar gurmukhiNumerals[10] = {
685 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F
687 return toNumeric(value, gurmukhiNumerals);
690 static const UChar kannadaNumerals[10] = {
691 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF
693 return toNumeric(value, kannadaNumerals);
695 case LowerHexadecimal: {
696 static const LChar lowerHexadecimalNumerals[16] = {
697 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
699 return toNumeric(value, lowerHexadecimalNumerals);
702 static const UChar laoNumerals[10] = {
703 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9
705 return toNumeric(value, laoNumerals);
708 static const UChar malayalamNumerals[10] = {
709 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F
711 return toNumeric(value, malayalamNumerals);
714 static const UChar mongolianNumerals[10] = {
715 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819
717 return toNumeric(value, mongolianNumerals);
720 static const UChar myanmarNumerals[10] = {
721 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049
723 return toNumeric(value, myanmarNumerals);
726 static const LChar octalNumerals[8] = {
727 '0', '1', '2', '3', '4', '5', '6', '7'
729 return toNumeric(value, octalNumerals);
732 static const UChar oriyaNumerals[10] = {
733 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F
735 return toNumeric(value, oriyaNumerals);
739 static const UChar urduNumerals[10] = {
740 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9
742 return toNumeric(value, urduNumerals);
745 static const UChar teluguNumerals[10] = {
746 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F
748 return toNumeric(value, teluguNumerals);
751 static const UChar tibetanNumerals[10] = {
752 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29
754 return toNumeric(value, tibetanNumerals);
757 static const UChar thaiNumerals[10] = {
758 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59
760 return toNumeric(value, thaiNumerals);
762 case UpperHexadecimal: {
763 static const LChar upperHexadecimalNumerals[16] = {
764 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
766 return toNumeric(value, upperHexadecimalNumerals);
771 static const LChar lowerLatinAlphabet[26] = {
772 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
773 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
775 return toAlphabetic(value, lowerLatinAlphabet);
779 static const LChar upperLatinAlphabet[26] = {
780 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
781 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
783 return toAlphabetic(value, upperLatinAlphabet);
786 static const UChar lowerGreekAlphabet[24] = {
787 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
788 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
789 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9
791 return toAlphabetic(value, lowerGreekAlphabet);
795 // FIXME: This table comes from the CSS3 draft, and is probably
796 // incorrect, given the comments in that draft.
797 static const UChar hiraganaAlphabet[48] = {
798 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
799 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
800 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
801 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
802 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
803 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093
805 return toAlphabetic(value, hiraganaAlphabet);
807 case HiraganaIroha: {
808 // FIXME: This table comes from the CSS3 draft, and is probably
809 // incorrect, given the comments in that draft.
810 static const UChar hiraganaIrohaAlphabet[47] = {
811 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061,
812 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F,
813 0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046,
814 0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075,
815 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
816 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059
818 return toAlphabetic(value, hiraganaIrohaAlphabet);
821 // FIXME: This table comes from the CSS3 draft, and is probably
822 // incorrect, given the comments in that draft.
823 static const UChar katakanaAlphabet[48] = {
824 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF,
825 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF,
826 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD,
827 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF,
828 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
829 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3
831 return toAlphabetic(value, katakanaAlphabet);
833 case KatakanaIroha: {
834 // FIXME: This table comes from the CSS3 draft, and is probably
835 // incorrect, given the comments in that draft.
836 static const UChar katakanaIrohaAlphabet[47] = {
837 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1,
838 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF,
839 0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6,
840 0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5,
841 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
842 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9
844 return toAlphabetic(value, katakanaIrohaAlphabet);
848 case EthiopicHalehameAaEt:
849 case EthiopicHalehameAaEr: {
850 static const UChar ethiopicHalehameAaErAlphabet[18] = {
851 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290,
852 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348
854 return toAlphabetic(value, ethiopicHalehameAaErAlphabet);
857 case EthiopicHalehameAmEt: {
858 static const UChar ethiopicHalehameAmEtAlphabet[33] = {
859 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
860 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8,
861 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320,
862 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
864 return toAlphabetic(value, ethiopicHalehameAmEtAlphabet);
867 case EthiopicAbegedeAmEt: {
868 static const UChar ethiopicAbegedeAmEtAlphabet[33] = {
869 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
870 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
871 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238,
872 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
874 return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet);
876 case CjkEarthlyBranch: {
877 static const UChar cjkEarthlyBranchAlphabet[12] = {
878 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533,
879 0x9149, 0x620C, 0x4EA5
881 return toAlphabetic(value, cjkEarthlyBranchAlphabet);
883 case CjkHeavenlyStem: {
884 static const UChar cjkHeavenlyStemAlphabet[10] = {
885 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC,
888 return toAlphabetic(value, cjkHeavenlyStemAlphabet);
891 case EthiopicHalehameGez: {
892 static const UChar ethiopicHalehameGezAlphabet[26] = {
893 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1240, 0x1260,
894 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
895 0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
897 return toAlphabetic(value, ethiopicHalehameGezAlphabet);
899 case EthiopicAbegede:
900 case EthiopicAbegedeGez: {
901 static const UChar ethiopicAbegedeGezAlphabet[26] = {
902 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1200, 0x12C8, 0x12D8, 0x1210, 0x1320,
903 0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338,
904 0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350
906 return toAlphabetic(value, ethiopicAbegedeGezAlphabet);
908 case HangulConsonant: {
909 static const UChar hangulConsonantAlphabet[14] = {
910 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148,
911 0x314A, 0x314B, 0x314C, 0x314D, 0x314E
913 return toAlphabetic(value, hangulConsonantAlphabet);
916 static const UChar hangulAlphabet[14] = {
917 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790,
918 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558
920 return toAlphabetic(value, hangulAlphabet);
923 case EthiopicHalehameOmEt: {
924 static const UChar ethiopicHalehameOmEtAlphabet[25] = {
925 0x1200, 0x1208, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 0x1270,
926 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8,
927 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
929 return toAlphabetic(value, ethiopicHalehameOmEtAlphabet);
932 case EthiopicHalehameSidEt: {
933 static const UChar ethiopicHalehameSidEtAlphabet[26] = {
934 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
935 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0,
936 0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
938 return toAlphabetic(value, ethiopicHalehameSidEtAlphabet);
941 case EthiopicHalehameSoEt: {
942 static const UChar ethiopicHalehameSoEtAlphabet[22] = {
943 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
944 0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0,
945 0x1300, 0x1308, 0x1338, 0x1348
947 return toAlphabetic(value, ethiopicHalehameSoEtAlphabet);
950 case EthiopicHalehameTig: {
951 static const UChar ethiopicHalehameTigAlphabet[27] = {
952 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
953 0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
954 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350
956 return toAlphabetic(value, ethiopicHalehameTigAlphabet);
959 case EthiopicHalehameTiEr: {
960 static const UChar ethiopicHalehameTiErAlphabet[31] = {
961 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1250,
962 0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8,
963 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328,
964 0x1330, 0x1338, 0x1348, 0x1350
966 return toAlphabetic(value, ethiopicHalehameTiErAlphabet);
968 case TigrinyaErAbegede:
969 case EthiopicAbegedeTiEr: {
970 static const UChar ethiopicAbegedeTiErAlphabet[31] = {
971 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
972 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
973 0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238,
974 0x1270, 0x1278, 0x1330, 0x1350
976 return toAlphabetic(value, ethiopicAbegedeTiErAlphabet);
979 case EthiopicHalehameTiEt: {
980 static const UChar ethiopicHalehameTiEtAlphabet[34] = {
981 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
982 0x1250, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8,
983 0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308,
984 0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
986 return toAlphabetic(value, ethiopicHalehameTiEtAlphabet);
988 case TigrinyaEtAbegede:
989 case EthiopicAbegedeTiEt: {
990 static const UChar ethiopicAbegedeTiEtAlphabet[34] = {
991 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
992 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
993 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230,
994 0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
996 return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet);
999 static const UChar upperGreekAlphabet[24] = {
1000 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399,
1001 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3,
1002 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9
1004 return toAlphabetic(value, upperGreekAlphabet);
1006 case LowerNorwegian: {
1007 static const LChar lowerNorwegianAlphabet[29] = {
1008 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
1009 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
1010 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xE6,
1013 return toAlphabetic(value, lowerNorwegianAlphabet);
1015 case UpperNorwegian: {
1016 static const LChar upperNorwegianAlphabet[29] = {
1017 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
1018 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
1019 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xC6,
1022 return toAlphabetic(value, upperNorwegianAlphabet);
1024 case CJKIdeographic: {
1025 static const UChar traditionalChineseInformalTable[16] = {
1026 0x842C, 0x5104, 0x5146,
1027 0x5341, 0x767E, 0x5343,
1028 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB,
1029 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D
1031 return toCJKIdeographic(value, traditionalChineseInformalTable);
1035 return toRoman(value, false);
1037 return toRoman(value, true);
1041 // CSS3 says "armenian" means "lower-armenian".
1042 // But the CSS2.1 test suite contains uppercase test results for "armenian",
1043 // so we'll match the test suite.
1044 return toArmenian(value, true);
1046 return toArmenian(value, false);
1048 return toGeorgian(value);
1050 return toHebrew(value);
1053 ASSERT_NOT_REACHED();
1057 RenderListMarker::RenderListMarker(RenderListItem* item)
1061 // init RenderObject attributes
1062 setInline(true); // our object is Inline
1063 setReplaced(true); // pretend to be replaced
1066 RenderListMarker::~RenderListMarker()
1069 m_image->removeClient(this);
1072 RenderListMarker* RenderListMarker::createAnonymous(RenderListItem* item)
1074 Document& document = item->document();
1075 RenderListMarker* renderer = new RenderListMarker(item);
1076 renderer->setDocumentForAnonymous(&document);
1080 void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
1082 if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType()))
1083 setNeedsLayoutAndPrefWidthsRecalc();
1085 RenderBox::styleWillChange(diff, newStyle);
1088 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
1090 RenderBox::styleDidChange(diff, oldStyle);
1092 if (m_image != style()->listStyleImage()) {
1094 m_image->removeClient(this);
1095 m_image = style()->listStyleImage();
1097 m_image->addClient(this);
1101 InlineBox* RenderListMarker::createInlineBox()
1103 InlineBox* result = RenderBox::createInlineBox();
1104 result->setIsText(isText());
1108 bool RenderListMarker::isImage() const
1110 return m_image && !m_image->errorOccurred();
1113 LayoutRect RenderListMarker::localSelectionRect()
1115 InlineBox* box = inlineBoxWrapper();
1117 return LayoutRect(LayoutPoint(), size());
1118 RootInlineBox* root = m_inlineBoxWrapper->root();
1119 LayoutUnit newLogicalTop = root->block()->style()->isFlippedBlocksWritingMode() ? m_inlineBoxWrapper->logicalBottom() - root->selectionBottom() : root->selectionTop() - m_inlineBoxWrapper->logicalTop();
1120 if (root->block()->style()->isHorizontalWritingMode())
1121 return LayoutRect(0, newLogicalTop, width(), root->selectionHeight());
1122 return LayoutRect(newLogicalTop, 0, root->selectionHeight(), height());
1125 void RenderListMarker::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1127 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
1129 if (paintInfo.phase != PaintPhaseForeground)
1132 if (style()->visibility() != VISIBLE)
1135 LayoutPoint boxOrigin(paintOffset + location());
1136 LayoutRect overflowRect(visualOverflowRect());
1137 overflowRect.moveBy(boxOrigin);
1138 overflowRect.inflate(maximalOutlineSize(paintInfo.phase));
1140 if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
1143 LayoutRect box(boxOrigin, size());
1145 IntRect marker = getRelativeMarkerRect();
1146 marker.moveBy(roundedIntPoint(boxOrigin));
1148 GraphicsContext* context = paintInfo.context;
1151 context->drawImage(m_image->image(this, marker.size()).get(), marker);
1152 if (selectionState() != SelectionNone) {
1153 LayoutRect selRect = localSelectionRect();
1154 selRect.moveBy(boxOrigin);
1155 context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor());
1160 if (selectionState() != SelectionNone) {
1161 LayoutRect selRect = localSelectionRect();
1162 selRect.moveBy(boxOrigin);
1163 context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor());
1166 const Color color(resolveColor(CSSPropertyColor));
1167 context->setStrokeColor(color);
1168 context->setStrokeStyle(SolidStroke);
1169 context->setStrokeThickness(1.0f);
1170 context->setFillColor(color);
1172 EListStyleType type = style()->listStyleType();
1175 context->fillEllipse(marker);
1178 context->strokeEllipse(marker);
1181 context->fillRect(marker);
1187 case AmharicAbegede:
1190 case BinaryListStyle:
1193 case CJKIdeographic:
1194 case CjkEarthlyBranch:
1195 case CjkHeavenlyStem:
1196 case DecimalLeadingZero:
1197 case DecimalListStyle:
1200 case EthiopicAbegede:
1201 case EthiopicAbegedeAmEt:
1202 case EthiopicAbegedeGez:
1203 case EthiopicAbegedeTiEr:
1204 case EthiopicAbegedeTiEt:
1205 case EthiopicHalehameAaEr:
1206 case EthiopicHalehameAaEt:
1207 case EthiopicHalehameAmEt:
1208 case EthiopicHalehameGez:
1209 case EthiopicHalehameOmEt:
1210 case EthiopicHalehameSidEt:
1211 case EthiopicHalehameSoEt:
1212 case EthiopicHalehameTiEr:
1213 case EthiopicHalehameTiEt:
1214 case EthiopicHalehameTig:
1219 case HangulConsonant:
1231 case LowerHexadecimal:
1233 case LowerNorwegian:
1249 case TigrinyaErAbegede:
1251 case TigrinyaEtAbegede:
1255 case UpperHexadecimal:
1257 case UpperNorwegian:
1264 if (m_text.isEmpty())
1267 const Font& font = style()->font();
1268 TextRun textRun = RenderBlockFlow::constructTextRun(this, font, m_text, style());
1270 GraphicsContextStateSaver stateSaver(*context, false);
1271 if (!style()->isHorizontalWritingMode()) {
1272 marker.moveBy(roundedIntPoint(-boxOrigin));
1273 marker = marker.transposedRect();
1274 marker.moveBy(IntPoint(roundToInt(box.x()), roundToInt(box.y() - logicalHeight())));
1276 context->translate(marker.x(), marker.maxY());
1277 context->rotate(static_cast<float>(deg2rad(90.)));
1278 context->translate(-marker.x(), -marker.maxY());
1281 TextRunPaintInfo textRunPaintInfo(textRun);
1282 textRunPaintInfo.bounds = marker;
1283 IntPoint textOrigin = IntPoint(marker.x(), marker.y() + style()->fontMetrics().ascent());
1285 if (type == Asterisks || type == Footnotes) {
1286 context->drawText(font, textRunPaintInfo, textOrigin);
1289 // Text is not arbitrary. We can judge whether it's RTL from the first character,
1290 // and we only need to handle the direction RightToLeft for now.
1291 bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
1292 StringBuilder reversedText;
1293 if (textNeedsReversing) {
1294 int length = m_text.length();
1295 reversedText.reserveCapacity(length);
1296 for (int i = length - 1; i >= 0; --i)
1297 reversedText.append(m_text[i]);
1298 ASSERT(reversedText.length() == reversedText.capacity());
1299 textRun.setText(reversedText.toString());
1302 const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1303 if (style()->isLeftToRightDirection()) {
1304 context->drawText(font, textRunPaintInfo, textOrigin);
1306 UChar suffixSpace[2] = { suffix, ' ' };
1307 TextRun suffixRun = RenderBlockFlow::constructTextRun(this, font, suffixSpace, 2, style());
1308 TextRunPaintInfo suffixRunInfo(suffixRun);
1309 suffixRunInfo.bounds = marker;
1310 context->drawText(font, suffixRunInfo, textOrigin + IntSize(font.width(textRun), 0));
1312 UChar spaceSuffix[2] = { ' ', suffix };
1313 TextRun suffixRun = RenderBlockFlow::constructTextRun(this, font, spaceSuffix, 2, style());
1314 TextRunPaintInfo suffixRunInfo(suffixRun);
1315 suffixRunInfo.bounds = marker;
1316 context->drawText(font, suffixRunInfo, textOrigin);
1318 context->drawText(font, textRunPaintInfo, textOrigin + IntSize(font.width(suffixRun), 0));
1323 void RenderListMarker::layout()
1325 ASSERT(needsLayout());
1328 updateMarginsAndContent();
1329 setWidth(m_image->imageSize(this, style()->effectiveZoom()).width());
1330 setHeight(m_image->imageSize(this, style()->effectiveZoom()).height());
1332 setLogicalWidth(minPreferredLogicalWidth());
1333 setLogicalHeight(style()->fontMetrics().height());
1339 Length startMargin = style()->marginStart();
1340 Length endMargin = style()->marginEnd();
1341 if (startMargin.isFixed())
1342 setMarginStart(startMargin.value());
1343 if (endMargin.isFixed())
1344 setMarginEnd(endMargin.value());
1349 void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*)
1351 // A list marker can't have a background or border image, so no need to call the base class method.
1352 if (o != m_image->data())
1355 if (width() != m_image->imageSize(this, style()->effectiveZoom()).width() || height() != m_image->imageSize(this, style()->effectiveZoom()).height() || m_image->errorOccurred())
1356 setNeedsLayoutAndPrefWidthsRecalc();
1361 void RenderListMarker::updateMarginsAndContent()
1367 void RenderListMarker::updateContent()
1369 // FIXME: This if-statement is just a performance optimization, but it's messy to use the preferredLogicalWidths dirty bit for this.
1370 // It's unclear if this is a premature optimization.
1371 if (!preferredLogicalWidthsDirty())
1377 // FIXME: This is a somewhat arbitrary width. Generated images for markers really won't become particularly useful
1378 // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box.
1379 int bulletWidth = style()->fontMetrics().ascent() / 2;
1380 m_image->setContainerSizeForRenderer(this, IntSize(bulletWidth, bulletWidth), style()->effectiveZoom());
1384 EListStyleType type = style()->listStyleType();
1391 m_text = listMarkerText(type, 0); // value is ignored for these types
1397 case AmharicAbegede:
1400 case BinaryListStyle:
1403 case CJKIdeographic:
1404 case CjkEarthlyBranch:
1405 case CjkHeavenlyStem:
1406 case DecimalLeadingZero:
1407 case DecimalListStyle:
1410 case EthiopicAbegede:
1411 case EthiopicAbegedeAmEt:
1412 case EthiopicAbegedeGez:
1413 case EthiopicAbegedeTiEr:
1414 case EthiopicAbegedeTiEt:
1415 case EthiopicHalehameAaEr:
1416 case EthiopicHalehameAaEt:
1417 case EthiopicHalehameAmEt:
1418 case EthiopicHalehameGez:
1419 case EthiopicHalehameOmEt:
1420 case EthiopicHalehameSidEt:
1421 case EthiopicHalehameSoEt:
1422 case EthiopicHalehameTiEr:
1423 case EthiopicHalehameTiEt:
1424 case EthiopicHalehameTig:
1429 case HangulConsonant:
1441 case LowerHexadecimal:
1443 case LowerNorwegian:
1459 case TigrinyaErAbegede:
1461 case TigrinyaEtAbegede:
1465 case UpperHexadecimal:
1467 case UpperNorwegian:
1470 m_text = listMarkerText(type, m_listItem->value());
1475 void RenderListMarker::computePreferredLogicalWidths()
1477 ASSERT(preferredLogicalWidthsDirty());
1481 LayoutSize imageSize = m_image->imageSize(this, style()->effectiveZoom());
1482 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = style()->isHorizontalWritingMode() ? imageSize.width() : imageSize.height();
1483 clearPreferredLogicalWidthsDirty();
1488 const Font& font = style()->font();
1490 LayoutUnit logicalWidth = 0;
1491 EListStyleType type = style()->listStyleType();
1497 logicalWidth = font.width(m_text); // no suffix for these types
1502 logicalWidth = (font.fontMetrics().ascent() * 2 / 3 + 1) / 2 + 2;
1506 case AmharicAbegede:
1509 case BinaryListStyle:
1512 case CJKIdeographic:
1513 case CjkEarthlyBranch:
1514 case CjkHeavenlyStem:
1515 case DecimalLeadingZero:
1516 case DecimalListStyle:
1519 case EthiopicAbegede:
1520 case EthiopicAbegedeAmEt:
1521 case EthiopicAbegedeGez:
1522 case EthiopicAbegedeTiEr:
1523 case EthiopicAbegedeTiEt:
1524 case EthiopicHalehameAaEr:
1525 case EthiopicHalehameAaEt:
1526 case EthiopicHalehameAmEt:
1527 case EthiopicHalehameGez:
1528 case EthiopicHalehameOmEt:
1529 case EthiopicHalehameSidEt:
1530 case EthiopicHalehameSoEt:
1531 case EthiopicHalehameTiEr:
1532 case EthiopicHalehameTiEt:
1533 case EthiopicHalehameTig:
1538 case HangulConsonant:
1550 case LowerHexadecimal:
1552 case LowerNorwegian:
1568 case TigrinyaErAbegede:
1570 case TigrinyaEtAbegede:
1574 case UpperHexadecimal:
1576 case UpperNorwegian:
1579 if (m_text.isEmpty())
1582 LayoutUnit itemWidth = font.width(m_text);
1583 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
1584 LayoutUnit suffixSpaceWidth = font.width(RenderBlockFlow::constructTextRun(this, font, suffixSpace, 2, style()));
1585 logicalWidth = itemWidth + suffixSpaceWidth;
1590 m_minPreferredLogicalWidth = logicalWidth;
1591 m_maxPreferredLogicalWidth = logicalWidth;
1593 clearPreferredLogicalWidthsDirty();
1598 void RenderListMarker::updateMargins()
1600 const FontMetrics& fontMetrics = style()->fontMetrics();
1602 LayoutUnit marginStart = 0;
1603 LayoutUnit marginEnd = 0;
1607 marginEnd = cMarkerPadding;
1608 else switch (style()->listStyleType()) {
1613 marginEnd = fontMetrics.ascent() - minPreferredLogicalWidth() + 1;
1619 if (style()->isLeftToRightDirection()) {
1621 marginStart = -minPreferredLogicalWidth() - cMarkerPadding;
1623 int offset = fontMetrics.ascent() * 2 / 3;
1624 switch (style()->listStyleType()) {
1628 marginStart = -offset - cMarkerPadding - 1;
1633 marginStart = m_text.isEmpty() ? LayoutUnit() : -minPreferredLogicalWidth() - offset / 2;
1636 marginEnd = -marginStart - minPreferredLogicalWidth();
1639 marginEnd = cMarkerPadding;
1641 int offset = fontMetrics.ascent() * 2 / 3;
1642 switch (style()->listStyleType()) {
1646 marginEnd = offset + cMarkerPadding + 1 - minPreferredLogicalWidth();
1651 marginEnd = m_text.isEmpty() ? 0 : offset / 2;
1654 marginStart = -marginEnd - minPreferredLogicalWidth();
1659 style()->setMarginStart(Length(marginStart, Fixed));
1660 style()->setMarginEnd(Length(marginEnd, Fixed));
1663 LayoutUnit RenderListMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1666 return m_listItem->lineHeight(firstLine, direction, PositionOfInteriorLineBoxes);
1667 return RenderBox::lineHeight(firstLine, direction, linePositionMode);
1670 int RenderListMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1672 ASSERT(linePositionMode == PositionOnContainingLine);
1674 return m_listItem->baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes);
1675 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1678 String RenderListMarker::suffix() const
1680 EListStyleType type = style()->listStyleType();
1681 const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1686 // If the suffix is not ' ', an extra space is needed
1688 if (style()->isLeftToRightDirection()) {
1696 return String(data, 2);
1699 bool RenderListMarker::isInside() const
1701 return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
1704 IntRect RenderListMarker::getRelativeMarkerRect()
1707 return IntRect(0, 0, m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height());
1709 IntRect relativeRect;
1710 EListStyleType type = style()->listStyleType();
1714 const Font& font = style()->font();
1715 relativeRect = IntRect(0, 0, font.width(m_text), font.fontMetrics().height());
1721 // FIXME: Are these particular rounding rules necessary?
1722 const FontMetrics& fontMetrics = style()->fontMetrics();
1723 int ascent = fontMetrics.ascent();
1724 int bulletWidth = (ascent * 2 / 3 + 1) / 2;
1725 relativeRect = IntRect(1, 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
1732 case AmharicAbegede:
1735 case BinaryListStyle:
1738 case CJKIdeographic:
1739 case CjkEarthlyBranch:
1740 case CjkHeavenlyStem:
1741 case DecimalLeadingZero:
1742 case DecimalListStyle:
1745 case EthiopicAbegede:
1746 case EthiopicAbegedeAmEt:
1747 case EthiopicAbegedeGez:
1748 case EthiopicAbegedeTiEr:
1749 case EthiopicAbegedeTiEt:
1750 case EthiopicHalehameAaEr:
1751 case EthiopicHalehameAaEt:
1752 case EthiopicHalehameAmEt:
1753 case EthiopicHalehameGez:
1754 case EthiopicHalehameOmEt:
1755 case EthiopicHalehameSidEt:
1756 case EthiopicHalehameSoEt:
1757 case EthiopicHalehameTiEr:
1758 case EthiopicHalehameTiEt:
1759 case EthiopicHalehameTig:
1764 case HangulConsonant:
1776 case LowerHexadecimal:
1778 case LowerNorwegian:
1794 case TigrinyaErAbegede:
1796 case TigrinyaEtAbegede:
1800 case UpperHexadecimal:
1802 case UpperNorwegian:
1805 if (m_text.isEmpty())
1807 const Font& font = style()->font();
1808 int itemWidth = font.width(m_text);
1809 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
1810 int suffixSpaceWidth = font.width(RenderBlockFlow::constructTextRun(this, font, suffixSpace, 2, style()));
1811 relativeRect = IntRect(0, 0, itemWidth + suffixSpaceWidth, font.fontMetrics().height());
1814 if (!style()->isHorizontalWritingMode()) {
1815 relativeRect = relativeRect.transposedRect();
1816 relativeRect.setX(width() - relativeRect.x() - relativeRect.width());
1819 return relativeRect;
1822 void RenderListMarker::setSelectionState(SelectionState state)
1824 // The selection state for our containing block hierarchy is updated by the base class call.
1825 RenderBox::setSelectionState(state);
1827 if (m_inlineBoxWrapper && canUpdateSelectionOnRootLineBoxes())
1828 if (RootInlineBox* root = m_inlineBoxWrapper->root())
1829 root->setHasSelectedChildren(state != SelectionNone);
1832 LayoutRect RenderListMarker::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent)
1834 ASSERT(!needsLayout());
1836 if (selectionState() == SelectionNone || !inlineBoxWrapper())
1837 return LayoutRect();
1839 RootInlineBox* root = inlineBoxWrapper()->root();
1840 LayoutRect rect(0, root->selectionTop() - y(), width(), root->selectionHeight());
1842 if (clipToVisibleContent)
1843 computeRectForRepaint(repaintContainer, rect);
1845 rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
1850 } // namespace WebCore