1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. 5 * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) 6 * Copyright (C) 2010 Daniel Bates (dbates@intudata.com) 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 * 23 */ 24 25 #include "config.h" 26 #include "RenderListMarker.h" 27 28 #include "Document.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> 39 40 41 namespace WebCore { 42 using namespace WTF; 43 using namespace Unicode; 44 45 WTF_MAKE_ISO_ALLOCATED_IMPL(RenderListMarker); 46 47 const int cMarkerPadding = 7; 48 49 enum SequenceType { NumericSequence, AlphabeticSequence }; 50 51 static NEVER_INLINE void toRoman(StringBuilder& builder, int number, bool upper) 52 { 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); 57 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]; 62 63 int length = 0; 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; 67 int d = 0; 68 do { 69 int num = number % 10; 70 if (num % 5 < 4) 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]; 75 if (num == 9) 76 letters[lettersSize - ++length] = digits[d + 2]; 77 if (num % 5 == 4) 78 letters[lettersSize - ++length] = digits[d]; 79 number /= 10; 80 d += 2; 81 } while (number); 82 83 ASSERT(length <= lettersSize); 84 builder.append(&letters[lettersSize - length], length); 85 } 86 87 template <typename CharacterType> 88 static inline void toAlphabeticOrNumeric(StringBuilder& builder, int number, const CharacterType* sequence, unsigned sequenceSize, SequenceType type) 89 { 90 ASSERT(sequenceSize >= 2); 91 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. 94 95 CharacterType letters[lettersSize]; 96 97 bool isNegativeNumber = false; 98 unsigned numberShadow = number; 99 if (type == AlphabeticSequence) { 100 ASSERT(number > 0); 101 --numberShadow; 102 } else if (number < 0) { 103 numberShadow = -number; 104 isNegativeNumber = true; 105 } 106 letters[lettersSize - 1] = sequence[numberShadow % sequenceSize]; 107 int length = 1; 108 109 if (type == AlphabeticSequence) { 110 while ((numberShadow /= sequenceSize) > 0) { 111 --numberShadow; 112 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize]; 113 } 114 } else { 115 while ((numberShadow /= sequenceSize) > 0) 116 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize]; 117 } 118 if (isNegativeNumber) 119 letters[lettersSize - ++length] = hyphenMinus; 120 121 ASSERT(length <= lettersSize); 122 builder.append(&letters[lettersSize - length], length); 123 } 124 125 template <typename CharacterType> 126 static NEVER_INLINE void toSymbolic(StringBuilder& builder, int number, const CharacterType* symbols, unsigned symbolsSize) 127 { 128 ASSERT(number > 0); 129 ASSERT(symbolsSize >= 1); 130 unsigned numberShadow = number; 131 --numberShadow; 132 133 // The asterisks list-style-type is the worst case; we show |numberShadow| asterisks. 134 builder.append(symbols[numberShadow % symbolsSize]); 135 unsigned numSymbols = numberShadow / symbolsSize; 136 while (numSymbols--) 137 builder.append(symbols[numberShadow % symbolsSize]); 138 } 139 140 template <typename CharacterType> 141 static NEVER_INLINE void toAlphabetic(StringBuilder& builder, int number, const CharacterType* alphabet, unsigned alphabetSize) 142 { 143 toAlphabeticOrNumeric(builder, number, alphabet, alphabetSize, AlphabeticSequence); 144 } 145 146 template <typename CharacterType> 147 static NEVER_INLINE void toNumeric(StringBuilder& builder, int number, const CharacterType* numerals, unsigned numeralsSize) 148 { 149 toAlphabeticOrNumeric(builder, number, numerals, numeralsSize, NumericSequence); 150 } 151 152 template <typename CharacterType, size_t size> 153 static inline void toAlphabetic(StringBuilder& builder, int number, const CharacterType(&alphabet)[size]) 154 { 155 toAlphabetic(builder, number, alphabet, size); 156 } 157 158 template <typename CharacterType, size_t size> 159 static inline void toNumeric(StringBuilder& builder, int number, const CharacterType(&alphabet)[size]) 160 { 161 toNumeric(builder, number, alphabet, size); 162 } 163 164 template <typename CharacterType, size_t size> 165 static inline void toSymbolic(StringBuilder& builder, int number, const CharacterType(&alphabet)[size]) 166 { 167 toSymbolic(builder, number, alphabet, size); 168 } 169 170 static NEVER_INLINE int toHebrewUnder1000(int number, UChar letters[5]) 171 { 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); 175 int length = 0; 176 int fourHundreds = number / 400; 177 for (int i = 0; i < fourHundreds; i++) 178 letters[length++] = 1511 + 3; 179 number %= 400; 180 if (number / 100) 181 letters[length++] = 1511 + (number / 100) - 1; 182 number %= 100; 183 if (number == 15 || number == 16) { 184 letters[length++] = 1487 + 9; 185 letters[length++] = 1487 + number - 9; 186 } else { 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]; 190 } 191 if (int ones = number % 10) 192 letters[length++] = 1487 + ones; 193 } 194 ASSERT(length <= 5); 195 return length; 196 } 197 198 static NEVER_INLINE void toHebrew(StringBuilder& builder, int number) 199 { 200 // FIXME: CSS3 mentions ways to make this work for much larger numbers. 201 ASSERT(number >= 0 && number <= 999999); 202 203 if (number == 0) { 204 static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 }; 205 builder.append(hebrewZero, 3); 206 return; 207 } 208 209 const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between 210 UChar letters[lettersSize]; 211 212 int length; 213 if (number < 1000) 214 length = 0; 215 else { 216 length = toHebrewUnder1000(number / 1000, letters); 217 letters[length++] = '\''; 218 number = number % 1000; 219 } 220 length += toHebrewUnder1000(number, letters + length); 221 222 ASSERT(length <= lettersSize); 223 builder.append(letters, length); 224 } 225 226 static NEVER_INLINE int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9]) 227 { 228 ASSERT(number >= 0 && number < 10000); 229 int length = 0; 230 231 int lowerOffset = upper ? 0 : 0x0030; 232 233 if (int thousands = number / 1000) { 234 if (thousands == 7) { 235 letters[length++] = 0x0552 + lowerOffset; 236 if (addCircumflex) 237 letters[length++] = 0x0302; 238 } else { 239 letters[length++] = (0x054C - 1 + lowerOffset) + thousands; 240 if (addCircumflex) 241 letters[length++] = 0x0302; 242 } 243 } 244 245 if (int hundreds = (number / 100) % 10) { 246 letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds; 247 if (addCircumflex) 248 letters[length++] = 0x0302; 249 } 250 251 if (int tens = (number / 10) % 10) { 252 letters[length++] = (0x053A - 1 + lowerOffset) + tens; 253 if (addCircumflex) 254 letters[length++] = 0x0302; 255 } 256 257 if (int ones = number % 10) { 258 letters[length++] = (0x531 - 1 + lowerOffset) + ones; 259 if (addCircumflex) 260 letters[length++] = 0x0302; 261 } 262 263 return length; 264 } 265 266 static NEVER_INLINE void toArmenian(StringBuilder& builder, int number, bool upper) 267 { 268 ASSERT(number >= 1 && number <= 99999999); 269 270 const int lettersSize = 18; // twice what toArmenianUnder10000 needs 271 UChar letters[lettersSize]; 272 273 int length = toArmenianUnder10000(number / 10000, upper, true, letters); 274 length += toArmenianUnder10000(number % 10000, upper, false, letters + length); 275 276 ASSERT(length <= lettersSize); 277 builder.append(letters, length); 278 } 279 280 static NEVER_INLINE void toGeorgian(StringBuilder& builder, int number) 281 { 282 ASSERT(number >= 1 && number <= 19999); 283 284 const int lettersSize = 5; 285 UChar letters[lettersSize]; 286 287 int length = 0; 288 289 if (number > 9999) 290 letters[length++] = 0x10F5; 291 292 if (int thousands = (number / 1000) % 10) { 293 static const UChar georgianThousands[9] = { 294 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0 295 }; 296 letters[length++] = georgianThousands[thousands - 1]; 297 } 298 299 if (int hundreds = (number / 100) % 10) { 300 static const UChar georgianHundreds[9] = { 301 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8 302 }; 303 letters[length++] = georgianHundreds[hundreds - 1]; 304 } 305 306 if (int tens = (number / 10) % 10) { 307 static const UChar georgianTens[9] = { 308 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF 309 }; 310 letters[length++] = georgianTens[tens - 1]; 311 } 312 313 if (int ones = number % 10) { 314 static const UChar georgianOnes[9] = { 315 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7 316 }; 317 letters[length++] = georgianOnes[ones - 1]; 318 } 319 320 ASSERT(length <= lettersSize); 321 builder.append(letters, length); 322 } 323 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]) 327 { 328 ASSERT(number >= 0); 329 330 enum AbstractCJKChar { 331 noChar, 332 secondGroupMarker, thirdGroupMarker, fourthGroupMarker, 333 secondDigitMarker, thirdDigitMarker, fourthDigitMarker, 334 digit0, digit1, digit2, digit3, digit4, 335 digit5, digit6, digit7, digit8, digit9 336 }; 337 338 if (number == 0) { 339 builder.append(table[digit0 - 1]); 340 return; 341 } 342 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 }; 346 347 for (int i = 0; i < 4; ++i) { 348 int groupValue = number % 10000; 349 number /= 10000; 350 351 // Process least-significant group first, but put it in the buffer last. 352 AbstractCJKChar* group = &buffer[(3 - i) * groupLength]; 353 354 if (groupValue && i) 355 group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i); 356 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); 362 if (digitValue) 363 group[5] = secondDigitMarker; 364 } 365 if (number != 0 || groupValue > 99) { 366 int digitValue = ((groupValue / 100) % 10); 367 group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue); 368 if (digitValue) 369 group[3] = thirdDigitMarker; 370 } 371 if (number != 0 || groupValue > 999) { 372 int digitValue = groupValue / 1000; 373 group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue); 374 if (digitValue) 375 group[1] = fourthDigitMarker; 376 } 377 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); 382 group[4] = noChar; 383 } 384 385 if (number == 0) 386 break; 387 } 388 389 // Convert into characters, omitting consecutive runs of digit0 and 390 // any trailing digit0. 391 int length = 0; 392 UChar characters[bufferLength]; 393 AbstractCJKChar last = noChar; 394 for (int i = 0; i < bufferLength; ++i) { 395 AbstractCJKChar a = buffer[i]; 396 if (a != noChar) { 397 if (a != digit0 || last != digit0) 398 characters[length++] = table[a - 1]; 399 last = a; 400 } 401 } 402 if (last == digit0) 403 --length; 404 405 builder.append(characters, length); 406 } 407 408 static EListStyleType effectiveListMarkerType(EListStyleType type, int value) 409 { 410 // Note, the following switch statement has been explicitly grouped 411 // by list-style-type ordinal range. 412 switch (type) { 413 case ArabicIndic: 414 case Bengali: 415 case BinaryListStyle: 416 case Cambodian: 417 case Circle: 418 case DecimalLeadingZero: 419 case DecimalListStyle: 420 case Devanagari: 421 case Disc: 422 case Gujarati: 423 case Gurmukhi: 424 case Kannada: 425 case Khmer: 426 case Lao: 427 case LowerHexadecimal: 428 case Malayalam: 429 case Mongolian: 430 case Myanmar: 431 case NoneListStyle: 432 case Octal: 433 case Oriya: 434 case Persian: 435 case Square: 436 case Telugu: 437 case Thai: 438 case Tibetan: 439 case UpperHexadecimal: 440 case Urdu: 441 return type; // Can represent all ordinals. 442 case Armenian: 443 return (value < 1 || value > 99999999) ? DecimalListStyle : type; 444 case CJKIdeographic: 445 return (value < 0) ? DecimalListStyle : type; 446 case Georgian: 447 return (value < 1 || value > 19999) ? DecimalListStyle : type; 448 case Hebrew: 449 return (value < 0 || value > 999999) ? DecimalListStyle : type; 450 case LowerRoman: 451 case UpperRoman: 452 return (value < 1 || value > 3999) ? DecimalListStyle : type; 453 case Afar: 454 case Amharic: 455 case AmharicAbegede: 456 case Asterisks: 457 case CjkEarthlyBranch: 458 case CjkHeavenlyStem: 459 case Ethiopic: 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: 475 case Footnotes: 476 case Hangul: 477 case HangulConsonant: 478 case Hiragana: 479 case HiraganaIroha: 480 case Katakana: 481 case KatakanaIroha: 482 case LowerAlpha: 483 case LowerArmenian: 484 case LowerGreek: 485 case LowerLatin: 486 case LowerNorwegian: 487 case Oromo: 488 case Sidama: 489 case Somali: 490 case Tigre: 491 case TigrinyaEr: 492 case TigrinyaErAbegede: 493 case TigrinyaEt: 494 case TigrinyaEtAbegede: 495 case UpperAlpha: 496 case UpperArmenian: 497 case UpperGreek: 498 case UpperLatin: 499 case UpperNorwegian: 500 return (value < 1) ? DecimalListStyle : type; 501 } 502 503 ASSERT_NOT_REACHED(); 504 return type; 505 } 506 507 static UChar listMarkerSuffix(EListStyleType type, int value) 508 { 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); 512 513 // Note, the following switch statement has been explicitly 514 // grouped by list-style-type suffix. 515 switch (effectiveType) { 516 case Asterisks: 517 case Circle: 518 case Disc: 519 case Footnotes: 520 case NoneListStyle: 521 case Square: 522 return ' '; 523 case Afar: 524 case Amharic: 525 case AmharicAbegede: 526 case Ethiopic: 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: 542 case Oromo: 543 case Sidama: 544 case Somali: 545 case Tigre: 546 case TigrinyaEr: 547 case TigrinyaErAbegede: 548 case TigrinyaEt: 549 case TigrinyaEtAbegede: 550 return ethiopicPrefaceColon; 551 case Armenian: 552 case ArabicIndic: 553 case Bengali: 554 case BinaryListStyle: 555 case Cambodian: 556 case CJKIdeographic: 557 case CjkEarthlyBranch: 558 case CjkHeavenlyStem: 559 case DecimalLeadingZero: 560 case DecimalListStyle: 561 case Devanagari: 562 case Georgian: 563 case Gujarati: 564 case Gurmukhi: 565 case Hangul: 566 case HangulConsonant: 567 case Hebrew: 568 case Hiragana: 569 case HiraganaIroha: 570 case Kannada: 571 case Katakana: 572 case KatakanaIroha: 573 case Khmer: 574 case Lao: 575 case LowerAlpha: 576 case LowerArmenian: 577 case LowerGreek: 578 case LowerHexadecimal: 579 case LowerLatin: 580 case LowerNorwegian: 581 case LowerRoman: 582 case Malayalam: 583 case Mongolian: 584 case Myanmar: 585 case Octal: 586 case Oriya: 587 case Persian: 588 case Telugu: 589 case Thai: 590 case Tibetan: 591 case UpperAlpha: 592 case UpperArmenian: 593 case UpperGreek: 594 case UpperHexadecimal: 595 case UpperLatin: 596 case UpperNorwegian: 597 case UpperRoman: 598 case Urdu: 599 return '.'; 600 } 601 602 ASSERT_NOT_REACHED(); 603 return '.'; 604 } 605 606 String listMarkerText(EListStyleType type, int value) 607 { 608 StringBuilder builder; 609 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)) { 613 case NoneListStyle: 614 return emptyString(); 615 616 case Asterisks: { 617 static const LChar asterisksSymbols[1] = { 0x2A }; 618 toSymbolic(builder, value, asterisksSymbols); 619 break; 620 } 621 // We use the same characters for text security. 622 // See RenderText::setInternalString. 623 case Circle: 624 builder.append(whiteBullet); 625 break; 626 case Disc: 627 builder.append(bullet); 628 break; 629 case Footnotes: { 630 static const UChar footnotesSymbols[4] = { 0x002A, 0x2051, 0x2020, 0x2021 }; 631 toSymbolic(builder, value, footnotesSymbols); 632 break; 633 } 634 case Square: 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); 638 break; 639 640 case DecimalListStyle: 641 builder.appendNumber(value); 642 break; 643 644 case DecimalLeadingZero: 645 if (value < -9 || value > 9) { 646 builder.appendNumber(value); 647 break; 648 } 649 if (value < 0) { 650 builder.appendLiteral("-0"); 651 builder.appendNumber(-value); // -01 to -09 652 break; 653 } 654 builder.append('0'); 655 builder.appendNumber(value); // 00 to 09 656 break; 657 658 case ArabicIndic: { 659 static const UChar arabicIndicNumerals[10] = { 660 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669 661 }; 662 toNumeric(builder, value, arabicIndicNumerals); 663 break; 664 } 665 666 case BinaryListStyle: { 667 static const LChar binaryNumerals[2] = { '0', '1' }; 668 toNumeric(builder, value, binaryNumerals); 669 break; 670 } 671 672 case Bengali: { 673 static const UChar bengaliNumerals[10] = { 674 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF 675 }; 676 toNumeric(builder, value, bengaliNumerals); 677 break; 678 } 679 680 case Cambodian: 681 case Khmer: { 682 static const UChar khmerNumerals[10] = { 683 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9 684 }; 685 toNumeric(builder, value, khmerNumerals); 686 break; 687 } 688 case Devanagari: { 689 static const UChar devanagariNumerals[10] = { 690 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F 691 }; 692 toNumeric(builder, value, devanagariNumerals); 693 break; 694 } 695 case Gujarati: { 696 static const UChar gujaratiNumerals[10] = { 697 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF 698 }; 699 toNumeric(builder, value, gujaratiNumerals); 700 break; 701 } 702 case Gurmukhi: { 703 static const UChar gurmukhiNumerals[10] = { 704 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F 705 }; 706 toNumeric(builder, value, gurmukhiNumerals); 707 break; 708 } 709 case Kannada: { 710 static const UChar kannadaNumerals[10] = { 711 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF 712 }; 713 toNumeric(builder, value, kannadaNumerals); 714 break; 715 } 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' 719 }; 720 toNumeric(builder, value, lowerHexadecimalNumerals); 721 break; 722 } 723 case Lao: { 724 static const UChar laoNumerals[10] = { 725 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9 726 }; 727 toNumeric(builder, value, laoNumerals); 728 break; 729 } 730 case Malayalam: { 731 static const UChar malayalamNumerals[10] = { 732 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F 733 }; 734 toNumeric(builder, value, malayalamNumerals); 735 break; 736 } 737 case Mongolian: { 738 static const UChar mongolianNumerals[10] = { 739 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819 740 }; 741 toNumeric(builder, value, mongolianNumerals); 742 break; 743 } 744 case Myanmar: { 745 static const UChar myanmarNumerals[10] = { 746 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049 747 }; 748 toNumeric(builder, value, myanmarNumerals); 749 break; 750 } 751 case Octal: { 752 static const LChar octalNumerals[8] = { 753 '0', '1', '2', '3', '4', '5', '6', '7' 754 }; 755 toNumeric(builder, value, octalNumerals); 756 break; 757 } 758 case Oriya: { 759 static const UChar oriyaNumerals[10] = { 760 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F 761 }; 762 toNumeric(builder, value, oriyaNumerals); 763 break; 764 } 765 case Persian: 766 case Urdu: { 767 static const UChar urduNumerals[10] = { 768 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9 769 }; 770 toNumeric(builder, value, urduNumerals); 771 break; 772 } 773 case Telugu: { 774 static const UChar teluguNumerals[10] = { 775 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F 776 }; 777 toNumeric(builder, value, teluguNumerals); 778 break; 779 } 780 case Tibetan: { 781 static const UChar tibetanNumerals[10] = { 782 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29 783 }; 784 toNumeric(builder, value, tibetanNumerals); 785 break; 786 } 787 case Thai: { 788 static const UChar thaiNumerals[10] = { 789 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59 790 }; 791 toNumeric(builder, value, thaiNumerals); 792 break; 793 } 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' 797 }; 798 toNumeric(builder, value, upperHexadecimalNumerals); 799 break; 800 } 801 802 case LowerAlpha: 803 case LowerLatin: { 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' 807 }; 808 toAlphabetic(builder, value, lowerLatinAlphabet); 809 break; 810 } 811 case UpperAlpha: 812 case UpperLatin: { 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' 816 }; 817 toAlphabetic(builder, value, upperLatinAlphabet); 818 break; 819 } 820 case LowerGreek: { 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 825 }; 826 toAlphabetic(builder, value, lowerGreekAlphabet); 827 break; 828 } 829 830 case Hiragana: { 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 840 }; 841 toAlphabetic(builder, value, hiraganaAlphabet); 842 break; 843 } 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 854 }; 855 toAlphabetic(builder, value, hiraganaIrohaAlphabet); 856 break; 857 } 858 case Katakana: { 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 868 }; 869 toAlphabetic(builder, value, katakanaAlphabet); 870 break; 871 } 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 882 }; 883 toAlphabetic(builder, value, katakanaIrohaAlphabet); 884 break; 885 } 886 887 case Afar: 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 893 }; 894 toAlphabetic(builder, value, ethiopicHalehameAaErAlphabet); 895 break; 896 } 897 case Amharic: 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 904 }; 905 toAlphabetic(builder, value, ethiopicHalehameAmEtAlphabet); 906 break; 907 } 908 case AmharicAbegede: 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 915 }; 916 toAlphabetic(builder, value, ethiopicAbegedeAmEtAlphabet); 917 break; 918 } 919 case CjkEarthlyBranch: { 920 static const UChar cjkEarthlyBranchAlphabet[12] = { 921 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533, 922 0x9149, 0x620C, 0x4EA5 923 }; 924 toAlphabetic(builder, value, cjkEarthlyBranchAlphabet); 925 break; 926 } 927 case CjkHeavenlyStem: { 928 static const UChar cjkHeavenlyStemAlphabet[10] = { 929 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC, 930 0x7678 931 }; 932 toAlphabetic(builder, value, cjkHeavenlyStemAlphabet); 933 break; 934 } 935 case Ethiopic: 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 941 }; 942 toAlphabetic(builder, value, ethiopicHalehameGezAlphabet); 943 break; 944 } 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 951 }; 952 toAlphabetic(builder, value, ethiopicAbegedeGezAlphabet); 953 break; 954 } 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 959 }; 960 toAlphabetic(builder, value, hangulConsonantAlphabet); 961 break; 962 } 963 case Hangul: { 964 static const UChar hangulAlphabet[14] = { 965 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790, 966 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558 967 }; 968 toAlphabetic(builder, value, hangulAlphabet); 969 break; 970 } 971 case Oromo: 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 977 }; 978 toAlphabetic(builder, value, ethiopicHalehameOmEtAlphabet); 979 break; 980 } 981 case Sidama: 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 987 }; 988 toAlphabetic(builder, value, ethiopicHalehameSidEtAlphabet); 989 break; 990 } 991 case Somali: 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 997 }; 998 toAlphabetic(builder, value, ethiopicHalehameSoEtAlphabet); 999 break; 1000 } 1001 case Tigre: 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 1007 }; 1008 toAlphabetic(builder, value, ethiopicHalehameTigAlphabet); 1009 break; 1010 } 1011 case TigrinyaEr: 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 1018 }; 1019 toAlphabetic(builder, value, ethiopicHalehameTiErAlphabet); 1020 break; 1021 } 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 1029 }; 1030 toAlphabetic(builder, value, ethiopicAbegedeTiErAlphabet); 1031 break; 1032 } 1033 case TigrinyaEt: 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 1040 }; 1041 toAlphabetic(builder, value, ethiopicHalehameTiEtAlphabet); 1042 break; 1043 } 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 1051 }; 1052 toAlphabetic(builder, value, ethiopicAbegedeTiEtAlphabet); 1053 break; 1054 } 1055 case UpperGreek: { 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 1060 }; 1061 toAlphabetic(builder, value, upperGreekAlphabet); 1062 break; 1063 } 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, 1069 0xF8, 0xE5 1070 }; 1071 toAlphabetic(builder, value, lowerNorwegianAlphabet); 1072 break; 1073 } 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, 1079 0xD8, 0xC5 1080 }; 1081 toAlphabetic(builder, value, upperNorwegianAlphabet); 1082 break; 1083 } 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 1090 }; 1091 toCJKIdeographic(builder, value, traditionalChineseInformalTable); 1092 break; 1093 } 1094 1095 case LowerRoman: 1096 toRoman(builder, value, false); 1097 break; 1098 case UpperRoman: 1099 toRoman(builder, value, true); 1100 break; 1101 1102 case Armenian: 1103 case UpperArmenian: 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); 1108 break; 1109 case LowerArmenian: 1110 toArmenian(builder, value, false); 1111 break; 1112 case Georgian: 1113 toGeorgian(builder, value); 1114 break; 1115 case Hebrew: 1116 toHebrew(builder, value); 1117 break; 1118 } 1119 1120 return builder.toString(); 1121 } 1122 1123 RenderListMarker::RenderListMarker(RenderListItem& listItem, RenderStyle&& style) 1124 : RenderBox(listItem.document(), WTFMove(style), 0) 1125 , m_listItem(listItem) 1126 { 1127 // init RenderObject attributes 1128 setInline(true); // our object is Inline 1129 setReplaced(true); // pretend to be replaced 1130 } 1131 1132 RenderListMarker::~RenderListMarker() 1133 { 1134 // Do not add any code here. Add it to willBeDestroyed() instead. 1135 } 1136 1137 void RenderListMarker::willBeDestroyed() 1138 { 1139 if (m_image) 1140 m_image->removeClient(this); 1141 RenderBox::willBeDestroyed(); 1142 } 1143 1144 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 1145 { 1146 RenderBox::styleDidChange(diff, oldStyle); 1147 1148 if (oldStyle && (style().listStylePosition() != oldStyle->listStylePosition() || style().listStyleType() != oldStyle->listStyleType())) 1149 setNeedsLayoutAndPrefWidthsRecalc(); 1150 1151 if (m_image != style().listStyleImage()) { 1152 if (m_image) 1153 m_image->removeClient(this); 1154 m_image = style().listStyleImage(); 1155 if (m_image) 1156 m_image->addClient(this); 1157 } 1158 } 1159 1160 std::unique_ptr<InlineElementBox> RenderListMarker::createInlineBox() 1161 { 1162 auto box = RenderBox::createInlineBox(); 1163 box->setBehavesLikeText(isText()); 1164 return box; 1165 } 1166 1167 bool RenderListMarker::isImage() const 1168 { 1169 return m_image && !m_image->errorOccurred(); 1170 } 1171 1172 LayoutRect RenderListMarker::localSelectionRect() 1173 { 1174 InlineBox* box = inlineBoxWrapper(); 1175 if (!box) 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()); 1182 } 1183 1184 void RenderListMarker::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1185 { 1186 if (paintInfo.phase != PaintPhaseForeground) 1187 return; 1188 1189 if (style().visibility() != VISIBLE) 1190 return; 1191 1192 LayoutPoint boxOrigin(paintOffset + location()); 1193 LayoutRect overflowRect(visualOverflowRect()); 1194 overflowRect.moveBy(boxOrigin); 1195 if (!paintInfo.rect.intersects(overflowRect)) 1196 return; 1197 1198 LayoutRect box(boxOrigin, size()); 1199 1200 auto markerRect = getRelativeMarkerRect(); 1201 markerRect.moveBy(boxOrigin); 1202 if (markerRect.isEmpty()) 1203 return; 1204 1205 GraphicsContext& context = paintInfo.context(); 1206 1207 if (isImage()) { 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()); 1214 } 1215 return; 1216 } 1217 1218 if (selectionState() != SelectionNone) { 1219 LayoutRect selRect = localSelectionRect(); 1220 selRect.moveBy(boxOrigin); 1221 context.fillRect(snappedIntRect(selRect), m_listItem.selectionBackgroundColor()); 1222 } 1223 1224 const Color color(style().visitedDependentColor(CSSPropertyColor)); 1225 context.setStrokeColor(color); 1226 context.setStrokeStyle(SolidStroke); 1227 context.setStrokeThickness(1.0f); 1228 context.setFillColor(color); 1229 1230 EListStyleType type = style().listStyleType(); 1231 switch (type) { 1232 case Disc: 1233 context.drawEllipse(markerRect); 1234 return; 1235 case Circle: 1236 context.setFillColor(Color::transparent); 1237 context.drawEllipse(markerRect); 1238 return; 1239 case Square: 1240 context.drawRect(markerRect); 1241 return; 1242 case NoneListStyle: 1243 return; 1244 case Afar: 1245 case Amharic: 1246 case AmharicAbegede: 1247 case ArabicIndic: 1248 case Armenian: 1249 case BinaryListStyle: 1250 case Bengali: 1251 case Cambodian: 1252 case CJKIdeographic: 1253 case CjkEarthlyBranch: 1254 case CjkHeavenlyStem: 1255 case DecimalLeadingZero: 1256 case DecimalListStyle: 1257 case Devanagari: 1258 case Ethiopic: 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: 1274 case Georgian: 1275 case Gujarati: 1276 case Gurmukhi: 1277 case Hangul: 1278 case HangulConsonant: 1279 case Hebrew: 1280 case Hiragana: 1281 case HiraganaIroha: 1282 case Kannada: 1283 case Katakana: 1284 case KatakanaIroha: 1285 case Khmer: 1286 case Lao: 1287 case LowerAlpha: 1288 case LowerArmenian: 1289 case LowerGreek: 1290 case LowerHexadecimal: 1291 case LowerLatin: 1292 case LowerNorwegian: 1293 case LowerRoman: 1294 case Malayalam: 1295 case Mongolian: 1296 case Myanmar: 1297 case Octal: 1298 case Oriya: 1299 case Oromo: 1300 case Persian: 1301 case Sidama: 1302 case Somali: 1303 case Telugu: 1304 case Thai: 1305 case Tibetan: 1306 case Tigre: 1307 case TigrinyaEr: 1308 case TigrinyaErAbegede: 1309 case TigrinyaEt: 1310 case TigrinyaEtAbegede: 1311 case UpperAlpha: 1312 case UpperArmenian: 1313 case UpperGreek: 1314 case UpperHexadecimal: 1315 case UpperLatin: 1316 case UpperNorwegian: 1317 case UpperRoman: 1318 case Urdu: 1319 case Asterisks: 1320 case Footnotes: 1321 break; 1322 } 1323 if (m_text.isEmpty()) 1324 return; 1325 1326 const FontCascade& font = style().fontCascade(); 1327 TextRun textRun = RenderBlock::constructTextRun(m_text, style()); 1328 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())); 1334 stateSaver.save(); 1335 context.translate(markerRect.x(), markerRect.maxY()); 1336 context.rotate(static_cast<float>(deg2rad(90.))); 1337 context.translate(-markerRect.x(), -markerRect.maxY()); 1338 } 1339 1340 FloatPoint textOrigin = FloatPoint(markerRect.x(), markerRect.y() + style().fontMetrics().ascent()); 1341 textOrigin = roundPointToDevicePixels(LayoutPoint(textOrigin), document().deviceScaleFactor(), style().isLeftToRightDirection()); 1342 1343 if (type == Asterisks || type == Footnotes) 1344 context.drawText(font, textRun, textOrigin); 1345 else { 1346 const UChar suffix = listMarkerSuffix(type, m_listItem.value()); 1347 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; 1351 String toDraw; 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); 1359 } 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); 1365 } 1366 toDraw = buffer.toString(); 1367 } else { 1368 if (style().isLeftToRightDirection()) 1369 toDraw = m_text + String(&suffix, 1) + String(&space, 1); 1370 else 1371 toDraw = String(&space, 1) + String(&suffix, 1) + m_text; 1372 } 1373 textRun.setText(StringView(toDraw)); 1374 1375 context.drawText(font, textRun, textOrigin); 1376 } 1377 } 1378 1379 void RenderListMarker::layout() 1380 { 1381 StackStats::LayoutCheckPoint layoutCheckPoint; 1382 ASSERT(needsLayout()); 1383 1384 LayoutUnit blockOffset; 1385 for (auto* ancestor = parentBox(); ancestor && ancestor != &m_listItem; ancestor = ancestor->parentBox()) 1386 blockOffset += ancestor->logicalTop(); 1387 if (style().isLeftToRightDirection()) 1388 m_lineOffsetForListItem = m_listItem.logicalLeftOffsetForLine(blockOffset, DoNotIndentText, LayoutUnit()); 1389 else 1390 m_lineOffsetForListItem = m_listItem.logicalRightOffsetForLine(blockOffset, DoNotIndentText, LayoutUnit()); 1391 1392 if (isImage()) { 1393 updateMarginsAndContent(); 1394 setWidth(m_image->imageSize(this, style().effectiveZoom()).width()); 1395 setHeight(m_image->imageSize(this, style().effectiveZoom()).height()); 1396 } else { 1397 setLogicalWidth(minPreferredLogicalWidth()); 1398 setLogicalHeight(style().fontMetrics().height()); 1399 } 1400 1401 setMarginStart(0); 1402 setMarginEnd(0); 1403 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()); 1410 1411 clearNeedsLayout(); 1412 } 1413 1414 void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*) 1415 { 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()) 1418 return; 1419 1420 if (width() != m_image->imageSize(this, style().effectiveZoom()).width() || height() != m_image->imageSize(this, style().effectiveZoom()).height() || m_image->errorOccurred()) 1421 setNeedsLayoutAndPrefWidthsRecalc(); 1422 else 1423 repaint(); 1424 } 1425 1426 void RenderListMarker::updateMarginsAndContent() 1427 { 1428 updateContent(); 1429 updateMargins(); 1430 } 1431 1432 void RenderListMarker::updateContent() 1433 { 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()) 1437 return; 1438 1439 m_text = emptyString(); 1440 1441 if (isImage()) { 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()); 1448 return; 1449 } 1450 1451 EListStyleType type = style().listStyleType(); 1452 switch (type) { 1453 case NoneListStyle: 1454 break; 1455 case Circle: 1456 case Disc: 1457 case Square: 1458 m_text = listMarkerText(type, 0); // value is ignored for these types 1459 break; 1460 case Asterisks: 1461 case Footnotes: 1462 case Afar: 1463 case Amharic: 1464 case AmharicAbegede: 1465 case ArabicIndic: 1466 case Armenian: 1467 case BinaryListStyle: 1468 case Bengali: 1469 case Cambodian: 1470 case CJKIdeographic: 1471 case CjkEarthlyBranch: 1472 case CjkHeavenlyStem: 1473 case DecimalLeadingZero: 1474 case DecimalListStyle: 1475 case Devanagari: 1476 case Ethiopic: 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: 1492 case Georgian: 1493 case Gujarati: 1494 case Gurmukhi: 1495 case Hangul: 1496 case HangulConsonant: 1497 case Hebrew: 1498 case Hiragana: 1499 case HiraganaIroha: 1500 case Kannada: 1501 case Katakana: 1502 case KatakanaIroha: 1503 case Khmer: 1504 case Lao: 1505 case LowerAlpha: 1506 case LowerArmenian: 1507 case LowerGreek: 1508 case LowerHexadecimal: 1509 case LowerLatin: 1510 case LowerNorwegian: 1511 case LowerRoman: 1512 case Malayalam: 1513 case Mongolian: 1514 case Myanmar: 1515 case Octal: 1516 case Oriya: 1517 case Oromo: 1518 case Persian: 1519 case Sidama: 1520 case Somali: 1521 case Telugu: 1522 case Thai: 1523 case Tibetan: 1524 case Tigre: 1525 case TigrinyaEr: 1526 case TigrinyaErAbegede: 1527 case TigrinyaEt: 1528 case TigrinyaEtAbegede: 1529 case UpperAlpha: 1530 case UpperArmenian: 1531 case UpperGreek: 1532 case UpperHexadecimal: 1533 case UpperLatin: 1534 case UpperNorwegian: 1535 case UpperRoman: 1536 case Urdu: 1537 m_text = listMarkerText(type, m_listItem.value()); 1538 break; 1539 } 1540 } 1541 1542 void RenderListMarker::computePreferredLogicalWidths() 1543 { 1544 ASSERT(preferredLogicalWidthsDirty()); 1545 updateContent(); 1546 1547 if (isImage()) { 1548 LayoutSize imageSize = LayoutSize(m_image->imageSize(this, style().effectiveZoom())); 1549 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = style().isHorizontalWritingMode() ? imageSize.width() : imageSize.height(); 1550 setPreferredLogicalWidthsDirty(false); 1551 updateMargins(); 1552 return; 1553 } 1554 1555 const FontCascade& font = style().fontCascade(); 1556 1557 LayoutUnit logicalWidth = 0; 1558 EListStyleType type = style().listStyleType(); 1559 switch (type) { 1560 case NoneListStyle: 1561 break; 1562 case Asterisks: 1563 case Footnotes: { 1564 TextRun run = RenderBlock::constructTextRun(m_text, style()); 1565 logicalWidth = font.width(run); // no suffix for these types 1566 } 1567 break; 1568 case Circle: 1569 case Disc: 1570 case Square: 1571 logicalWidth = (font.fontMetrics().ascent() * 2 / 3 + 1) / 2 + 2; 1572 break; 1573 case Afar: 1574 case Amharic: 1575 case AmharicAbegede: 1576 case ArabicIndic: 1577 case Armenian: 1578 case BinaryListStyle: 1579 case Bengali: 1580 case Cambodian: 1581 case CJKIdeographic: 1582 case CjkEarthlyBranch: 1583 case CjkHeavenlyStem: 1584 case DecimalLeadingZero: 1585 case DecimalListStyle: 1586 case Devanagari: 1587 case Ethiopic: 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: 1603 case Georgian: 1604 case Gujarati: 1605 case Gurmukhi: 1606 case Hangul: 1607 case HangulConsonant: 1608 case Hebrew: 1609 case Hiragana: 1610 case HiraganaIroha: 1611 case Kannada: 1612 case Katakana: 1613 case KatakanaIroha: 1614 case Khmer: 1615 case Lao: 1616 case LowerAlpha: 1617 case LowerArmenian: 1618 case LowerGreek: 1619 case LowerHexadecimal: 1620 case LowerLatin: 1621 case LowerNorwegian: 1622 case LowerRoman: 1623 case Malayalam: 1624 case Mongolian: 1625 case Myanmar: 1626 case Octal: 1627 case Oriya: 1628 case Oromo: 1629 case Persian: 1630 case Sidama: 1631 case Somali: 1632 case Telugu: 1633 case Thai: 1634 case Tibetan: 1635 case Tigre: 1636 case TigrinyaEr: 1637 case TigrinyaErAbegede: 1638 case TigrinyaEt: 1639 case TigrinyaEtAbegede: 1640 case UpperAlpha: 1641 case UpperArmenian: 1642 case UpperGreek: 1643 case UpperHexadecimal: 1644 case UpperLatin: 1645 case UpperNorwegian: 1646 case UpperRoman: 1647 case Urdu: 1648 if (m_text.isEmpty()) 1649 logicalWidth = 0; 1650 else { 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; 1656 } 1657 break; 1658 } 1659 1660 m_minPreferredLogicalWidth = logicalWidth; 1661 m_maxPreferredLogicalWidth = logicalWidth; 1662 1663 setPreferredLogicalWidthsDirty(false); 1664 1665 updateMargins(); 1666 } 1667 1668 void RenderListMarker::updateMargins() 1669 { 1670 const FontMetrics& fontMetrics = style().fontMetrics(); 1671 1672 LayoutUnit marginStart = 0; 1673 LayoutUnit marginEnd = 0; 1674 1675 if (isInside()) { 1676 if (isImage()) 1677 marginEnd = cMarkerPadding; 1678 else switch (style().listStyleType()) { 1679 case Disc: 1680 case Circle: 1681 case Square: 1682 marginStart = -1; 1683 marginEnd = fontMetrics.ascent() - minPreferredLogicalWidth() + 1; 1684 break; 1685 default: 1686 break; 1687 } 1688 } else { 1689 if (style().isLeftToRightDirection()) { 1690 if (isImage()) 1691 marginStart = -minPreferredLogicalWidth() - cMarkerPadding; 1692 else { 1693 int offset = fontMetrics.ascent() * 2 / 3; 1694 switch (style().listStyleType()) { 1695 case Disc: 1696 case Circle: 1697 case Square: 1698 marginStart = -offset - cMarkerPadding - 1; 1699 break; 1700 case NoneListStyle: 1701 break; 1702 default: 1703 marginStart = m_text.isEmpty() ? LayoutUnit() : -minPreferredLogicalWidth() - offset / 2; 1704 } 1705 } 1706 marginEnd = -marginStart - minPreferredLogicalWidth(); 1707 } else { 1708 if (isImage()) 1709 marginEnd = cMarkerPadding; 1710 else { 1711 int offset = fontMetrics.ascent() * 2 / 3; 1712 switch (style().listStyleType()) { 1713 case Disc: 1714 case Circle: 1715 case Square: 1716 marginEnd = offset + cMarkerPadding + 1 - minPreferredLogicalWidth(); 1717 break; 1718 case NoneListStyle: 1719 break; 1720 default: 1721 marginEnd = m_text.isEmpty() ? 0 : offset / 2; 1722 } 1723 } 1724 marginStart = -marginEnd - minPreferredLogicalWidth(); 1725 } 1726 1727 } 1728 1729 mutableStyle().setMarginStart(Length(marginStart, Fixed)); 1730 mutableStyle().setMarginEnd(Length(marginEnd, Fixed)); 1731 } 1732 1733 LayoutUnit RenderListMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const 1734 { 1735 if (!isImage()) 1736 return m_listItem.lineHeight(firstLine, direction, PositionOfInteriorLineBoxes); 1737 return RenderBox::lineHeight(firstLine, direction, linePositionMode); 1738 } 1739 1740 int RenderListMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const 1741 { 1742 if (!isImage()) 1743 return m_listItem.baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes); 1744 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode); 1745 } 1746 1747 String RenderListMarker::suffix() const 1748 { 1749 EListStyleType type = style().listStyleType(); 1750 const UChar suffix = listMarkerSuffix(type, m_listItem.value()); 1751 1752 if (suffix == ' ') 1753 return String(" "); 1754 1755 // If the suffix is not ' ', an extra space is needed 1756 UChar data[2]; 1757 if (style().isLeftToRightDirection()) { 1758 data[0] = suffix; 1759 data[1] = ' '; 1760 } else { 1761 data[0] = ' '; 1762 data[1] = suffix; 1763 } 1764 1765 return String(data, 2); 1766 } 1767 1768 bool RenderListMarker::isInside() const 1769 { 1770 return m_listItem.notInList() || style().listStylePosition() == INSIDE; 1771 } 1772 1773 FloatRect RenderListMarker::getRelativeMarkerRect() 1774 { 1775 if (isImage()) 1776 return FloatRect(0, 0, m_image->imageSize(this, style().effectiveZoom()).width(), m_image->imageSize(this, style().effectiveZoom()).height()); 1777 1778 FloatRect relativeRect; 1779 EListStyleType type = style().listStyleType(); 1780 switch (type) { 1781 case Asterisks: 1782 case Footnotes: { 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()); 1786 break; 1787 } 1788 case Disc: 1789 case Circle: 1790 case Square: { 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); 1796 break; 1797 } 1798 case NoneListStyle: 1799 return FloatRect(); 1800 case Afar: 1801 case Amharic: 1802 case AmharicAbegede: 1803 case ArabicIndic: 1804 case Armenian: 1805 case BinaryListStyle: 1806 case Bengali: 1807 case Cambodian: 1808 case CJKIdeographic: 1809 case CjkEarthlyBranch: 1810 case CjkHeavenlyStem: 1811 case DecimalLeadingZero: 1812 case DecimalListStyle: 1813 case Devanagari: 1814 case Ethiopic: 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: 1830 case Georgian: 1831 case Gujarati: 1832 case Gurmukhi: 1833 case Hangul: 1834 case HangulConsonant: 1835 case Hebrew: 1836 case Hiragana: 1837 case HiraganaIroha: 1838 case Kannada: 1839 case Katakana: 1840 case KatakanaIroha: 1841 case Khmer: 1842 case Lao: 1843 case LowerAlpha: 1844 case LowerArmenian: 1845 case LowerGreek: 1846 case LowerHexadecimal: 1847 case LowerLatin: 1848 case LowerNorwegian: 1849 case LowerRoman: 1850 case Malayalam: 1851 case Mongolian: 1852 case Myanmar: 1853 case Octal: 1854 case Oriya: 1855 case Oromo: 1856 case Persian: 1857 case Sidama: 1858 case Somali: 1859 case Telugu: 1860 case Thai: 1861 case Tibetan: 1862 case Tigre: 1863 case TigrinyaEr: 1864 case TigrinyaErAbegede: 1865 case TigrinyaEt: 1866 case TigrinyaEtAbegede: 1867 case UpperAlpha: 1868 case UpperArmenian: 1869 case UpperGreek: 1870 case UpperHexadecimal: 1871 case UpperLatin: 1872 case UpperNorwegian: 1873 case UpperRoman: 1874 case Urdu: 1875 if (m_text.isEmpty()) 1876 return FloatRect(); 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()); 1883 } 1884 1885 if (!style().isHorizontalWritingMode()) { 1886 relativeRect = relativeRect.transposedRect(); 1887 relativeRect.setX(width() - relativeRect.x() - relativeRect.width()); 1888 } 1889 1890 return relativeRect; 1891 } 1892 1893 void RenderListMarker::setSelectionState(SelectionState state) 1894 { 1895 // The selection state for our containing block hierarchy is updated by the base class call. 1896 RenderBox::setSelectionState(state); 1897 1898 if (m_inlineBoxWrapper && canUpdateSelectionOnRootLineBoxes()) 1899 m_inlineBoxWrapper->root().setHasSelectedChildren(state != SelectionNone); 1900 } 1901 1902 LayoutRect RenderListMarker::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent) 1903 { 1904 ASSERT(!needsLayout()); 1905 1906 if (selectionState() == SelectionNone || !inlineBoxWrapper()) 1907 return LayoutRect(); 1908 1909 RootInlineBox& rootBox = inlineBoxWrapper()->root(); 1910 LayoutRect rect(0, rootBox.selectionTop() - y(), width(), rootBox.selectionHeight()); 1911 1912 if (clipToVisibleContent) 1913 return computeRectForRepaint(rect, repaintContainer); 1914 return localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox(); 1915 } 1916 1917 } // namespace WebCore