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 "RenderListMarker.h"
29 #include "FontCascade.h"
30 #include "GraphicsContext.h"
31 #include "InlineElementBox.h"
32 #include "RenderLayer.h"
33 #include "RenderListItem.h"
34 #include "RenderView.h"
35 #include <wtf/IsoMallocInlines.h>
36 #include <wtf/StackStats.h>
37 #include <wtf/text/StringBuilder.h>
38 #include <wtf/unicode/CharacterNames.h>
43 using namespace Unicode;
45 WTF_MAKE_ISO_ALLOCATED_IMPL(RenderListMarker);
47 const int cMarkerPadding = 7;
49 enum SequenceType { NumericSequence, AlphabeticSequence };
51 static NEVER_INLINE void toRoman(StringBuilder& builder, int number, bool upper)
53 // FIXME: CSS3 describes how to make this work for much larger numbers,
54 // using overbars and special characters. It also specifies the characters
55 // in the range U+2160 to U+217F instead of standard ASCII ones.
56 ASSERT(number >= 1 && number <= 3999);
58 // Big enough to store largest roman number less than 3999 which
59 // is 3888 (MMMDCCCLXXXVIII)
60 const int lettersSize = 15;
61 LChar letters[lettersSize];
64 const LChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
65 const LChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
66 const LChar* digits = upper ? udigits : ldigits;
69 int num = number % 10;
71 for (int i = num % 5; i > 0; i--)
72 letters[lettersSize - ++length] = digits[d];
73 if (num >= 4 && num <= 8)
74 letters[lettersSize - ++length] = digits[d + 1];
76 letters[lettersSize - ++length] = digits[d + 2];
78 letters[lettersSize - ++length] = digits[d];
83 ASSERT(length <= lettersSize);
84 builder.append(&letters[lettersSize - length], length);
87 template <typename CharacterType>
88 static inline void toAlphabeticOrNumeric(StringBuilder& builder, int number, const CharacterType* sequence, unsigned sequenceSize, SequenceType type)
90 ASSERT(sequenceSize >= 2);
92 // Taking sizeof(number) in the expression below doesn't work with some compilers.
93 const int lettersSize = sizeof(int) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
95 CharacterType letters[lettersSize];
97 bool isNegativeNumber = false;
98 unsigned numberShadow = number;
99 if (type == AlphabeticSequence) {
102 } else if (number < 0) {
103 numberShadow = -number;
104 isNegativeNumber = true;
106 letters[lettersSize - 1] = sequence[numberShadow % sequenceSize];
109 if (type == AlphabeticSequence) {
110 while ((numberShadow /= sequenceSize) > 0) {
112 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
115 while ((numberShadow /= sequenceSize) > 0)
116 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
118 if (isNegativeNumber)
119 letters[lettersSize - ++length] = hyphenMinus;
121 ASSERT(length <= lettersSize);
122 builder.append(&letters[lettersSize - length], length);
125 template <typename CharacterType>
126 static NEVER_INLINE void toSymbolic(StringBuilder& builder, int number, const CharacterType* symbols, unsigned symbolsSize)
129 ASSERT(symbolsSize >= 1);
130 unsigned numberShadow = number;
133 // The asterisks list-style-type is the worst case; we show |numberShadow| asterisks.
134 builder.append(symbols[numberShadow % symbolsSize]);
135 unsigned numSymbols = numberShadow / symbolsSize;
137 builder.append(symbols[numberShadow % symbolsSize]);
140 template <typename CharacterType>
141 static NEVER_INLINE void toAlphabetic(StringBuilder& builder, int number, const CharacterType* alphabet, unsigned alphabetSize)
143 toAlphabeticOrNumeric(builder, number, alphabet, alphabetSize, AlphabeticSequence);
146 template <typename CharacterType>
147 static NEVER_INLINE void toNumeric(StringBuilder& builder, int number, const CharacterType* numerals, unsigned numeralsSize)
149 toAlphabeticOrNumeric(builder, number, numerals, numeralsSize, NumericSequence);
152 template <typename CharacterType, size_t size>
153 static inline void toAlphabetic(StringBuilder& builder, int number, const CharacterType(&alphabet)[size])
155 toAlphabetic(builder, number, alphabet, size);
158 template <typename CharacterType, size_t size>
159 static inline void toNumeric(StringBuilder& builder, int number, const CharacterType(&alphabet)[size])
161 toNumeric(builder, number, alphabet, size);
164 template <typename CharacterType, size_t size>
165 static inline void toSymbolic(StringBuilder& builder, int number, const CharacterType(&alphabet)[size])
167 toSymbolic(builder, number, alphabet, size);
170 static NEVER_INLINE int toHebrewUnder1000(int number, UChar letters[5])
172 // FIXME: CSS3 mentions various refinements not implemented here.
173 // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
174 ASSERT(number >= 0 && number < 1000);
176 int fourHundreds = number / 400;
177 for (int i = 0; i < fourHundreds; i++)
178 letters[length++] = 1511 + 3;
181 letters[length++] = 1511 + (number / 100) - 1;
183 if (number == 15 || number == 16) {
184 letters[length++] = 1487 + 9;
185 letters[length++] = 1487 + number - 9;
187 if (int tens = number / 10) {
188 static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
189 letters[length++] = hebrewTens[tens - 1];
191 if (int ones = number % 10)
192 letters[length++] = 1487 + ones;
198 static NEVER_INLINE void toHebrew(StringBuilder& builder, int number)
200 // FIXME: CSS3 mentions ways to make this work for much larger numbers.
201 ASSERT(number >= 0 && number <= 999999);
204 static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
205 builder.append(hebrewZero, 3);
209 const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
210 UChar letters[lettersSize];
216 length = toHebrewUnder1000(number / 1000, letters);
217 letters[length++] = '\'';
218 number = number % 1000;
220 length += toHebrewUnder1000(number, letters + length);
222 ASSERT(length <= lettersSize);
223 builder.append(letters, length);
226 static NEVER_INLINE int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
228 ASSERT(number >= 0 && number < 10000);
231 int lowerOffset = upper ? 0 : 0x0030;
233 if (int thousands = number / 1000) {
234 if (thousands == 7) {
235 letters[length++] = 0x0552 + lowerOffset;
237 letters[length++] = 0x0302;
239 letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
241 letters[length++] = 0x0302;
245 if (int hundreds = (number / 100) % 10) {
246 letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
248 letters[length++] = 0x0302;
251 if (int tens = (number / 10) % 10) {
252 letters[length++] = (0x053A - 1 + lowerOffset) + tens;
254 letters[length++] = 0x0302;
257 if (int ones = number % 10) {
258 letters[length++] = (0x531 - 1 + lowerOffset) + ones;
260 letters[length++] = 0x0302;
266 static NEVER_INLINE void toArmenian(StringBuilder& builder, int number, bool upper)
268 ASSERT(number >= 1 && number <= 99999999);
270 const int lettersSize = 18; // twice what toArmenianUnder10000 needs
271 UChar letters[lettersSize];
273 int length = toArmenianUnder10000(number / 10000, upper, true, letters);
274 length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
276 ASSERT(length <= lettersSize);
277 builder.append(letters, length);
280 static NEVER_INLINE void toGeorgian(StringBuilder& builder, int number)
282 ASSERT(number >= 1 && number <= 19999);
284 const int lettersSize = 5;
285 UChar letters[lettersSize];
290 letters[length++] = 0x10F5;
292 if (int thousands = (number / 1000) % 10) {
293 static const UChar georgianThousands[9] = {
294 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
296 letters[length++] = georgianThousands[thousands - 1];
299 if (int hundreds = (number / 100) % 10) {
300 static const UChar georgianHundreds[9] = {
301 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
303 letters[length++] = georgianHundreds[hundreds - 1];
306 if (int tens = (number / 10) % 10) {
307 static const UChar georgianTens[9] = {
308 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
310 letters[length++] = georgianTens[tens - 1];
313 if (int ones = number % 10) {
314 static const UChar georgianOnes[9] = {
315 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
317 letters[length++] = georgianOnes[ones - 1];
320 ASSERT(length <= lettersSize);
321 builder.append(letters, length);
324 // The table uses the order from the CSS3 specification:
325 // first 3 group markers, then 3 digit markers, then ten digits.
326 static NEVER_INLINE void toCJKIdeographic(StringBuilder& builder, int number, const UChar table[16])
330 enum AbstractCJKChar {
332 secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
333 secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
334 digit0, digit1, digit2, digit3, digit4,
335 digit5, digit6, digit7, digit8, digit9
339 builder.append(table[digit0 - 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 builder.append(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 StringBuilder builder;
610 // If the list-style-type, say hebrew, cannot represent |value| because it's outside
611 // its ordinal range then we fallback to some list style that can represent |value|.
612 switch (effectiveListMarkerType(type, value)) {
614 return emptyString();
617 static const LChar asterisksSymbols[1] = { 0x2A };
618 toSymbolic(builder, value, asterisksSymbols);
621 // We use the same characters for text security.
622 // See RenderText::setInternalString.
624 builder.append(whiteBullet);
627 builder.append(bullet);
630 static const UChar footnotesSymbols[4] = { 0x002A, 0x2051, 0x2020, 0x2021 };
631 toSymbolic(builder, value, footnotesSymbols);
635 // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
636 // instead, but I think this looks better.
637 builder.append(blackSquare);
640 case DecimalListStyle:
641 builder.appendNumber(value);
644 case DecimalLeadingZero:
645 if (value < -9 || value > 9) {
646 builder.appendNumber(value);
650 builder.appendLiteral("-0");
651 builder.appendNumber(-value); // -01 to -09
655 builder.appendNumber(value); // 00 to 09
659 static const UChar arabicIndicNumerals[10] = {
660 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669
662 toNumeric(builder, value, arabicIndicNumerals);
666 case BinaryListStyle: {
667 static const LChar binaryNumerals[2] = { '0', '1' };
668 toNumeric(builder, value, binaryNumerals);
673 static const UChar bengaliNumerals[10] = {
674 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF
676 toNumeric(builder, value, bengaliNumerals);
682 static const UChar khmerNumerals[10] = {
683 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9
685 toNumeric(builder, value, khmerNumerals);
689 static const UChar devanagariNumerals[10] = {
690 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F
692 toNumeric(builder, value, devanagariNumerals);
696 static const UChar gujaratiNumerals[10] = {
697 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF
699 toNumeric(builder, value, gujaratiNumerals);
703 static const UChar gurmukhiNumerals[10] = {
704 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F
706 toNumeric(builder, value, gurmukhiNumerals);
710 static const UChar kannadaNumerals[10] = {
711 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF
713 toNumeric(builder, value, kannadaNumerals);
716 case LowerHexadecimal: {
717 static const LChar lowerHexadecimalNumerals[16] = {
718 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
720 toNumeric(builder, value, lowerHexadecimalNumerals);
724 static const UChar laoNumerals[10] = {
725 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9
727 toNumeric(builder, value, laoNumerals);
731 static const UChar malayalamNumerals[10] = {
732 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F
734 toNumeric(builder, value, malayalamNumerals);
738 static const UChar mongolianNumerals[10] = {
739 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819
741 toNumeric(builder, value, mongolianNumerals);
745 static const UChar myanmarNumerals[10] = {
746 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049
748 toNumeric(builder, value, myanmarNumerals);
752 static const LChar octalNumerals[8] = {
753 '0', '1', '2', '3', '4', '5', '6', '7'
755 toNumeric(builder, value, octalNumerals);
759 static const UChar oriyaNumerals[10] = {
760 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F
762 toNumeric(builder, value, oriyaNumerals);
767 static const UChar urduNumerals[10] = {
768 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9
770 toNumeric(builder, value, urduNumerals);
774 static const UChar teluguNumerals[10] = {
775 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F
777 toNumeric(builder, value, teluguNumerals);
781 static const UChar tibetanNumerals[10] = {
782 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29
784 toNumeric(builder, value, tibetanNumerals);
788 static const UChar thaiNumerals[10] = {
789 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59
791 toNumeric(builder, value, thaiNumerals);
794 case UpperHexadecimal: {
795 static const LChar upperHexadecimalNumerals[16] = {
796 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
798 toNumeric(builder, value, upperHexadecimalNumerals);
804 static const LChar lowerLatinAlphabet[26] = {
805 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
806 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
808 toAlphabetic(builder, value, lowerLatinAlphabet);
813 static const LChar upperLatinAlphabet[26] = {
814 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
815 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
817 toAlphabetic(builder, value, upperLatinAlphabet);
821 static const UChar lowerGreekAlphabet[24] = {
822 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
823 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
824 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9
826 toAlphabetic(builder, value, lowerGreekAlphabet);
831 // FIXME: This table comes from the CSS3 draft, and is probably
832 // incorrect, given the comments in that draft.
833 static const UChar hiraganaAlphabet[48] = {
834 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
835 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
836 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
837 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
838 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
839 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093
841 toAlphabetic(builder, value, hiraganaAlphabet);
844 case HiraganaIroha: {
845 // FIXME: This table comes from the CSS3 draft, and is probably
846 // incorrect, given the comments in that draft.
847 static const UChar hiraganaIrohaAlphabet[47] = {
848 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061,
849 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F,
850 0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046,
851 0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075,
852 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
853 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059
855 toAlphabetic(builder, value, hiraganaIrohaAlphabet);
859 // FIXME: This table comes from the CSS3 draft, and is probably
860 // incorrect, given the comments in that draft.
861 static const UChar katakanaAlphabet[48] = {
862 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF,
863 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF,
864 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD,
865 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF,
866 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
867 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3
869 toAlphabetic(builder, value, katakanaAlphabet);
872 case KatakanaIroha: {
873 // FIXME: This table comes from the CSS3 draft, and is probably
874 // incorrect, given the comments in that draft.
875 static const UChar katakanaIrohaAlphabet[47] = {
876 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1,
877 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF,
878 0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6,
879 0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5,
880 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
881 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9
883 toAlphabetic(builder, value, katakanaIrohaAlphabet);
888 case EthiopicHalehameAaEt:
889 case EthiopicHalehameAaEr: {
890 static const UChar ethiopicHalehameAaErAlphabet[18] = {
891 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290,
892 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348
894 toAlphabetic(builder, value, ethiopicHalehameAaErAlphabet);
898 case EthiopicHalehameAmEt: {
899 static const UChar ethiopicHalehameAmEtAlphabet[33] = {
900 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
901 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8,
902 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320,
903 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
905 toAlphabetic(builder, value, ethiopicHalehameAmEtAlphabet);
909 case EthiopicAbegedeAmEt: {
910 static const UChar ethiopicAbegedeAmEtAlphabet[33] = {
911 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
912 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
913 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238,
914 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
916 toAlphabetic(builder, value, ethiopicAbegedeAmEtAlphabet);
919 case CjkEarthlyBranch: {
920 static const UChar cjkEarthlyBranchAlphabet[12] = {
921 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533,
922 0x9149, 0x620C, 0x4EA5
924 toAlphabetic(builder, value, cjkEarthlyBranchAlphabet);
927 case CjkHeavenlyStem: {
928 static const UChar cjkHeavenlyStemAlphabet[10] = {
929 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC,
932 toAlphabetic(builder, value, cjkHeavenlyStemAlphabet);
936 case EthiopicHalehameGez: {
937 static const UChar ethiopicHalehameGezAlphabet[26] = {
938 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1240, 0x1260,
939 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
940 0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
942 toAlphabetic(builder, value, ethiopicHalehameGezAlphabet);
945 case EthiopicAbegede:
946 case EthiopicAbegedeGez: {
947 static const UChar ethiopicAbegedeGezAlphabet[26] = {
948 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1200, 0x12C8, 0x12D8, 0x1210, 0x1320,
949 0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338,
950 0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350
952 toAlphabetic(builder, value, ethiopicAbegedeGezAlphabet);
955 case HangulConsonant: {
956 static const UChar hangulConsonantAlphabet[14] = {
957 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148,
958 0x314A, 0x314B, 0x314C, 0x314D, 0x314E
960 toAlphabetic(builder, value, hangulConsonantAlphabet);
964 static const UChar hangulAlphabet[14] = {
965 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790,
966 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558
968 toAlphabetic(builder, value, hangulAlphabet);
972 case EthiopicHalehameOmEt: {
973 static const UChar ethiopicHalehameOmEtAlphabet[25] = {
974 0x1200, 0x1208, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 0x1270,
975 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8,
976 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
978 toAlphabetic(builder, value, ethiopicHalehameOmEtAlphabet);
982 case EthiopicHalehameSidEt: {
983 static const UChar ethiopicHalehameSidEtAlphabet[26] = {
984 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
985 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0,
986 0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
988 toAlphabetic(builder, value, ethiopicHalehameSidEtAlphabet);
992 case EthiopicHalehameSoEt: {
993 static const UChar ethiopicHalehameSoEtAlphabet[22] = {
994 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
995 0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0,
996 0x1300, 0x1308, 0x1338, 0x1348
998 toAlphabetic(builder, value, ethiopicHalehameSoEtAlphabet);
1002 case EthiopicHalehameTig: {
1003 static const UChar ethiopicHalehameTigAlphabet[27] = {
1004 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
1005 0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
1006 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350
1008 toAlphabetic(builder, value, ethiopicHalehameTigAlphabet);
1012 case EthiopicHalehameTiEr: {
1013 static const UChar ethiopicHalehameTiErAlphabet[31] = {
1014 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1250,
1015 0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8,
1016 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328,
1017 0x1330, 0x1338, 0x1348, 0x1350
1019 toAlphabetic(builder, value, ethiopicHalehameTiErAlphabet);
1022 case TigrinyaErAbegede:
1023 case EthiopicAbegedeTiEr: {
1024 static const UChar ethiopicAbegedeTiErAlphabet[31] = {
1025 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
1026 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
1027 0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238,
1028 0x1270, 0x1278, 0x1330, 0x1350
1030 toAlphabetic(builder, value, ethiopicAbegedeTiErAlphabet);
1034 case EthiopicHalehameTiEt: {
1035 static const UChar ethiopicHalehameTiEtAlphabet[34] = {
1036 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
1037 0x1250, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8,
1038 0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308,
1039 0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
1041 toAlphabetic(builder, value, ethiopicHalehameTiEtAlphabet);
1044 case TigrinyaEtAbegede:
1045 case EthiopicAbegedeTiEt: {
1046 static const UChar ethiopicAbegedeTiEtAlphabet[34] = {
1047 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
1048 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
1049 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230,
1050 0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
1052 toAlphabetic(builder, value, ethiopicAbegedeTiEtAlphabet);
1056 static const UChar upperGreekAlphabet[24] = {
1057 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399,
1058 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3,
1059 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9
1061 toAlphabetic(builder, value, upperGreekAlphabet);
1064 case LowerNorwegian: {
1065 static const LChar lowerNorwegianAlphabet[29] = {
1066 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
1067 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
1068 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xE6,
1071 toAlphabetic(builder, value, lowerNorwegianAlphabet);
1074 case UpperNorwegian: {
1075 static const LChar upperNorwegianAlphabet[29] = {
1076 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
1077 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
1078 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xC6,
1081 toAlphabetic(builder, value, upperNorwegianAlphabet);
1084 case CJKIdeographic: {
1085 static const UChar traditionalChineseInformalTable[16] = {
1086 0x842C, 0x5104, 0x5146,
1087 0x5341, 0x767E, 0x5343,
1088 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB,
1089 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D
1091 toCJKIdeographic(builder, value, traditionalChineseInformalTable);
1096 toRoman(builder, value, false);
1099 toRoman(builder, value, true);
1104 // CSS3 says "armenian" means "lower-armenian".
1105 // But the CSS2.1 test suite contains uppercase test results for "armenian",
1106 // so we'll match the test suite.
1107 toArmenian(builder, value, true);
1110 toArmenian(builder, value, false);
1113 toGeorgian(builder, value);
1116 toHebrew(builder, value);
1120 return builder.toString();
1123 RenderListMarker::RenderListMarker(RenderListItem& listItem, RenderStyle&& style)
1124 : RenderBox(listItem.document(), WTFMove(style), 0)
1125 , m_listItem(listItem)
1127 // init RenderObject attributes
1128 setInline(true); // our object is Inline
1129 setReplaced(true); // pretend to be replaced
1132 RenderListMarker::~RenderListMarker()
1134 // Do not add any code here. Add it to willBeDestroyed() instead.
1137 void RenderListMarker::willBeDestroyed()
1140 m_image->removeClient(this);
1141 RenderBox::willBeDestroyed();
1144 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
1146 RenderBox::styleDidChange(diff, oldStyle);
1148 if (oldStyle && (style().listStylePosition() != oldStyle->listStylePosition() || style().listStyleType() != oldStyle->listStyleType()))
1149 setNeedsLayoutAndPrefWidthsRecalc();
1151 if (m_image != style().listStyleImage()) {
1153 m_image->removeClient(this);
1154 m_image = style().listStyleImage();
1156 m_image->addClient(this);
1160 std::unique_ptr<InlineElementBox> RenderListMarker::createInlineBox()
1162 auto box = RenderBox::createInlineBox();
1163 box->setBehavesLikeText(isText());
1167 bool RenderListMarker::isImage() const
1169 return m_image && !m_image->errorOccurred();
1172 LayoutRect RenderListMarker::localSelectionRect()
1174 InlineBox* box = inlineBoxWrapper();
1176 return LayoutRect(LayoutPoint(), size());
1177 const RootInlineBox& rootBox = m_inlineBoxWrapper->root();
1178 LayoutUnit newLogicalTop = rootBox.blockFlow().style().isFlippedBlocksWritingMode() ? m_inlineBoxWrapper->logicalBottom() - rootBox.selectionBottom() : rootBox.selectionTop() - m_inlineBoxWrapper->logicalTop();
1179 if (rootBox.blockFlow().style().isHorizontalWritingMode())
1180 return LayoutRect(0, newLogicalTop, width(), rootBox.selectionHeight());
1181 return LayoutRect(newLogicalTop, 0, rootBox.selectionHeight(), height());
1184 void RenderListMarker::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1186 if (paintInfo.phase != PaintPhaseForeground)
1189 if (style().visibility() != VISIBLE)
1192 LayoutPoint boxOrigin(paintOffset + location());
1193 LayoutRect overflowRect(visualOverflowRect());
1194 overflowRect.moveBy(boxOrigin);
1195 if (!paintInfo.rect.intersects(overflowRect))
1198 LayoutRect box(boxOrigin, size());
1200 auto markerRect = getRelativeMarkerRect();
1201 markerRect.moveBy(boxOrigin);
1202 if (markerRect.isEmpty())
1205 GraphicsContext& context = paintInfo.context();
1208 if (RefPtr<Image> markerImage = m_image->image(this, markerRect.size()))
1209 context.drawImage(*markerImage, markerRect);
1210 if (selectionState() != SelectionNone) {
1211 LayoutRect selRect = localSelectionRect();
1212 selRect.moveBy(boxOrigin);
1213 context.fillRect(snappedIntRect(selRect), m_listItem.selectionBackgroundColor());
1218 if (selectionState() != SelectionNone) {
1219 LayoutRect selRect = localSelectionRect();
1220 selRect.moveBy(boxOrigin);
1221 context.fillRect(snappedIntRect(selRect), m_listItem.selectionBackgroundColor());
1224 const Color color(style().visitedDependentColor(CSSPropertyColor));
1225 context.setStrokeColor(color);
1226 context.setStrokeStyle(SolidStroke);
1227 context.setStrokeThickness(1.0f);
1228 context.setFillColor(color);
1230 EListStyleType type = style().listStyleType();
1233 context.drawEllipse(markerRect);
1236 context.setFillColor(Color::transparent);
1237 context.drawEllipse(markerRect);
1240 context.drawRect(markerRect);
1246 case AmharicAbegede:
1249 case BinaryListStyle:
1252 case CJKIdeographic:
1253 case CjkEarthlyBranch:
1254 case CjkHeavenlyStem:
1255 case DecimalLeadingZero:
1256 case DecimalListStyle:
1259 case EthiopicAbegede:
1260 case EthiopicAbegedeAmEt:
1261 case EthiopicAbegedeGez:
1262 case EthiopicAbegedeTiEr:
1263 case EthiopicAbegedeTiEt:
1264 case EthiopicHalehameAaEr:
1265 case EthiopicHalehameAaEt:
1266 case EthiopicHalehameAmEt:
1267 case EthiopicHalehameGez:
1268 case EthiopicHalehameOmEt:
1269 case EthiopicHalehameSidEt:
1270 case EthiopicHalehameSoEt:
1271 case EthiopicHalehameTiEr:
1272 case EthiopicHalehameTiEt:
1273 case EthiopicHalehameTig:
1278 case HangulConsonant:
1290 case LowerHexadecimal:
1292 case LowerNorwegian:
1308 case TigrinyaErAbegede:
1310 case TigrinyaEtAbegede:
1314 case UpperHexadecimal:
1316 case UpperNorwegian:
1323 if (m_text.isEmpty())
1326 const FontCascade& font = style().fontCascade();
1327 TextRun textRun = RenderBlock::constructTextRun(m_text, style());
1329 GraphicsContextStateSaver stateSaver(context, false);
1330 if (!style().isHorizontalWritingMode()) {
1331 markerRect.moveBy(-boxOrigin);
1332 markerRect = markerRect.transposedRect();
1333 markerRect.moveBy(FloatPoint(box.x(), box.y() - logicalHeight()));
1335 context.translate(markerRect.x(), markerRect.maxY());
1336 context.rotate(static_cast<float>(deg2rad(90.)));
1337 context.translate(-markerRect.x(), -markerRect.maxY());
1340 FloatPoint textOrigin = FloatPoint(markerRect.x(), markerRect.y() + style().fontMetrics().ascent());
1341 textOrigin = roundPointToDevicePixels(LayoutPoint(textOrigin), document().deviceScaleFactor(), style().isLeftToRightDirection());
1343 if (type == Asterisks || type == Footnotes)
1344 context.drawText(font, textRun, textOrigin);
1346 const UChar suffix = listMarkerSuffix(type, m_listItem.value());
1348 // Text is not arbitrary. We can judge whether it's RTL from the first character,
1349 // and we only need to handle the direction U_RIGHT_TO_LEFT for now.
1350 bool textNeedsReversing = u_charDirection(m_text[0]) == U_RIGHT_TO_LEFT;
1352 if (textNeedsReversing) {
1353 unsigned length = m_text.length();
1354 StringBuilder buffer;
1355 buffer.reserveCapacity(length + 2);
1356 if (!style().isLeftToRightDirection()) {
1357 buffer.append(space);
1358 buffer.append(suffix);
1360 for (unsigned i = 0; i < length; ++i)
1361 buffer.append(m_text[length - i - 1]);
1362 if (style().isLeftToRightDirection()) {
1363 buffer.append(suffix);
1364 buffer.append(space);
1366 toDraw = buffer.toString();
1368 if (style().isLeftToRightDirection())
1369 toDraw = m_text + String(&suffix, 1) + String(&space, 1);
1371 toDraw = String(&space, 1) + String(&suffix, 1) + m_text;
1373 textRun.setText(StringView(toDraw));
1375 context.drawText(font, textRun, textOrigin);
1379 void RenderListMarker::layout()
1381 StackStats::LayoutCheckPoint layoutCheckPoint;
1382 ASSERT(needsLayout());
1384 LayoutUnit blockOffset;
1385 for (auto* box = parentBox(); box && box != &m_listItem; box = box->parentBox())
1386 blockOffset += box->logicalTop();
1387 if (style().isLeftToRightDirection())
1388 m_lineOffsetForListItem = m_listItem.logicalLeftOffsetForLine(blockOffset, DoNotIndentText, LayoutUnit());
1390 m_lineOffsetForListItem = m_listItem.logicalRightOffsetForLine(blockOffset, DoNotIndentText, LayoutUnit());
1393 updateMarginsAndContent();
1394 setWidth(m_image->imageSize(this, style().effectiveZoom()).width());
1395 setHeight(m_image->imageSize(this, style().effectiveZoom()).height());
1397 setLogicalWidth(minPreferredLogicalWidth());
1398 setLogicalHeight(style().fontMetrics().height());
1404 Length startMargin = style().marginStart();
1405 Length endMargin = style().marginEnd();
1406 if (startMargin.isFixed())
1407 setMarginStart(startMargin.value());
1408 if (endMargin.isFixed())
1409 setMarginEnd(endMargin.value());
1414 void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*)
1416 // A list marker can't have a background or border image, so no need to call the base class method.
1417 if (o != m_image->data())
1420 if (width() != m_image->imageSize(this, style().effectiveZoom()).width() || height() != m_image->imageSize(this, style().effectiveZoom()).height() || m_image->errorOccurred())
1421 setNeedsLayoutAndPrefWidthsRecalc();
1426 void RenderListMarker::updateMarginsAndContent()
1432 void RenderListMarker::updateContent()
1434 // FIXME: This if-statement is just a performance optimization, but it's messy to use the preferredLogicalWidths dirty bit for this.
1435 // It's unclear if this is a premature optimization.
1436 if (!preferredLogicalWidthsDirty())
1439 m_text = emptyString();
1442 // FIXME: This is a somewhat arbitrary width. Generated images for markers really won't become particularly useful
1443 // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box.
1444 LayoutUnit bulletWidth = style().fontMetrics().ascent() / LayoutUnit(2);
1445 LayoutSize defaultBulletSize(bulletWidth, bulletWidth);
1446 LayoutSize imageSize = calculateImageIntrinsicDimensions(m_image.get(), defaultBulletSize, DoNotScaleByEffectiveZoom);
1447 m_image->setContainerContextForRenderer(*this, imageSize, style().effectiveZoom());
1451 EListStyleType type = style().listStyleType();
1458 m_text = listMarkerText(type, 0); // value is ignored for these types
1464 case AmharicAbegede:
1467 case BinaryListStyle:
1470 case CJKIdeographic:
1471 case CjkEarthlyBranch:
1472 case CjkHeavenlyStem:
1473 case DecimalLeadingZero:
1474 case DecimalListStyle:
1477 case EthiopicAbegede:
1478 case EthiopicAbegedeAmEt:
1479 case EthiopicAbegedeGez:
1480 case EthiopicAbegedeTiEr:
1481 case EthiopicAbegedeTiEt:
1482 case EthiopicHalehameAaEr:
1483 case EthiopicHalehameAaEt:
1484 case EthiopicHalehameAmEt:
1485 case EthiopicHalehameGez:
1486 case EthiopicHalehameOmEt:
1487 case EthiopicHalehameSidEt:
1488 case EthiopicHalehameSoEt:
1489 case EthiopicHalehameTiEr:
1490 case EthiopicHalehameTiEt:
1491 case EthiopicHalehameTig:
1496 case HangulConsonant:
1508 case LowerHexadecimal:
1510 case LowerNorwegian:
1526 case TigrinyaErAbegede:
1528 case TigrinyaEtAbegede:
1532 case UpperHexadecimal:
1534 case UpperNorwegian:
1537 m_text = listMarkerText(type, m_listItem.value());
1542 void RenderListMarker::computePreferredLogicalWidths()
1544 ASSERT(preferredLogicalWidthsDirty());
1548 LayoutSize imageSize = LayoutSize(m_image->imageSize(this, style().effectiveZoom()));
1549 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = style().isHorizontalWritingMode() ? imageSize.width() : imageSize.height();
1550 setPreferredLogicalWidthsDirty(false);
1555 const FontCascade& font = style().fontCascade();
1557 LayoutUnit logicalWidth = 0;
1558 EListStyleType type = style().listStyleType();
1564 TextRun run = RenderBlock::constructTextRun(m_text, style());
1565 logicalWidth = font.width(run); // no suffix for these types
1571 logicalWidth = (font.fontMetrics().ascent() * 2 / 3 + 1) / 2 + 2;
1575 case AmharicAbegede:
1578 case BinaryListStyle:
1581 case CJKIdeographic:
1582 case CjkEarthlyBranch:
1583 case CjkHeavenlyStem:
1584 case DecimalLeadingZero:
1585 case DecimalListStyle:
1588 case EthiopicAbegede:
1589 case EthiopicAbegedeAmEt:
1590 case EthiopicAbegedeGez:
1591 case EthiopicAbegedeTiEr:
1592 case EthiopicAbegedeTiEt:
1593 case EthiopicHalehameAaEr:
1594 case EthiopicHalehameAaEt:
1595 case EthiopicHalehameAmEt:
1596 case EthiopicHalehameGez:
1597 case EthiopicHalehameOmEt:
1598 case EthiopicHalehameSidEt:
1599 case EthiopicHalehameSoEt:
1600 case EthiopicHalehameTiEr:
1601 case EthiopicHalehameTiEt:
1602 case EthiopicHalehameTig:
1607 case HangulConsonant:
1619 case LowerHexadecimal:
1621 case LowerNorwegian:
1637 case TigrinyaErAbegede:
1639 case TigrinyaEtAbegede:
1643 case UpperHexadecimal:
1645 case UpperNorwegian:
1648 if (m_text.isEmpty())
1651 TextRun run = RenderBlock::constructTextRun(m_text, style());
1652 LayoutUnit itemWidth = font.width(run);
1653 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem.value()), ' ' };
1654 LayoutUnit suffixSpaceWidth = font.width(RenderBlock::constructTextRun(suffixSpace, 2, style()));
1655 logicalWidth = itemWidth + suffixSpaceWidth;
1660 m_minPreferredLogicalWidth = logicalWidth;
1661 m_maxPreferredLogicalWidth = logicalWidth;
1663 setPreferredLogicalWidthsDirty(false);
1668 void RenderListMarker::updateMargins()
1670 const FontMetrics& fontMetrics = style().fontMetrics();
1672 LayoutUnit marginStart = 0;
1673 LayoutUnit marginEnd = 0;
1677 marginEnd = cMarkerPadding;
1678 else switch (style().listStyleType()) {
1683 marginEnd = fontMetrics.ascent() - minPreferredLogicalWidth() + 1;
1689 if (style().isLeftToRightDirection()) {
1691 marginStart = -minPreferredLogicalWidth() - cMarkerPadding;
1693 int offset = fontMetrics.ascent() * 2 / 3;
1694 switch (style().listStyleType()) {
1698 marginStart = -offset - cMarkerPadding - 1;
1703 marginStart = m_text.isEmpty() ? LayoutUnit() : -minPreferredLogicalWidth() - offset / 2;
1706 marginEnd = -marginStart - minPreferredLogicalWidth();
1709 marginEnd = cMarkerPadding;
1711 int offset = fontMetrics.ascent() * 2 / 3;
1712 switch (style().listStyleType()) {
1716 marginEnd = offset + cMarkerPadding + 1 - minPreferredLogicalWidth();
1721 marginEnd = m_text.isEmpty() ? 0 : offset / 2;
1724 marginStart = -marginEnd - minPreferredLogicalWidth();
1729 mutableStyle().setMarginStart(Length(marginStart, Fixed));
1730 mutableStyle().setMarginEnd(Length(marginEnd, Fixed));
1733 LayoutUnit RenderListMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1736 return m_listItem.lineHeight(firstLine, direction, PositionOfInteriorLineBoxes);
1737 return RenderBox::lineHeight(firstLine, direction, linePositionMode);
1740 int RenderListMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1743 return m_listItem.baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes);
1744 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1747 String RenderListMarker::suffix() const
1749 EListStyleType type = style().listStyleType();
1750 const UChar suffix = listMarkerSuffix(type, m_listItem.value());
1755 // If the suffix is not ' ', an extra space is needed
1757 if (style().isLeftToRightDirection()) {
1765 return String(data, 2);
1768 bool RenderListMarker::isInside() const
1770 return m_listItem.notInList() || style().listStylePosition() == INSIDE;
1773 FloatRect RenderListMarker::getRelativeMarkerRect()
1776 return FloatRect(0, 0, m_image->imageSize(this, style().effectiveZoom()).width(), m_image->imageSize(this, style().effectiveZoom()).height());
1778 FloatRect relativeRect;
1779 EListStyleType type = style().listStyleType();
1783 const FontCascade& font = style().fontCascade();
1784 TextRun run = RenderBlock::constructTextRun(m_text, style());
1785 relativeRect = FloatRect(0, 0, font.width(run), font.fontMetrics().height());
1791 // FIXME: Are these particular rounding rules necessary?
1792 const FontMetrics& fontMetrics = style().fontMetrics();
1793 int ascent = fontMetrics.ascent();
1794 int bulletWidth = (ascent * 2 / 3 + 1) / 2;
1795 relativeRect = FloatRect(1, 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
1802 case AmharicAbegede:
1805 case BinaryListStyle:
1808 case CJKIdeographic:
1809 case CjkEarthlyBranch:
1810 case CjkHeavenlyStem:
1811 case DecimalLeadingZero:
1812 case DecimalListStyle:
1815 case EthiopicAbegede:
1816 case EthiopicAbegedeAmEt:
1817 case EthiopicAbegedeGez:
1818 case EthiopicAbegedeTiEr:
1819 case EthiopicAbegedeTiEt:
1820 case EthiopicHalehameAaEr:
1821 case EthiopicHalehameAaEt:
1822 case EthiopicHalehameAmEt:
1823 case EthiopicHalehameGez:
1824 case EthiopicHalehameOmEt:
1825 case EthiopicHalehameSidEt:
1826 case EthiopicHalehameSoEt:
1827 case EthiopicHalehameTiEr:
1828 case EthiopicHalehameTiEt:
1829 case EthiopicHalehameTig:
1834 case HangulConsonant:
1846 case LowerHexadecimal:
1848 case LowerNorwegian:
1864 case TigrinyaErAbegede:
1866 case TigrinyaEtAbegede:
1870 case UpperHexadecimal:
1872 case UpperNorwegian:
1875 if (m_text.isEmpty())
1877 const FontCascade& font = style().fontCascade();
1878 TextRun run = RenderBlock::constructTextRun(m_text, style());
1879 float itemWidth = font.width(run);
1880 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem.value()), ' ' };
1881 float suffixSpaceWidth = font.width(RenderBlock::constructTextRun(suffixSpace, 2, style()));
1882 relativeRect = FloatRect(0, 0, itemWidth + suffixSpaceWidth, font.fontMetrics().height());
1885 if (!style().isHorizontalWritingMode()) {
1886 relativeRect = relativeRect.transposedRect();
1887 relativeRect.setX(width() - relativeRect.x() - relativeRect.width());
1890 return relativeRect;
1893 void RenderListMarker::setSelectionState(SelectionState state)
1895 // The selection state for our containing block hierarchy is updated by the base class call.
1896 RenderBox::setSelectionState(state);
1898 if (m_inlineBoxWrapper && canUpdateSelectionOnRootLineBoxes())
1899 m_inlineBoxWrapper->root().setHasSelectedChildren(state != SelectionNone);
1902 LayoutRect RenderListMarker::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent)
1904 ASSERT(!needsLayout());
1906 if (selectionState() == SelectionNone || !inlineBoxWrapper())
1907 return LayoutRect();
1909 RootInlineBox& rootBox = inlineBoxWrapper()->root();
1910 LayoutRect rect(0, rootBox.selectionTop() - y(), width(), rootBox.selectionHeight());
1912 if (clipToVisibleContent)
1913 return computeRectForRepaint(rect, repaintContainer);
1914 return localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
1917 } // namespace WebCore