blob: b66aab57b30cf57c6c4ca754c52ea88dcb924022 [file] [log] [blame]
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001/*
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 "core/rendering/RenderListMarker.h"
27
28#include "core/dom/Document.h"
Torne (Richard Coles)e1f1df52013-08-23 16:39:30 +010029#include "core/fetch/ImageResource.h"
Torne (Richard Coles)19cde672013-11-06 12:28:04 +000030#include "core/rendering/GraphicsContextAnnotator.h"
Torne (Richard Coles)51b29062013-11-28 11:56:03 +000031#include "core/rendering/LayoutRectRecorder.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010032#include "core/rendering/RenderLayer.h"
33#include "core/rendering/RenderListItem.h"
34#include "core/rendering/RenderView.h"
Torne (Richard Coles)a854de02013-12-18 16:25:25 +000035#include "platform/fonts/Font.h"
36#include "platform/graphics/GraphicsContextStateSaver.h"
Ben Murdoch02772c62013-07-26 10:21:05 +010037#include "wtf/text/StringBuilder.h"
38#include "wtf/unicode/CharacterNames.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010039
40using namespace std;
41using namespace WTF;
42using namespace Unicode;
43
44namespace WebCore {
45
46const int cMarkerPadding = 7;
47
48enum SequenceType { NumericSequence, AlphabeticSequence };
49
50static String toRoman(int number, bool upper)
51{
52 // FIXME: CSS3 describes how to make this work for much larger numbers,
53 // using overbars and special characters. It also specifies the characters
54 // in the range U+2160 to U+217F instead of standard ASCII ones.
55 ASSERT(number >= 1 && number <= 3999);
56
57 // Big enough to store largest roman number less than 3999 which
58 // is 3888 (MMMDCCCLXXXVIII)
59 const int lettersSize = 15;
60 LChar letters[lettersSize];
61
62 int length = 0;
63 const LChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
64 const LChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
65 const LChar* digits = upper ? udigits : ldigits;
66 int d = 0;
67 do {
68 int num = number % 10;
69 if (num % 5 < 4)
70 for (int i = num % 5; i > 0; i--)
71 letters[lettersSize - ++length] = digits[d];
72 if (num >= 4 && num <= 8)
73 letters[lettersSize - ++length] = digits[d + 1];
74 if (num == 9)
75 letters[lettersSize - ++length] = digits[d + 2];
76 if (num % 5 == 4)
77 letters[lettersSize - ++length] = digits[d];
78 number /= 10;
79 d += 2;
80 } while (number);
81
82 ASSERT(length <= lettersSize);
83 return String(&letters[lettersSize - length], length);
84}
85
86// The typedef is needed because taking sizeof(number) in the const expression below doesn't work with some compilers.
87// This is likely the case because of the template.
88typedef int numberType;
89
90template <typename CharacterType>
91static inline String toAlphabeticOrNumeric(numberType number, const CharacterType* sequence, unsigned sequenceSize, SequenceType type)
92{
93 ASSERT(sequenceSize >= 2);
94
95 const int lettersSize = sizeof(numberType) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
96
97 CharacterType letters[lettersSize];
98
99 bool isNegativeNumber = false;
100 unsigned numberShadow = number;
101 if (type == AlphabeticSequence) {
102 ASSERT(number > 0);
103 --numberShadow;
104 } else if (number < 0) {
105 numberShadow = -number;
106 isNegativeNumber = true;
107 }
108 letters[lettersSize - 1] = sequence[numberShadow % sequenceSize];
109 int length = 1;
110
111 if (type == AlphabeticSequence) {
112 while ((numberShadow /= sequenceSize) > 0) {
113 --numberShadow;
114 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
115 }
116 } else {
117 while ((numberShadow /= sequenceSize) > 0)
118 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
119 }
120 if (isNegativeNumber)
121 letters[lettersSize - ++length] = hyphenMinus;
122
123 ASSERT(length <= lettersSize);
124 return String(&letters[lettersSize - length], length);
125}
126
127template <typename CharacterType>
128static String toSymbolic(int number, const CharacterType* symbols, unsigned symbolsSize)
129{
130 ASSERT(number > 0);
131 ASSERT(symbolsSize >= 1);
132 unsigned numberShadow = number;
133 --numberShadow;
134
135 // The asterisks list-style-type is the worst case; we show |numberShadow| asterisks.
136 StringBuilder letters;
137 letters.append(symbols[numberShadow % symbolsSize]);
138 unsigned numSymbols = numberShadow / symbolsSize;
139 while (numSymbols--)
140 letters.append(symbols[numberShadow % symbolsSize]);
141 return letters.toString();
142}
143
144template <typename CharacterType>
145static String toAlphabetic(int number, const CharacterType* alphabet, unsigned alphabetSize)
146{
147 return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence);
148}
149
150template <typename CharacterType>
151static String toNumeric(int number, const CharacterType* numerals, unsigned numeralsSize)
152{
153 return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence);
154}
155
156template <typename CharacterType, size_t size>
157static inline String toAlphabetic(int number, const CharacterType(&alphabet)[size])
158{
159 return toAlphabetic(number, alphabet, size);
160}
161
162template <typename CharacterType, size_t size>
163static inline String toNumeric(int number, const CharacterType(&alphabet)[size])
164{
165 return toNumeric(number, alphabet, size);
166}
167
168template <typename CharacterType, size_t size>
169static inline String toSymbolic(int number, const CharacterType(&alphabet)[size])
Ben Murdoch02772c62013-07-26 10:21:05 +0100170{
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100171 return toSymbolic(number, alphabet, size);
172}
173
174static int toHebrewUnder1000(int number, UChar letters[5])
175{
176 // FIXME: CSS3 mentions various refinements not implemented here.
177 // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
178 ASSERT(number >= 0 && number < 1000);
179 int length = 0;
180 int fourHundreds = number / 400;
181 for (int i = 0; i < fourHundreds; i++)
182 letters[length++] = 1511 + 3;
183 number %= 400;
184 if (number / 100)
185 letters[length++] = 1511 + (number / 100) - 1;
186 number %= 100;
187 if (number == 15 || number == 16) {
188 letters[length++] = 1487 + 9;
189 letters[length++] = 1487 + number - 9;
190 } else {
191 if (int tens = number / 10) {
192 static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
193 letters[length++] = hebrewTens[tens - 1];
194 }
195 if (int ones = number % 10)
196 letters[length++] = 1487 + ones;
197 }
198 ASSERT(length <= 5);
199 return length;
200}
201
202static String toHebrew(int number)
203{
204 // FIXME: CSS3 mentions ways to make this work for much larger numbers.
205 ASSERT(number >= 0 && number <= 999999);
206
207 if (number == 0) {
208 static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
209 return String(hebrewZero, 3);
210 }
211
212 const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
213 UChar letters[lettersSize];
214
215 int length;
216 if (number < 1000)
217 length = 0;
218 else {
219 length = toHebrewUnder1000(number / 1000, letters);
220 letters[length++] = '\'';
221 number = number % 1000;
222 }
223 length += toHebrewUnder1000(number, letters + length);
224
225 ASSERT(length <= lettersSize);
226 return String(letters, length);
227}
228
229static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
230{
231 ASSERT(number >= 0 && number < 10000);
232 int length = 0;
233
234 int lowerOffset = upper ? 0 : 0x0030;
235
236 if (int thousands = number / 1000) {
237 if (thousands == 7) {
238 letters[length++] = 0x0552 + lowerOffset;
239 if (addCircumflex)
240 letters[length++] = 0x0302;
241 } else {
242 letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
243 if (addCircumflex)
244 letters[length++] = 0x0302;
245 }
246 }
247
248 if (int hundreds = (number / 100) % 10) {
249 letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
250 if (addCircumflex)
251 letters[length++] = 0x0302;
252 }
253
254 if (int tens = (number / 10) % 10) {
255 letters[length++] = (0x053A - 1 + lowerOffset) + tens;
256 if (addCircumflex)
257 letters[length++] = 0x0302;
258 }
259
260 if (int ones = number % 10) {
261 letters[length++] = (0x531 - 1 + lowerOffset) + ones;
262 if (addCircumflex)
263 letters[length++] = 0x0302;
264 }
265
266 return length;
267}
268
269static String toArmenian(int number, bool upper)
270{
271 ASSERT(number >= 1 && number <= 99999999);
272
273 const int lettersSize = 18; // twice what toArmenianUnder10000 needs
274 UChar letters[lettersSize];
275
276 int length = toArmenianUnder10000(number / 10000, upper, true, letters);
277 length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
278
279 ASSERT(length <= lettersSize);
280 return String(letters, length);
281}
282
283static String toGeorgian(int number)
284{
285 ASSERT(number >= 1 && number <= 19999);
286
287 const int lettersSize = 5;
288 UChar letters[lettersSize];
289
290 int length = 0;
291
292 if (number > 9999)
293 letters[length++] = 0x10F5;
294
295 if (int thousands = (number / 1000) % 10) {
296 static const UChar georgianThousands[9] = {
297 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
298 };
299 letters[length++] = georgianThousands[thousands - 1];
300 }
301
302 if (int hundreds = (number / 100) % 10) {
303 static const UChar georgianHundreds[9] = {
304 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
305 };
306 letters[length++] = georgianHundreds[hundreds - 1];
307 }
308
309 if (int tens = (number / 10) % 10) {
310 static const UChar georgianTens[9] = {
311 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
312 };
313 letters[length++] = georgianTens[tens - 1];
314 }
315
316 if (int ones = number % 10) {
317 static const UChar georgianOnes[9] = {
318 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
319 };
320 letters[length++] = georgianOnes[ones - 1];
321 }
322
323 ASSERT(length <= lettersSize);
324 return String(letters, length);
325}
326
327// The table uses the order from the CSS3 specification:
328// first 3 group markers, then 3 digit markers, then ten digits.
329static String toCJKIdeographic(int number, const UChar table[16])
330{
331 ASSERT(number >= 0);
332
333 enum AbstractCJKChar {
334 noChar,
335 secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
336 secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
337 digit0, digit1, digit2, digit3, digit4,
338 digit5, digit6, digit7, digit8, digit9
339 };
340
341 if (number == 0)
342 return String(&table[digit0 - 1], 1);
343
344 const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker
345 const int bufferLength = 4 * groupLength;
346 AbstractCJKChar buffer[bufferLength] = { noChar };
347
348 for (int i = 0; i < 4; ++i) {
349 int groupValue = number % 10000;
350 number /= 10000;
351
352 // Process least-significant group first, but put it in the buffer last.
353 AbstractCJKChar* group = &buffer[(3 - i) * groupLength];
354
355 if (groupValue && i)
356 group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i);
357
358 // Put in the four digits and digit markers for any non-zero digits.
359 group[6] = static_cast<AbstractCJKChar>(digit0 + (groupValue % 10));
360 if (number != 0 || groupValue > 9) {
361 int digitValue = ((groupValue / 10) % 10);
362 group[4] = static_cast<AbstractCJKChar>(digit0 + digitValue);
363 if (digitValue)
364 group[5] = secondDigitMarker;
365 }
366 if (number != 0 || groupValue > 99) {
367 int digitValue = ((groupValue / 100) % 10);
368 group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue);
369 if (digitValue)
370 group[3] = thirdDigitMarker;
371 }
372 if (number != 0 || groupValue > 999) {
373 int digitValue = groupValue / 1000;
374 group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue);
375 if (digitValue)
376 group[1] = fourthDigitMarker;
377 }
378
379 // Remove the tens digit, but leave the marker, for any group that has
380 // a value of less than 20.
381 if (groupValue < 20) {
382 ASSERT(group[4] == noChar || group[4] == digit0 || group[4] == digit1);
383 group[4] = noChar;
384 }
385
386 if (number == 0)
387 break;
388 }
389
390 // Convert into characters, omitting consecutive runs of digit0 and
391 // any trailing digit0.
392 int length = 0;
393 UChar characters[bufferLength];
394 AbstractCJKChar last = noChar;
395 for (int i = 0; i < bufferLength; ++i) {
396 AbstractCJKChar a = buffer[i];
397 if (a != noChar) {
398 if (a != digit0 || last != digit0)
399 characters[length++] = table[a - 1];
400 last = a;
401 }
402 }
403 if (last == digit0)
404 --length;
405
406 return String(characters, length);
407}
408
409static EListStyleType effectiveListMarkerType(EListStyleType type, int value)
410{
411 // Note, the following switch statement has been explicitly grouped
412 // by list-style-type ordinal range.
413 switch (type) {
414 case ArabicIndic:
415 case Bengali:
416 case BinaryListStyle:
417 case Cambodian:
418 case Circle:
419 case DecimalLeadingZero:
420 case DecimalListStyle:
421 case Devanagari:
422 case Disc:
423 case Gujarati:
424 case Gurmukhi:
425 case Kannada:
426 case Khmer:
427 case Lao:
428 case LowerHexadecimal:
429 case Malayalam:
430 case Mongolian:
431 case Myanmar:
432 case NoneListStyle:
433 case Octal:
434 case Oriya:
435 case Persian:
436 case Square:
437 case Telugu:
438 case Thai:
439 case Tibetan:
440 case UpperHexadecimal:
441 case Urdu:
442 return type; // Can represent all ordinals.
443 case Armenian:
444 return (value < 1 || value > 99999999) ? DecimalListStyle : type;
445 case CJKIdeographic:
446 return (value < 0) ? DecimalListStyle : type;
447 case Georgian:
448 return (value < 1 || value > 19999) ? DecimalListStyle : type;
449 case Hebrew:
450 return (value < 0 || value > 999999) ? DecimalListStyle : type;
451 case LowerRoman:
452 case UpperRoman:
453 return (value < 1 || value > 3999) ? DecimalListStyle : type;
454 case Afar:
455 case Amharic:
456 case AmharicAbegede:
457 case Asterisks:
458 case CjkEarthlyBranch:
459 case CjkHeavenlyStem:
460 case Ethiopic:
461 case EthiopicAbegede:
462 case EthiopicAbegedeAmEt:
463 case EthiopicAbegedeGez:
464 case EthiopicAbegedeTiEr:
465 case EthiopicAbegedeTiEt:
466 case EthiopicHalehameAaEr:
467 case EthiopicHalehameAaEt:
468 case EthiopicHalehameAmEt:
469 case EthiopicHalehameGez:
470 case EthiopicHalehameOmEt:
471 case EthiopicHalehameSidEt:
472 case EthiopicHalehameSoEt:
473 case EthiopicHalehameTiEr:
474 case EthiopicHalehameTiEt:
475 case EthiopicHalehameTig:
476 case Footnotes:
477 case Hangul:
478 case HangulConsonant:
479 case Hiragana:
480 case HiraganaIroha:
481 case Katakana:
482 case KatakanaIroha:
483 case LowerAlpha:
484 case LowerArmenian:
485 case LowerGreek:
486 case LowerLatin:
487 case LowerNorwegian:
488 case Oromo:
489 case Sidama:
490 case Somali:
491 case Tigre:
492 case TigrinyaEr:
493 case TigrinyaErAbegede:
494 case TigrinyaEt:
495 case TigrinyaEtAbegede:
496 case UpperAlpha:
497 case UpperArmenian:
498 case UpperGreek:
499 case UpperLatin:
500 case UpperNorwegian:
501 return (value < 1) ? DecimalListStyle : type;
502 }
503
504 ASSERT_NOT_REACHED();
505 return type;
506}
507
508static UChar listMarkerSuffix(EListStyleType type, int value)
509{
510 // If the list-style-type cannot represent |value| because it's outside its
511 // ordinal range then we fall back to some list style that can represent |value|.
512 EListStyleType effectiveType = effectiveListMarkerType(type, value);
513
514 // Note, the following switch statement has been explicitly
515 // grouped by list-style-type suffix.
516 switch (effectiveType) {
517 case Asterisks:
518 case Circle:
519 case Disc:
520 case Footnotes:
521 case NoneListStyle:
522 case Square:
523 return ' ';
524 case Afar:
525 case Amharic:
526 case AmharicAbegede:
527 case Ethiopic:
528 case EthiopicAbegede:
529 case EthiopicAbegedeAmEt:
530 case EthiopicAbegedeGez:
531 case EthiopicAbegedeTiEr:
532 case EthiopicAbegedeTiEt:
533 case EthiopicHalehameAaEr:
534 case EthiopicHalehameAaEt:
535 case EthiopicHalehameAmEt:
536 case EthiopicHalehameGez:
537 case EthiopicHalehameOmEt:
538 case EthiopicHalehameSidEt:
539 case EthiopicHalehameSoEt:
540 case EthiopicHalehameTiEr:
541 case EthiopicHalehameTiEt:
542 case EthiopicHalehameTig:
543 case Oromo:
544 case Sidama:
545 case Somali:
546 case Tigre:
547 case TigrinyaEr:
548 case TigrinyaErAbegede:
549 case TigrinyaEt:
550 case TigrinyaEtAbegede:
551 return ethiopicPrefaceColon;
552 case Armenian:
553 case ArabicIndic:
554 case Bengali:
555 case BinaryListStyle:
556 case Cambodian:
557 case CJKIdeographic:
558 case CjkEarthlyBranch:
559 case CjkHeavenlyStem:
560 case DecimalLeadingZero:
561 case DecimalListStyle:
562 case Devanagari:
563 case Georgian:
564 case Gujarati:
565 case Gurmukhi:
566 case Hangul:
567 case HangulConsonant:
568 case Hebrew:
569 case Hiragana:
570 case HiraganaIroha:
571 case Kannada:
572 case Katakana:
573 case KatakanaIroha:
574 case Khmer:
575 case Lao:
576 case LowerAlpha:
577 case LowerArmenian:
578 case LowerGreek:
579 case LowerHexadecimal:
580 case LowerLatin:
581 case LowerNorwegian:
582 case LowerRoman:
583 case Malayalam:
584 case Mongolian:
585 case Myanmar:
586 case Octal:
587 case Oriya:
588 case Persian:
589 case Telugu:
590 case Thai:
591 case Tibetan:
592 case UpperAlpha:
593 case UpperArmenian:
594 case UpperGreek:
595 case UpperHexadecimal:
596 case UpperLatin:
597 case UpperNorwegian:
598 case UpperRoman:
599 case Urdu:
600 return '.';
601 }
602
603 ASSERT_NOT_REACHED();
604 return '.';
605}
606
607String listMarkerText(EListStyleType type, int value)
608{
609 // If the list-style-type, say hebrew, cannot represent |value| because it's outside
610 // its ordinal range then we fallback to some list style that can represent |value|.
611 switch (effectiveListMarkerType(type, value)) {
612 case NoneListStyle:
613 return "";
614
615 case Asterisks: {
616 static const LChar asterisksSymbols[1] = {
617 0x2A
618 };
619 return toSymbolic(value, asterisksSymbols);
620 }
621 // We use the same characters for text security.
622 // See RenderText::setInternalString.
623 case Circle:
624 return String(&whiteBullet, 1);
625 case Disc:
626 return String(&bullet, 1);
627 case Footnotes: {
628 static const UChar footnotesSymbols[4] = {
629 0x002A, 0x2051, 0x2020, 0x2021
630 };
631 return toSymbolic(value, footnotesSymbols);
632 }
633 case Square:
634 // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
635 // instead, but I think this looks better.
636 return String(&blackSquare, 1);
637
638 case DecimalListStyle:
639 return String::number(value);
640 case DecimalLeadingZero:
641 if (value < -9 || value > 9)
642 return String::number(value);
643 if (value < 0)
644 return "-0" + String::number(-value); // -01 to -09
645 return "0" + String::number(value); // 00 to 09
646
647 case ArabicIndic: {
648 static const UChar arabicIndicNumerals[10] = {
649 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669
650 };
651 return toNumeric(value, arabicIndicNumerals);
652 }
653 case BinaryListStyle: {
654 static const LChar binaryNumerals[2] = {
655 '0', '1'
656 };
657 return toNumeric(value, binaryNumerals);
658 }
659 case Bengali: {
660 static const UChar bengaliNumerals[10] = {
661 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF
662 };
663 return toNumeric(value, bengaliNumerals);
664 }
665 case Cambodian:
666 case Khmer: {
667 static const UChar khmerNumerals[10] = {
668 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9
669 };
670 return toNumeric(value, khmerNumerals);
671 }
672 case Devanagari: {
673 static const UChar devanagariNumerals[10] = {
674 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F
675 };
676 return toNumeric(value, devanagariNumerals);
677 }
678 case Gujarati: {
679 static const UChar gujaratiNumerals[10] = {
680 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF
681 };
682 return toNumeric(value, gujaratiNumerals);
683 }
684 case Gurmukhi: {
685 static const UChar gurmukhiNumerals[10] = {
686 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F
687 };
688 return toNumeric(value, gurmukhiNumerals);
689 }
690 case Kannada: {
691 static const UChar kannadaNumerals[10] = {
692 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF
693 };
694 return toNumeric(value, kannadaNumerals);
695 }
696 case LowerHexadecimal: {
697 static const LChar lowerHexadecimalNumerals[16] = {
698 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
699 };
700 return toNumeric(value, lowerHexadecimalNumerals);
701 }
702 case Lao: {
703 static const UChar laoNumerals[10] = {
704 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9
705 };
706 return toNumeric(value, laoNumerals);
707 }
708 case Malayalam: {
709 static const UChar malayalamNumerals[10] = {
710 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F
711 };
712 return toNumeric(value, malayalamNumerals);
713 }
714 case Mongolian: {
715 static const UChar mongolianNumerals[10] = {
716 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819
717 };
718 return toNumeric(value, mongolianNumerals);
719 }
720 case Myanmar: {
721 static const UChar myanmarNumerals[10] = {
722 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049
723 };
724 return toNumeric(value, myanmarNumerals);
725 }
726 case Octal: {
727 static const LChar octalNumerals[8] = {
728 '0', '1', '2', '3', '4', '5', '6', '7'
729 };
730 return toNumeric(value, octalNumerals);
731 }
732 case Oriya: {
733 static const UChar oriyaNumerals[10] = {
734 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F
735 };
736 return toNumeric(value, oriyaNumerals);
737 }
738 case Persian:
739 case Urdu: {
740 static const UChar urduNumerals[10] = {
741 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9
742 };
743 return toNumeric(value, urduNumerals);
744 }
745 case Telugu: {
746 static const UChar teluguNumerals[10] = {
747 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F
748 };
749 return toNumeric(value, teluguNumerals);
750 }
751 case Tibetan: {
752 static const UChar tibetanNumerals[10] = {
753 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29
754 };
755 return toNumeric(value, tibetanNumerals);
756 }
757 case Thai: {
758 static const UChar thaiNumerals[10] = {
759 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59
760 };
761 return toNumeric(value, thaiNumerals);
762 }
763 case UpperHexadecimal: {
764 static const LChar upperHexadecimalNumerals[16] = {
765 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
766 };
767 return toNumeric(value, upperHexadecimalNumerals);
768 }
769
770 case LowerAlpha:
771 case LowerLatin: {
772 static const LChar lowerLatinAlphabet[26] = {
773 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
774 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
775 };
776 return toAlphabetic(value, lowerLatinAlphabet);
777 }
778 case UpperAlpha:
779 case UpperLatin: {
780 static const LChar upperLatinAlphabet[26] = {
781 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
782 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
783 };
784 return toAlphabetic(value, upperLatinAlphabet);
785 }
786 case LowerGreek: {
787 static const UChar lowerGreekAlphabet[24] = {
788 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
789 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
790 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9
791 };
792 return toAlphabetic(value, lowerGreekAlphabet);
793 }
794
795 case Hiragana: {
796 // FIXME: This table comes from the CSS3 draft, and is probably
797 // incorrect, given the comments in that draft.
798 static const UChar hiraganaAlphabet[48] = {
799 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
800 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
801 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
802 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
803 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
804 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093
805 };
806 return toAlphabetic(value, hiraganaAlphabet);
807 }
808 case HiraganaIroha: {
809 // FIXME: This table comes from the CSS3 draft, and is probably
810 // incorrect, given the comments in that draft.
811 static const UChar hiraganaIrohaAlphabet[47] = {
812 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061,
813 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F,
814 0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046,
815 0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075,
816 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
817 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059
818 };
819 return toAlphabetic(value, hiraganaIrohaAlphabet);
820 }
821 case Katakana: {
822 // FIXME: This table comes from the CSS3 draft, and is probably
823 // incorrect, given the comments in that draft.
824 static const UChar katakanaAlphabet[48] = {
825 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF,
826 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF,
827 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD,
828 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF,
829 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
830 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3
831 };
832 return toAlphabetic(value, katakanaAlphabet);
833 }
834 case KatakanaIroha: {
835 // FIXME: This table comes from the CSS3 draft, and is probably
836 // incorrect, given the comments in that draft.
837 static const UChar katakanaIrohaAlphabet[47] = {
838 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1,
839 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF,
840 0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6,
841 0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5,
842 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
843 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9
844 };
845 return toAlphabetic(value, katakanaIrohaAlphabet);
846 }
847
848 case Afar:
849 case EthiopicHalehameAaEt:
850 case EthiopicHalehameAaEr: {
851 static const UChar ethiopicHalehameAaErAlphabet[18] = {
852 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290,
853 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348
854 };
855 return toAlphabetic(value, ethiopicHalehameAaErAlphabet);
856 }
857 case Amharic:
858 case EthiopicHalehameAmEt: {
859 static const UChar ethiopicHalehameAmEtAlphabet[33] = {
860 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
861 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8,
862 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320,
863 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
864 };
865 return toAlphabetic(value, ethiopicHalehameAmEtAlphabet);
866 }
867 case AmharicAbegede:
868 case EthiopicAbegedeAmEt: {
869 static const UChar ethiopicAbegedeAmEtAlphabet[33] = {
870 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
871 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
872 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238,
873 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
874 };
875 return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet);
876 }
877 case CjkEarthlyBranch: {
878 static const UChar cjkEarthlyBranchAlphabet[12] = {
879 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533,
880 0x9149, 0x620C, 0x4EA5
881 };
882 return toAlphabetic(value, cjkEarthlyBranchAlphabet);
883 }
884 case CjkHeavenlyStem: {
885 static const UChar cjkHeavenlyStemAlphabet[10] = {
886 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC,
887 0x7678
888 };
889 return toAlphabetic(value, cjkHeavenlyStemAlphabet);
890 }
891 case Ethiopic:
892 case EthiopicHalehameGez: {
893 static const UChar ethiopicHalehameGezAlphabet[26] = {
894 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1240, 0x1260,
895 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
896 0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
897 };
898 return toAlphabetic(value, ethiopicHalehameGezAlphabet);
899 }
900 case EthiopicAbegede:
901 case EthiopicAbegedeGez: {
902 static const UChar ethiopicAbegedeGezAlphabet[26] = {
903 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1200, 0x12C8, 0x12D8, 0x1210, 0x1320,
904 0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338,
905 0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350
906 };
907 return toAlphabetic(value, ethiopicAbegedeGezAlphabet);
908 }
909 case HangulConsonant: {
910 static const UChar hangulConsonantAlphabet[14] = {
911 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148,
912 0x314A, 0x314B, 0x314C, 0x314D, 0x314E
913 };
914 return toAlphabetic(value, hangulConsonantAlphabet);
915 }
916 case Hangul: {
917 static const UChar hangulAlphabet[14] = {
918 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790,
919 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558
920 };
921 return toAlphabetic(value, hangulAlphabet);
922 }
923 case Oromo:
924 case EthiopicHalehameOmEt: {
925 static const UChar ethiopicHalehameOmEtAlphabet[25] = {
926 0x1200, 0x1208, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 0x1270,
927 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8,
928 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
929 };
930 return toAlphabetic(value, ethiopicHalehameOmEtAlphabet);
931 }
932 case Sidama:
933 case EthiopicHalehameSidEt: {
934 static const UChar ethiopicHalehameSidEtAlphabet[26] = {
935 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
936 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0,
937 0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
938 };
939 return toAlphabetic(value, ethiopicHalehameSidEtAlphabet);
940 }
941 case Somali:
942 case EthiopicHalehameSoEt: {
943 static const UChar ethiopicHalehameSoEtAlphabet[22] = {
944 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
945 0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0,
946 0x1300, 0x1308, 0x1338, 0x1348
947 };
948 return toAlphabetic(value, ethiopicHalehameSoEtAlphabet);
949 }
950 case Tigre:
951 case EthiopicHalehameTig: {
952 static const UChar ethiopicHalehameTigAlphabet[27] = {
953 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
954 0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
955 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350
956 };
957 return toAlphabetic(value, ethiopicHalehameTigAlphabet);
958 }
959 case TigrinyaEr:
960 case EthiopicHalehameTiEr: {
961 static const UChar ethiopicHalehameTiErAlphabet[31] = {
962 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1250,
963 0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8,
964 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328,
965 0x1330, 0x1338, 0x1348, 0x1350
966 };
967 return toAlphabetic(value, ethiopicHalehameTiErAlphabet);
968 }
969 case TigrinyaErAbegede:
970 case EthiopicAbegedeTiEr: {
971 static const UChar ethiopicAbegedeTiErAlphabet[31] = {
972 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
973 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
974 0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238,
975 0x1270, 0x1278, 0x1330, 0x1350
976 };
977 return toAlphabetic(value, ethiopicAbegedeTiErAlphabet);
978 }
979 case TigrinyaEt:
980 case EthiopicHalehameTiEt: {
981 static const UChar ethiopicHalehameTiEtAlphabet[34] = {
982 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
983 0x1250, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8,
984 0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308,
985 0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
986 };
987 return toAlphabetic(value, ethiopicHalehameTiEtAlphabet);
988 }
989 case TigrinyaEtAbegede:
990 case EthiopicAbegedeTiEt: {
991 static const UChar ethiopicAbegedeTiEtAlphabet[34] = {
992 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
993 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
994 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230,
995 0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
996 };
997 return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet);
998 }
999 case UpperGreek: {
1000 static const UChar upperGreekAlphabet[24] = {
1001 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399,
1002 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3,
1003 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9
1004 };
1005 return toAlphabetic(value, upperGreekAlphabet);
1006 }
1007 case LowerNorwegian: {
1008 static const LChar lowerNorwegianAlphabet[29] = {
1009 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
1010 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
1011 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xE6,
1012 0xF8, 0xE5
1013 };
1014 return toAlphabetic(value, lowerNorwegianAlphabet);
1015 }
1016 case UpperNorwegian: {
1017 static const LChar upperNorwegianAlphabet[29] = {
1018 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
1019 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
1020 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xC6,
1021 0xD8, 0xC5
1022 };
1023 return toAlphabetic(value, upperNorwegianAlphabet);
1024 }
1025 case CJKIdeographic: {
1026 static const UChar traditionalChineseInformalTable[16] = {
1027 0x842C, 0x5104, 0x5146,
1028 0x5341, 0x767E, 0x5343,
1029 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB,
1030 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D
1031 };
1032 return toCJKIdeographic(value, traditionalChineseInformalTable);
1033 }
1034
1035 case LowerRoman:
1036 return toRoman(value, false);
1037 case UpperRoman:
1038 return toRoman(value, true);
1039
1040 case Armenian:
1041 case UpperArmenian:
1042 // CSS3 says "armenian" means "lower-armenian".
1043 // But the CSS2.1 test suite contains uppercase test results for "armenian",
1044 // so we'll match the test suite.
1045 return toArmenian(value, true);
1046 case LowerArmenian:
1047 return toArmenian(value, false);
1048 case Georgian:
1049 return toGeorgian(value);
1050 case Hebrew:
1051 return toHebrew(value);
1052 }
1053
1054 ASSERT_NOT_REACHED();
1055 return "";
1056}
1057
1058RenderListMarker::RenderListMarker(RenderListItem* item)
1059 : RenderBox(0)
1060 , m_listItem(item)
1061{
1062 // init RenderObject attributes
1063 setInline(true); // our object is Inline
1064 setReplaced(true); // pretend to be replaced
1065}
1066
1067RenderListMarker::~RenderListMarker()
1068{
1069 if (m_image)
1070 m_image->removeClient(this);
1071}
1072
1073RenderListMarker* RenderListMarker::createAnonymous(RenderListItem* item)
1074{
Torne (Richard Coles)8abfc582013-09-12 12:10:38 +01001075 Document& document = item->document();
Torne (Richard Coles)f5e4ad52013-08-05 13:57:57 +01001076 RenderListMarker* renderer = new RenderListMarker(item);
Torne (Richard Coles)8abfc582013-09-12 12:10:38 +01001077 renderer->setDocumentForAnonymous(&document);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001078 return renderer;
1079}
1080
1081void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
1082{
1083 if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType()))
1084 setNeedsLayoutAndPrefWidthsRecalc();
Ben Murdoch02772c62013-07-26 10:21:05 +01001085
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001086 RenderBox::styleWillChange(diff, newStyle);
1087}
1088
1089void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
1090{
1091 RenderBox::styleDidChange(diff, oldStyle);
1092
1093 if (m_image != style()->listStyleImage()) {
1094 if (m_image)
1095 m_image->removeClient(this);
1096 m_image = style()->listStyleImage();
1097 if (m_image)
1098 m_image->addClient(this);
1099 }
1100}
1101
1102InlineBox* RenderListMarker::createInlineBox()
1103{
1104 InlineBox* result = RenderBox::createInlineBox();
1105 result->setIsText(isText());
1106 return result;
1107}
1108
1109bool RenderListMarker::isImage() const
1110{
1111 return m_image && !m_image->errorOccurred();
1112}
1113
1114LayoutRect RenderListMarker::localSelectionRect()
1115{
1116 InlineBox* box = inlineBoxWrapper();
1117 if (!box)
1118 return LayoutRect(LayoutPoint(), size());
Torne (Richard Coles)09380292014-02-21 12:17:33 +00001119 RootInlineBox* root = inlineBoxWrapper()->root();
1120 LayoutUnit newLogicalTop = root->block()->style()->isFlippedBlocksWritingMode() ? inlineBoxWrapper()->logicalBottom() - root->selectionBottom() : root->selectionTop() - inlineBoxWrapper()->logicalTop();
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001121 if (root->block()->style()->isHorizontalWritingMode())
1122 return LayoutRect(0, newLogicalTop, width(), root->selectionHeight());
1123 return LayoutRect(newLogicalTop, 0, root->selectionHeight(), height());
1124}
1125
1126void RenderListMarker::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1127{
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001128 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
1129
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001130 if (paintInfo.phase != PaintPhaseForeground)
1131 return;
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001132
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001133 if (style()->visibility() != VISIBLE)
1134 return;
1135
1136 LayoutPoint boxOrigin(paintOffset + location());
1137 LayoutRect overflowRect(visualOverflowRect());
1138 overflowRect.moveBy(boxOrigin);
1139 overflowRect.inflate(maximalOutlineSize(paintInfo.phase));
1140
1141 if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
1142 return;
1143
1144 LayoutRect box(boxOrigin, size());
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001145
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001146 IntRect marker = getRelativeMarkerRect();
1147 marker.moveBy(roundedIntPoint(boxOrigin));
1148
1149 GraphicsContext* context = paintInfo.context;
1150
1151 if (isImage()) {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001152 context->drawImage(m_image->image(this, marker.size()).get(), marker);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001153 if (selectionState() != SelectionNone) {
1154 LayoutRect selRect = localSelectionRect();
1155 selRect.moveBy(boxOrigin);
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001156 context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001157 }
1158 return;
1159 }
1160
1161 if (selectionState() != SelectionNone) {
1162 LayoutRect selRect = localSelectionRect();
1163 selRect.moveBy(boxOrigin);
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001164 context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001165 }
1166
Ben Murdoch7757ec22013-07-23 11:17:36 +01001167 const Color color(resolveColor(CSSPropertyColor));
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001168 context->setStrokeColor(color);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001169 context->setStrokeStyle(SolidStroke);
1170 context->setStrokeThickness(1.0f);
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001171 context->setFillColor(color);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001172
1173 EListStyleType type = style()->listStyleType();
1174 switch (type) {
1175 case Disc:
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001176 context->fillEllipse(marker);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001177 return;
1178 case Circle:
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001179 context->strokeEllipse(marker);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001180 return;
1181 case Square:
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001182 context->fillRect(marker);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001183 return;
1184 case NoneListStyle:
1185 return;
1186 case Afar:
1187 case Amharic:
1188 case AmharicAbegede:
1189 case ArabicIndic:
1190 case Armenian:
1191 case BinaryListStyle:
1192 case Bengali:
1193 case Cambodian:
1194 case CJKIdeographic:
1195 case CjkEarthlyBranch:
1196 case CjkHeavenlyStem:
1197 case DecimalLeadingZero:
1198 case DecimalListStyle:
1199 case Devanagari:
1200 case Ethiopic:
1201 case EthiopicAbegede:
1202 case EthiopicAbegedeAmEt:
1203 case EthiopicAbegedeGez:
1204 case EthiopicAbegedeTiEr:
1205 case EthiopicAbegedeTiEt:
1206 case EthiopicHalehameAaEr:
1207 case EthiopicHalehameAaEt:
1208 case EthiopicHalehameAmEt:
1209 case EthiopicHalehameGez:
1210 case EthiopicHalehameOmEt:
1211 case EthiopicHalehameSidEt:
1212 case EthiopicHalehameSoEt:
1213 case EthiopicHalehameTiEr:
1214 case EthiopicHalehameTiEt:
1215 case EthiopicHalehameTig:
1216 case Georgian:
1217 case Gujarati:
1218 case Gurmukhi:
1219 case Hangul:
1220 case HangulConsonant:
1221 case Hebrew:
1222 case Hiragana:
1223 case HiraganaIroha:
1224 case Kannada:
1225 case Katakana:
1226 case KatakanaIroha:
1227 case Khmer:
1228 case Lao:
1229 case LowerAlpha:
1230 case LowerArmenian:
1231 case LowerGreek:
1232 case LowerHexadecimal:
1233 case LowerLatin:
1234 case LowerNorwegian:
1235 case LowerRoman:
1236 case Malayalam:
1237 case Mongolian:
1238 case Myanmar:
1239 case Octal:
1240 case Oriya:
1241 case Oromo:
1242 case Persian:
1243 case Sidama:
1244 case Somali:
1245 case Telugu:
1246 case Thai:
1247 case Tibetan:
1248 case Tigre:
1249 case TigrinyaEr:
1250 case TigrinyaErAbegede:
1251 case TigrinyaEt:
1252 case TigrinyaEtAbegede:
1253 case UpperAlpha:
1254 case UpperArmenian:
1255 case UpperGreek:
1256 case UpperHexadecimal:
1257 case UpperLatin:
1258 case UpperNorwegian:
1259 case UpperRoman:
1260 case Urdu:
1261 case Asterisks:
1262 case Footnotes:
1263 break;
1264 }
1265 if (m_text.isEmpty())
1266 return;
1267
1268 const Font& font = style()->font();
Torne (Richard Coles)19cde672013-11-06 12:28:04 +00001269 TextRun textRun = RenderBlockFlow::constructTextRun(this, font, m_text, style());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001270
1271 GraphicsContextStateSaver stateSaver(*context, false);
1272 if (!style()->isHorizontalWritingMode()) {
1273 marker.moveBy(roundedIntPoint(-boxOrigin));
1274 marker = marker.transposedRect();
1275 marker.moveBy(IntPoint(roundToInt(box.x()), roundToInt(box.y() - logicalHeight())));
1276 stateSaver.save();
1277 context->translate(marker.x(), marker.maxY());
1278 context->rotate(static_cast<float>(deg2rad(90.)));
1279 context->translate(-marker.x(), -marker.maxY());
1280 }
1281
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001282 TextRunPaintInfo textRunPaintInfo(textRun);
1283 textRunPaintInfo.bounds = marker;
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001284 IntPoint textOrigin = IntPoint(marker.x(), marker.y() + style()->fontMetrics().ascent());
1285
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001286 if (type == Asterisks || type == Footnotes) {
1287 context->drawText(font, textRunPaintInfo, textOrigin);
1288 }
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001289 else {
1290 // Text is not arbitrary. We can judge whether it's RTL from the first character,
1291 // and we only need to handle the direction RightToLeft for now.
1292 bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
1293 StringBuilder reversedText;
1294 if (textNeedsReversing) {
1295 int length = m_text.length();
1296 reversedText.reserveCapacity(length);
1297 for (int i = length - 1; i >= 0; --i)
1298 reversedText.append(m_text[i]);
Ben Murdoche69819b2013-07-17 14:56:49 +01001299 ASSERT(reversedText.length() == reversedText.capacity());
1300 textRun.setText(reversedText.toString());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001301 }
1302
1303 const UChar suffix = listMarkerSuffix(type, m_listItem->value());
Torne (Richard Coles)09380292014-02-21 12:17:33 +00001304 UChar suffixStr[2] = {
1305 style()->isLeftToRightDirection() ? suffix : ' ',
1306 style()->isLeftToRightDirection() ? ' ' : suffix
1307 };
1308 TextRun suffixRun = RenderBlockFlow::constructTextRun(this, font, suffixStr, 2, style(), style()->direction());
1309 TextRunPaintInfo suffixRunInfo(suffixRun);
1310 suffixRunInfo.bounds = marker;
1311
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001312 if (style()->isLeftToRightDirection()) {
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001313 context->drawText(font, textRunPaintInfo, textOrigin);
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001314 context->drawText(font, suffixRunInfo, textOrigin + IntSize(font.width(textRun), 0));
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001315 } else {
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001316 context->drawText(font, suffixRunInfo, textOrigin);
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001317 context->drawText(font, textRunPaintInfo, textOrigin + IntSize(font.width(suffixRun), 0));
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001318 }
1319 }
1320}
1321
1322void RenderListMarker::layout()
1323{
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001324 ASSERT(needsLayout());
Ben Murdoch02772c62013-07-26 10:21:05 +01001325
Torne (Richard Coles)51b29062013-11-28 11:56:03 +00001326 LayoutRectRecorder recorder(*this);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001327 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
Ben Murdoch3c9e4ae2013-08-12 14:20:44 +01001346 clearNeedsLayout();
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001347}
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();
Torne (Richard Coles)c0e19a62013-08-30 15:15:11 +01001483 clearPreferredLogicalWidthsDirty();
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001484 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()), ' ' };
Torne (Richard Coles)09380292014-02-21 12:17:33 +00001584 LayoutUnit suffixSpaceWidth = font.width(RenderBlockFlow::constructTextRun(this, font, suffixSpace, 2, style(), style()->direction()));
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001585 logicalWidth = itemWidth + suffixSpaceWidth;
1586 }
1587 break;
1588 }
1589
1590 m_minPreferredLogicalWidth = logicalWidth;
1591 m_maxPreferredLogicalWidth = logicalWidth;
1592
Torne (Richard Coles)c0e19a62013-08-30 15:15:11 +01001593 clearPreferredLogicalWidthsDirty();
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001594
1595 updateMargins();
1596}
1597
1598void RenderListMarker::updateMargins()
1599{
1600 const FontMetrics& fontMetrics = style()->fontMetrics();
1601
1602 LayoutUnit marginStart = 0;
1603 LayoutUnit marginEnd = 0;
1604
Torne (Richard Coles)8abfc582013-09-12 12:10:38 +01001605 if (isInside()) {
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001606 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 }
Ben Murdoch02772c62013-07-26 10:21:05 +01001656
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001657 }
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 ASSERT(linePositionMode == PositionOnContainingLine);
1673 if (!isImage())
1674 return m_listItem->baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes);
1675 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1676}
1677
1678String RenderListMarker::suffix() const
1679{
1680 EListStyleType type = style()->listStyleType();
1681 const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1682
1683 if (suffix == ' ')
1684 return String(" ");
1685
1686 // If the suffix is not ' ', an extra space is needed
1687 UChar data[2];
1688 if (style()->isLeftToRightDirection()) {
1689 data[0] = suffix;
1690 data[1] = ' ';
1691 } else {
1692 data[0] = ' ';
1693 data[1] = suffix;
1694 }
1695
1696 return String(data, 2);
1697}
1698
1699bool RenderListMarker::isInside() const
1700{
1701 return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
1702}
1703
1704IntRect RenderListMarker::getRelativeMarkerRect()
1705{
1706 if (isImage())
1707 return IntRect(0, 0, m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height());
Ben Murdoch02772c62013-07-26 10:21:05 +01001708
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001709 IntRect relativeRect;
1710 EListStyleType type = style()->listStyleType();
1711 switch (type) {
1712 case Asterisks:
1713 case Footnotes: {
1714 const Font& font = style()->font();
1715 relativeRect = IntRect(0, 0, font.width(m_text), font.fontMetrics().height());
1716 break;
1717 }
1718 case Disc:
1719 case Circle:
1720 case Square: {
1721 // FIXME: Are these particular rounding rules necessary?
1722 const FontMetrics& fontMetrics = style()->fontMetrics();
1723 int ascent = fontMetrics.ascent();
1724 int bulletWidth = (ascent * 2 / 3 + 1) / 2;
1725 relativeRect = IntRect(1, 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
1726 break;
1727 }
1728 case NoneListStyle:
1729 return IntRect();
1730 case Afar:
1731 case Amharic:
1732 case AmharicAbegede:
1733 case ArabicIndic:
1734 case Armenian:
1735 case BinaryListStyle:
1736 case Bengali:
1737 case Cambodian:
1738 case CJKIdeographic:
1739 case CjkEarthlyBranch:
1740 case CjkHeavenlyStem:
1741 case DecimalLeadingZero:
1742 case DecimalListStyle:
1743 case Devanagari:
1744 case Ethiopic:
1745 case EthiopicAbegede:
1746 case EthiopicAbegedeAmEt:
1747 case EthiopicAbegedeGez:
1748 case EthiopicAbegedeTiEr:
1749 case EthiopicAbegedeTiEt:
1750 case EthiopicHalehameAaEr:
1751 case EthiopicHalehameAaEt:
1752 case EthiopicHalehameAmEt:
1753 case EthiopicHalehameGez:
1754 case EthiopicHalehameOmEt:
1755 case EthiopicHalehameSidEt:
1756 case EthiopicHalehameSoEt:
1757 case EthiopicHalehameTiEr:
1758 case EthiopicHalehameTiEt:
1759 case EthiopicHalehameTig:
1760 case Georgian:
1761 case Gujarati:
1762 case Gurmukhi:
1763 case Hangul:
1764 case HangulConsonant:
1765 case Hebrew:
1766 case Hiragana:
1767 case HiraganaIroha:
1768 case Kannada:
1769 case Katakana:
1770 case KatakanaIroha:
1771 case Khmer:
1772 case Lao:
1773 case LowerAlpha:
1774 case LowerArmenian:
1775 case LowerGreek:
1776 case LowerHexadecimal:
1777 case LowerLatin:
1778 case LowerNorwegian:
1779 case LowerRoman:
1780 case Malayalam:
1781 case Mongolian:
1782 case Myanmar:
1783 case Octal:
1784 case Oriya:
1785 case Oromo:
1786 case Persian:
1787 case Sidama:
1788 case Somali:
1789 case Telugu:
1790 case Thai:
1791 case Tibetan:
1792 case Tigre:
1793 case TigrinyaEr:
1794 case TigrinyaErAbegede:
1795 case TigrinyaEt:
1796 case TigrinyaEtAbegede:
1797 case UpperAlpha:
1798 case UpperArmenian:
1799 case UpperGreek:
1800 case UpperHexadecimal:
1801 case UpperLatin:
1802 case UpperNorwegian:
1803 case UpperRoman:
1804 case Urdu:
1805 if (m_text.isEmpty())
1806 return IntRect();
1807 const Font& font = style()->font();
1808 int itemWidth = font.width(m_text);
1809 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
Torne (Richard Coles)09380292014-02-21 12:17:33 +00001810 int suffixSpaceWidth = font.width(RenderBlockFlow::constructTextRun(this, font, suffixSpace, 2, style(), style()->direction()));
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001811 relativeRect = IntRect(0, 0, itemWidth + suffixSpaceWidth, font.fontMetrics().height());
1812 }
1813
1814 if (!style()->isHorizontalWritingMode()) {
1815 relativeRect = relativeRect.transposedRect();
1816 relativeRect.setX(width() - relativeRect.x() - relativeRect.width());
1817 }
1818
1819 return relativeRect;
1820}
1821
1822void RenderListMarker::setSelectionState(SelectionState state)
1823{
1824 // The selection state for our containing block hierarchy is updated by the base class call.
1825 RenderBox::setSelectionState(state);
1826
Torne (Richard Coles)09380292014-02-21 12:17:33 +00001827 if (inlineBoxWrapper() && canUpdateSelectionOnRootLineBoxes())
1828 if (RootInlineBox* root = inlineBoxWrapper()->root())
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001829 root->setHasSelectedChildren(state != SelectionNone);
1830}
1831
1832LayoutRect RenderListMarker::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent)
1833{
1834 ASSERT(!needsLayout());
1835
1836 if (selectionState() == SelectionNone || !inlineBoxWrapper())
1837 return LayoutRect();
1838
1839 RootInlineBox* root = inlineBoxWrapper()->root();
1840 LayoutRect rect(0, root->selectionTop() - y(), width(), root->selectionHeight());
Ben Murdoch02772c62013-07-26 10:21:05 +01001841
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001842 if (clipToVisibleContent)
1843 computeRectForRepaint(repaintContainer, rect);
1844 else
1845 rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
Ben Murdoch02772c62013-07-26 10:21:05 +01001846
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001847 return rect;
1848}
1849
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001850} // namespace WebCore