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