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