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