1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. 5 * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) 6 * Copyright (C) 2010 Daniel Bates (dbates@intudata.com) 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 * 23 */ 24 25#include "config.h" 26#include "RenderListMarker.h" 27 28#include "Document.h" 29#include "Font.h" 30#include "GraphicsContext.h" 31#include "RenderLayer.h" 32#include "RenderListItem.h" 33#include "RenderView.h" 34#include <wtf/StackStats.h> 35#include <wtf/text/StringBuilder.h> 36#include <wtf/unicode/CharacterNames.h> 37 38using namespace std; 39using namespace WTF; 40using namespace Unicode; 41 42namespace WebCore { 43 44const int cMarkerPadding = 7; 45 46enum SequenceType { NumericSequence, AlphabeticSequence }; 47 48static 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. 86typedef int numberType; 87 88template <typename CharacterType> 89static 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 125template <typename CharacterType> 126static 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 142template <typename CharacterType> 143static String toAlphabetic(int number, const CharacterType* alphabet, unsigned alphabetSize) 144{ 145 return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence); 146} 147 148template <typename CharacterType> 149static String toNumeric(int number, const CharacterType* numerals, unsigned numeralsSize) 150{ 151 return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence); 152} 153 154template <typename CharacterType, size_t size> 155static inline String toAlphabetic(int number, const CharacterType(&alphabet)[size]) 156{ 157 return toAlphabetic(number, alphabet, size); 158} 159 160template <typename CharacterType, size_t size> 161static inline String toNumeric(int number, const CharacterType(&alphabet)[size]) 162{ 163 return toNumeric(number, alphabet, size); 164} 165 166template <typename CharacterType, size_t size> 167static inline String toSymbolic(int number, const CharacterType(&alphabet)[size]) 168{ 169 return toSymbolic(number, alphabet, size); 170} 171 172static 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 200static 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 227static 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 267static 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 281static 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. 327static 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 407static 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 506static 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 605String 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 1056RenderListMarker::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 1065RenderListMarker::~RenderListMarker() 1066{ 1067 if (m_image) 1068 m_image->removeClient(this); 1069} 1070 1071RenderListMarker* RenderListMarker::createAnonymous(RenderListItem* item) 1072{ 1073 Document* document = item->document(); 1074 RenderListMarker* renderer = new (document->renderArena()) RenderListMarker(item); 1075 renderer->setDocumentForAnonymous(document); 1076 return renderer; 1077} 1078 1079void 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 1087void 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 1100InlineBox* RenderListMarker::createInlineBox() 1101{ 1102 InlineBox* result = RenderBox::createInlineBox(); 1103 result->setIsText(isText()); 1104 return result; 1105} 1106 1107bool RenderListMarker::isImage() const 1108{ 1109 return m_image && !m_image->errorOccurred(); 1110} 1111 1112LayoutRect 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 1124void RenderListMarker::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1125{ 1126 if (paintInfo.phase != PaintPhaseForeground) 1127 return; 1128 1129 if (style()->visibility() != VISIBLE) 1130 return; 1131 1132 LayoutPoint boxOrigin(paintOffset + location()); 1133 LayoutRect overflowRect(visualOverflowRect()); 1134 overflowRect.moveBy(boxOrigin); 1135 overflowRect.inflate(maximalOutlineSize(paintInfo.phase)); 1136 1137 if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect))) 1138 return; 1139 1140 LayoutRect box(boxOrigin, size()); 1141 1142 IntRect marker = getRelativeMarkerRect(); 1143 marker.moveBy(roundedIntPoint(boxOrigin)); 1144 1145 GraphicsContext* context = paintInfo.context; 1146 1147 if (isImage()) { 1148#if PLATFORM(MAC) 1149 if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) 1150 paintCustomHighlight(paintOffset, style()->highlight(), true); 1151#endif 1152 context->drawImage(m_image->image(this, marker.size()).get(), style()->colorSpace(), marker); 1153 if (selectionState() != SelectionNone) { 1154 LayoutRect selRect = localSelectionRect(); 1155 selRect.moveBy(boxOrigin); 1156 context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor(), style()->colorSpace()); 1157 } 1158 return; 1159 } 1160 1161#if PLATFORM(MAC) 1162 // FIXME: paint gap between marker and list item proper 1163 if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) 1164 paintCustomHighlight(paintOffset, style()->highlight(), true); 1165#endif 1166 1167 if (selectionState() != SelectionNone) { 1168 LayoutRect selRect = localSelectionRect(); 1169 selRect.moveBy(boxOrigin); 1170 context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor(), style()->colorSpace()); 1171 } 1172 1173 const Color color(style()->visitedDependentColor(CSSPropertyColor)); 1174 context->setStrokeColor(color, style()->colorSpace()); 1175 context->setStrokeStyle(SolidStroke); 1176 context->setStrokeThickness(1.0f); 1177 context->setFillColor(color, style()->colorSpace()); 1178 1179 EListStyleType type = style()->listStyleType(); 1180 switch (type) { 1181 case Disc: 1182 context->drawEllipse(marker); 1183 return; 1184 case Circle: 1185 context->setFillColor(Color::transparent, ColorSpaceDeviceRGB); 1186 context->drawEllipse(marker); 1187 return; 1188 case Square: 1189 context->drawRect(marker); 1190 return; 1191 case NoneListStyle: 1192 return; 1193 case Afar: 1194 case Amharic: 1195 case AmharicAbegede: 1196 case ArabicIndic: 1197 case Armenian: 1198 case BinaryListStyle: 1199 case Bengali: 1200 case Cambodian: 1201 case CJKIdeographic: 1202 case CjkEarthlyBranch: 1203 case CjkHeavenlyStem: 1204 case DecimalLeadingZero: 1205 case DecimalListStyle: 1206 case Devanagari: 1207 case Ethiopic: 1208 case EthiopicAbegede: 1209 case EthiopicAbegedeAmEt: 1210 case EthiopicAbegedeGez: 1211 case EthiopicAbegedeTiEr: 1212 case EthiopicAbegedeTiEt: 1213 case EthiopicHalehameAaEr: 1214 case EthiopicHalehameAaEt: 1215 case EthiopicHalehameAmEt: 1216 case EthiopicHalehameGez: 1217 case EthiopicHalehameOmEt: 1218 case EthiopicHalehameSidEt: 1219 case EthiopicHalehameSoEt: 1220 case EthiopicHalehameTiEr: 1221 case EthiopicHalehameTiEt: 1222 case EthiopicHalehameTig: 1223 case Georgian: 1224 case Gujarati: 1225 case Gurmukhi: 1226 case Hangul: 1227 case HangulConsonant: 1228 case Hebrew: 1229 case Hiragana: 1230 case HiraganaIroha: 1231 case Kannada: 1232 case Katakana: 1233 case KatakanaIroha: 1234 case Khmer: 1235 case Lao: 1236 case LowerAlpha: 1237 case LowerArmenian: 1238 case LowerGreek: 1239 case LowerHexadecimal: 1240 case LowerLatin: 1241 case LowerNorwegian: 1242 case LowerRoman: 1243 case Malayalam: 1244 case Mongolian: 1245 case Myanmar: 1246 case Octal: 1247 case Oriya: 1248 case Oromo: 1249 case Persian: 1250 case Sidama: 1251 case Somali: 1252 case Telugu: 1253 case Thai: 1254 case Tibetan: 1255 case Tigre: 1256 case TigrinyaEr: 1257 case TigrinyaErAbegede: 1258 case TigrinyaEt: 1259 case TigrinyaEtAbegede: 1260 case UpperAlpha: 1261 case UpperArmenian: 1262 case UpperGreek: 1263 case UpperHexadecimal: 1264 case UpperLatin: 1265 case UpperNorwegian: 1266 case UpperRoman: 1267 case Urdu: 1268 case Asterisks: 1269 case Footnotes: 1270 break; 1271 } 1272 if (m_text.isEmpty()) 1273 return; 1274 1275 const Font& font = style()->font(); 1276 TextRun textRun = RenderBlock::constructTextRun(this, font, m_text, style()); 1277 1278 GraphicsContextStateSaver stateSaver(*context, false); 1279 if (!style()->isHorizontalWritingMode()) { 1280 marker.moveBy(roundedIntPoint(-boxOrigin)); 1281 marker = marker.transposedRect(); 1282 marker.moveBy(IntPoint(roundToInt(box.x()), roundToInt(box.y() - logicalHeight()))); 1283 stateSaver.save(); 1284 context->translate(marker.x(), marker.maxY()); 1285 context->rotate(static_cast<float>(deg2rad(90.))); 1286 context->translate(-marker.x(), -marker.maxY()); 1287 } 1288 1289 IntPoint textOrigin = IntPoint(marker.x(), marker.y() + style()->fontMetrics().ascent()); 1290 1291 if (type == Asterisks || type == Footnotes) 1292 context->drawText(font, textRun, textOrigin); 1293 else { 1294 // Text is not arbitrary. We can judge whether it's RTL from the first character, 1295 // and we only need to handle the direction RightToLeft for now. 1296 bool textNeedsReversing = direction(m_text[0]) == RightToLeft; 1297 StringBuilder reversedText; 1298 if (textNeedsReversing) { 1299 int length = m_text.length(); 1300 reversedText.reserveCapacity(length); 1301 for (int i = length - 1; i >= 0; --i) 1302 reversedText.append(m_text[i]); 1303 textRun.setText(reversedText.characters(), length); 1304 } 1305 1306 const UChar suffix = listMarkerSuffix(type, m_listItem->value()); 1307 if (style()->isLeftToRightDirection()) { 1308 int width = font.width(textRun); 1309 context->drawText(font, textRun, textOrigin); 1310 UChar suffixSpace[2] = { suffix, ' ' }; 1311 context->drawText(font, RenderBlock::constructTextRun(this, font, suffixSpace, 2, style()), textOrigin + IntSize(width, 0)); 1312 } else { 1313 UChar spaceSuffix[2] = { ' ', suffix }; 1314 TextRun spaceSuffixRun = RenderBlock::constructTextRun(this, font, spaceSuffix, 2, style()); 1315 int width = font.width(spaceSuffixRun); 1316 context->drawText(font, spaceSuffixRun, textOrigin); 1317 context->drawText(font, textRun, textOrigin + IntSize(width, 0)); 1318 } 1319 } 1320} 1321 1322void 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 setNeedsLayout(false); 1347} 1348 1349void 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 1361void RenderListMarker::updateMarginsAndContent() 1362{ 1363 updateContent(); 1364 updateMargins(); 1365} 1366 1367void 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 1475void 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 1598void RenderListMarker::updateMargins() 1599{ 1600 const FontMetrics& fontMetrics = style()->fontMetrics(); 1601 1602 LayoutUnit marginStart = 0; 1603 LayoutUnit marginEnd = 0; 1604 1605 if (isInside()) { 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 1663LayoutUnit 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 1670int RenderListMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const 1671{ 1672 if (!isImage()) 1673 return m_listItem->baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes); 1674 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode); 1675} 1676 1677String 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 1698bool RenderListMarker::isInside() const 1699{ 1700 return m_listItem->notInList() || style()->listStylePosition() == INSIDE; 1701} 1702 1703IntRect 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(RenderBlock::constructTextRun(this, font, suffixSpace, 2, style())); 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 1821void 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 (m_inlineBoxWrapper && canUpdateSelectionOnRootLineBoxes()) 1827 if (RootInlineBox* root = m_inlineBoxWrapper->root()) 1828 root->setHasSelectedChildren(state != SelectionNone); 1829} 1830 1831LayoutRect RenderListMarker::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent) 1832{ 1833 ASSERT(!needsLayout()); 1834 1835 if (selectionState() == SelectionNone || !inlineBoxWrapper()) 1836 return LayoutRect(); 1837 1838 RootInlineBox* root = inlineBoxWrapper()->root(); 1839 LayoutRect rect(0, root->selectionTop() - y(), width(), root->selectionHeight()); 1840 1841 if (clipToVisibleContent) 1842 computeRectForRepaint(repaintContainer, rect); 1843 else 1844 rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox(); 1845 1846 return rect; 1847} 1848 1849} // namespace WebCore 1850