2009-11-19 Rahul Kuchhal <kuchhal@chromium.org>
[WebKit-https.git] / WebCore / rendering / RenderListMarker.cpp
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  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  */
23
24 #include "config.h"
25 #include "RenderListMarker.h"
26
27 #include "CachedImage.h"
28 #include "CharacterNames.h"
29 #include "Document.h"
30 #include "GraphicsContext.h"
31 #include "RenderLayer.h"
32 #include "RenderListItem.h"
33 #include "RenderView.h"
34
35 using namespace std;
36 using namespace WTF;
37 using namespace Unicode;
38
39 namespace WebCore {
40
41 const int cMarkerPadding = 7;
42
43 static String toRoman(int number, bool upper)
44 {
45     // FIXME: CSS3 describes how to make this work for much larger numbers,
46     // using overbars and special characters. It also specifies the characters
47     // in the range U+2160 to U+217F instead of standard ASCII ones.
48     if (number < 1 || number > 3999)
49         return String::number(number);
50
51     // Big enough to store largest roman number less than 3999 which
52     // is 3888 (MMMDCCCLXXXVIII)
53     const int lettersSize = 15;
54     UChar letters[lettersSize];
55
56     int length = 0;
57     const UChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
58     const UChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
59     const UChar* digits = upper ? udigits : ldigits;
60     int d = 0;
61     do {
62         int num = number % 10;
63         if (num % 5 < 4)
64             for (int i = num % 5; i > 0; i--)
65                 letters[lettersSize - ++length] = digits[d];
66         if (num >= 4 && num <= 8)
67             letters[lettersSize - ++length] = digits[d + 1];
68         if (num == 9)
69             letters[lettersSize - ++length] = digits[d + 2];
70         if (num % 5 == 4)
71             letters[lettersSize - ++length] = digits[d];
72         number /= 10;
73         d += 2;
74     } while (number);
75
76     ASSERT(length <= lettersSize);
77     return String(&letters[lettersSize - length], length);
78 }
79
80 static String toAlphabetic(int number, const UChar* alphabet, int alphabetSize)
81 {
82     ASSERT(alphabetSize >= 10);
83
84     if (number < 1)
85         return String::number(number);
86
87     const int lettersSize = 10; // big enough for a 32-bit int, with a 10-letter alphabet
88     UChar letters[lettersSize];
89
90     --number;
91     letters[lettersSize - 1] = alphabet[number % alphabetSize];
92     int length = 1;
93     while ((number /= alphabetSize) > 0)
94         letters[lettersSize - ++length] = alphabet[number % alphabetSize - 1];
95
96     ASSERT(length <= lettersSize);
97     return String(&letters[lettersSize - length], length);
98 }
99
100 static int toHebrewUnder1000(int number, UChar letters[5])
101 {
102     // FIXME: CSS3 mentions various refinements not implemented here.
103     // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
104     ASSERT(number >= 0 && number < 1000);
105     int length = 0;
106     int fourHundreds = number / 400;
107     for (int i = 0; i < fourHundreds; i++)
108         letters[length++] = 1511 + 3;
109     number %= 400;
110     if (number / 100)
111         letters[length++] = 1511 + (number / 100) - 1;
112     number %= 100;
113     if (number == 15 || number == 16) {
114         letters[length++] = 1487 + 9;
115         letters[length++] = 1487 + number - 9;
116     } else {
117         if (int tens = number / 10) {
118             static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
119             letters[length++] = hebrewTens[tens - 1];
120         }
121         if (int ones = number % 10)
122             letters[length++] = 1487 + ones;
123     }
124     ASSERT(length <= 5);
125     return length;
126 }
127
128 static String toHebrew(int number)
129 {
130     // FIXME: CSS3 mentions ways to make this work for much larger numbers.
131     if (number < 0 || number > 999999)
132         return String::number(number);
133
134     if (number == 0) {
135         static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
136         return String(hebrewZero, 3);
137     }
138
139     const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
140     UChar letters[lettersSize];
141
142     int length;
143     if (number < 1000)
144         length = 0;
145     else {
146         length = toHebrewUnder1000(number / 1000, letters);
147         letters[length++] = '\'';
148         number = number % 1000;
149     }
150     length += toHebrewUnder1000(number, letters + length);
151
152     ASSERT(length <= lettersSize);
153     return String(letters, length);
154 }
155
156 static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
157 {
158     ASSERT(number >= 0 && number < 10000);
159     int length = 0;
160
161     int lowerOffset = upper ? 0 : 0x0030;
162
163     if (int thousands = number / 1000) {
164         if (thousands == 7) {
165             letters[length++] = 0x0548 + lowerOffset;
166             letters[length++] = 0x0552 + lowerOffset;
167             if (addCircumflex)
168                 letters[length++] = 0x0302;
169         } else {
170             letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
171             if (addCircumflex)
172                 letters[length++] = 0x0302;
173         }
174     }
175
176     if (int hundreds = (number / 100) % 10) {
177         letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
178         if (addCircumflex)
179             letters[length++] = 0x0302;
180     }
181
182     if (int tens = (number / 10) % 10) {
183         letters[length++] = (0x053A - 1 + lowerOffset) + tens;
184         if (addCircumflex)
185             letters[length++] = 0x0302;
186     }
187
188     if (int ones = number % 10) {
189         letters[length++] = (0x531 - 1 + lowerOffset) + ones;
190         if (addCircumflex)
191             letters[length++] = 0x0302;
192     }
193
194     return length;
195 }
196
197 static String toArmenian(int number, bool upper)
198 {
199     if (number < 1 || number > 99999999)
200         return String::number(number);
201
202     const int lettersSize = 18; // twice what toArmenianUnder10000 needs
203     UChar letters[lettersSize];
204
205     int length = toArmenianUnder10000(number / 10000, upper, true, letters);
206     length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
207
208     ASSERT(length <= lettersSize);
209     return String(letters, length);
210 }
211
212 static String toGeorgian(int number)
213 {
214     if (number < 1 || number > 19999)
215         return String::number(number);
216
217     const int lettersSize = 5;
218     UChar letters[lettersSize];
219
220     int length = 0;
221
222     if (number > 9999)
223         letters[length++] = 0x10F5;
224
225     if (int thousands = (number / 1000) % 10) {
226         static const UChar georgianThousands[9] = {
227             0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
228         };
229         letters[length++] = georgianThousands[thousands - 1];
230     }
231
232     if (int hundreds = (number / 100) % 10) {
233         static const UChar georgianHundreds[9] = {
234             0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
235         };
236         letters[length++] = georgianHundreds[hundreds - 1];
237     }
238
239     if (int tens = (number / 10) % 10) {
240         static const UChar georgianTens[9] = {
241             0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
242         };
243         letters[length++] = georgianTens[tens - 1];
244     }
245
246     if (int ones = number % 10) {
247         static const UChar georgianOnes[9] = {
248             0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
249         };
250         letters[length++] = georgianOnes[ones - 1];
251     }
252
253     ASSERT(length <= lettersSize);
254     return String(letters, length);
255 }
256
257 // The table uses the order from the CSS3 specification:
258 // first 3 group markers, then 3 digit markers, then ten digits.
259 static String toCJKIdeographic(int number, const UChar table[16])
260 {
261     if (number < 0)
262         return String::number(number);
263
264     enum AbstractCJKChar {
265         noChar,
266         secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
267         secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
268         digit0, digit1, digit2, digit3, digit4,
269         digit5, digit6, digit7, digit8, digit9
270     };
271
272     if (number == 0)
273         return String(&table[digit0 - 1], 1);
274
275     const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker
276     const int bufferLength = 4 * groupLength;
277     AbstractCJKChar buffer[bufferLength] = { noChar };
278
279     for (int i = 0; i < 4; ++i) {
280         int groupValue = number % 10000;
281         number /= 10000;
282
283         // Process least-significant group first, but put it in the buffer last.
284         AbstractCJKChar* group = &buffer[(3 - i) * groupLength];
285
286         if (groupValue && i)
287             group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i);
288
289         // Put in the four digits and digit markers for any non-zero digits.
290         group[6] = static_cast<AbstractCJKChar>(digit0 + (groupValue % 10));
291         if (number != 0 || groupValue > 9) {
292             int digitValue = ((groupValue / 10) % 10);
293             group[4] = static_cast<AbstractCJKChar>(digit0 + digitValue);
294             if (digitValue)
295                 group[5] = secondDigitMarker;
296         }
297         if (number != 0 || groupValue > 99) {
298             int digitValue = ((groupValue / 100) % 10);
299             group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue);
300             if (digitValue)
301                 group[3] = thirdDigitMarker;
302         }
303         if (number != 0 || groupValue > 999) {
304             int digitValue = groupValue / 1000;
305             group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue);
306             if (digitValue)
307                 group[1] = fourthDigitMarker;
308         }
309
310         // Remove the tens digit, but leave the marker, for any group that has
311         // a value of less than 20.
312         if (groupValue < 20) {
313             ASSERT(group[4] == noChar || group[4] == digit0 || group[4] == digit1);
314             group[4] = noChar;
315         }
316
317         if (number == 0)
318             break;
319     }
320
321     // Convert into characters, omitting consecutive runs of digit0 and
322     // any trailing digit0.
323     int length = 0;
324     UChar characters[bufferLength];
325     AbstractCJKChar last = noChar;
326     for (int i = 0; i < bufferLength; ++i) {
327         AbstractCJKChar a = buffer[i];
328         if (a != noChar) {
329             if (a != digit0 || last != digit0)
330                 characters[length++] = table[a - 1];
331             last = a;
332         }
333     }
334     if (last == digit0)
335         --length;
336
337     return String(characters, length);
338 }
339
340 String listMarkerText(EListStyleType type, int value)
341 {
342     switch (type) {
343         case LNONE:
344             return "";
345
346         // We use the same characters for text security.
347         // See RenderText::setInternalString.
348         case CIRCLE:
349             return String(&whiteBullet, 1);
350         case DISC:
351             return String(&bullet, 1);
352         case SQUARE:
353             // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
354             // instead, but I think this looks better.
355             return String(&blackSquare, 1);
356
357         case LDECIMAL:
358             return String::number(value);
359         case DECIMAL_LEADING_ZERO:
360             if (value < -9 || value > 9)
361                 return String::number(value);
362             if (value < 0)
363                 return "-0" + String::number(-value); // -01 to -09
364             return "0" + String::number(value); // 00 to 09
365
366         case LOWER_ALPHA:
367         case LOWER_LATIN: {
368             static const UChar lowerLatinAlphabet[26] = {
369                 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
370                 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
371             };
372             return toAlphabetic(value, lowerLatinAlphabet, 26);
373         }
374         case UPPER_ALPHA:
375         case UPPER_LATIN: {
376             static const UChar upperLatinAlphabet[26] = {
377                 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
378                 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
379             };
380             return toAlphabetic(value, upperLatinAlphabet, 26);
381         }
382         case LOWER_GREEK: {
383             static const UChar lowerGreekAlphabet[24] = {
384                 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
385                 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
386                 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9
387             };
388             return toAlphabetic(value, lowerGreekAlphabet, 24);
389         }
390
391         case HIRAGANA: {
392             // FIXME: This table comes from the CSS3 draft, and is probably
393             // incorrect, given the comments in that draft.
394             static const UChar hiraganaAlphabet[48] = {
395                 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
396                 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
397                 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
398                 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
399                 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
400                 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093
401             };
402             return toAlphabetic(value, hiraganaAlphabet, 48);
403         }
404         case HIRAGANA_IROHA: {
405             // FIXME: This table comes from the CSS3 draft, and is probably
406             // incorrect, given the comments in that draft.
407             static const UChar hiraganaIrohaAlphabet[47] = {
408                 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061,
409                 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F,
410                 0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046,
411                 0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075,
412                 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
413                 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059
414             };
415             return toAlphabetic(value, hiraganaIrohaAlphabet, 47);
416         }
417         case KATAKANA: {
418             // FIXME: This table comes from the CSS3 draft, and is probably
419             // incorrect, given the comments in that draft.
420             static const UChar katakanaAlphabet[48] = {
421                 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF,
422                 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF,
423                 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD,
424                 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF,
425                 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
426                 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3
427             };
428             return toAlphabetic(value, katakanaAlphabet, 48);
429         }
430         case KATAKANA_IROHA: {
431             // FIXME: This table comes from the CSS3 draft, and is probably
432             // incorrect, given the comments in that draft.
433             static const UChar katakanaIrohaAlphabet[47] = {
434                 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1,
435                 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF,
436                 0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6,
437                 0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5,
438                 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
439                 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9
440             };
441             return toAlphabetic(value, katakanaIrohaAlphabet, 47);
442         }
443
444         case CJK_IDEOGRAPHIC: {
445             static const UChar traditionalChineseInformalTable[16] = {
446                 0x842C, 0x5104, 0x5146,
447                 0x5341, 0x767E, 0x5343,
448                 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB,
449                 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D
450             };
451             return toCJKIdeographic(value, traditionalChineseInformalTable);
452         }
453
454         case LOWER_ROMAN:
455             return toRoman(value, false);
456         case UPPER_ROMAN:
457             return toRoman(value, true);
458
459         case ARMENIAN:
460             // CSS3 says "armenian" means "lower-armenian".
461             // But the CSS2.1 test suite contains uppercase test results for "armenian",
462             // so we'll match the test suite.
463             return toArmenian(value, true);
464         case GEORGIAN:
465             return toGeorgian(value);
466         case HEBREW:
467             return toHebrew(value);
468     }
469
470     ASSERT_NOT_REACHED();
471     return "";
472 }
473
474 RenderListMarker::RenderListMarker(RenderListItem* item)
475     : RenderBox(item->document())
476     , m_listItem(item)
477 {
478     // init RenderObject attributes
479     setInline(true);   // our object is Inline
480     setReplaced(true); // pretend to be replaced
481 }
482
483 RenderListMarker::~RenderListMarker()
484 {
485     if (m_image)
486         m_image->removeClient(this);
487 }
488
489 void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
490 {
491     if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType()))
492         setNeedsLayoutAndPrefWidthsRecalc();
493     
494     RenderBox::styleWillChange(diff, newStyle);
495 }
496
497 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
498 {
499     RenderBox::styleDidChange(diff, oldStyle);
500
501     if (m_image != style()->listStyleImage()) {
502         if (m_image)
503             m_image->removeClient(this);
504         m_image = style()->listStyleImage();
505         if (m_image)
506             m_image->addClient(this);
507     }
508 }
509
510 InlineBox* RenderListMarker::createInlineBox()
511 {
512     InlineBox* result = RenderBox::createInlineBox();
513     result->setIsText(isText());
514     return result;
515 }
516
517 bool RenderListMarker::isImage() const
518 {
519     return m_image && !m_image->errorOccurred();
520 }
521
522 void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty)
523 {
524     if (paintInfo.phase != PaintPhaseForeground)
525         return;
526     
527     if (style()->visibility() != VISIBLE)
528         return;
529
530     IntRect marker = getRelativeMarkerRect();
531     marker.move(tx, ty);
532
533     IntRect box(tx + x(), ty + y(), width(), height());
534
535     if (box.y() > paintInfo.rect.bottom() || box.y() + box.height() < paintInfo.rect.y())
536         return;
537
538     if (hasBoxDecorations()) 
539         paintBoxDecorations(paintInfo, box.x(), box.y());
540
541     GraphicsContext* context = paintInfo.context;
542
543     if (isImage()) {
544 #if PLATFORM(MAC)
545         if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
546             paintCustomHighlight(tx, ty, style()->highlight(), true);
547 #endif
548         context->drawImage(m_image->image(this, marker.size()), marker.location());
549         if (selectionState() != SelectionNone) {
550             // FIXME: selectionRect() is in absolute, not painting coordinates.
551             context->fillRect(selectionRect(), selectionBackgroundColor(), style()->colorSpace());
552         }
553         return;
554     }
555
556 #if PLATFORM(MAC)
557     // FIXME: paint gap between marker and list item proper
558     if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
559         paintCustomHighlight(tx, ty, style()->highlight(), true);
560 #endif
561
562     if (selectionState() != SelectionNone) {
563         // FIXME: selectionRect() is in absolute, not painting coordinates.
564         context->fillRect(selectionRect(), selectionBackgroundColor(), style()->colorSpace());
565     }
566
567     const Color color(style()->color());
568     context->setStrokeColor(color, style()->colorSpace());
569     context->setStrokeStyle(SolidStroke);
570     context->setStrokeThickness(1.0f);
571     context->setFillColor(color, style()->colorSpace());
572
573     switch (style()->listStyleType()) {
574         case DISC:
575             context->drawEllipse(marker);
576             return;
577         case CIRCLE:
578             context->setFillColor(Color::transparent, DeviceColorSpace);
579             context->drawEllipse(marker);
580             return;
581         case SQUARE:
582             context->drawRect(marker);
583             return;
584         case LNONE:
585             return;
586         case ARMENIAN:
587         case CJK_IDEOGRAPHIC:
588         case DECIMAL_LEADING_ZERO:
589         case GEORGIAN:
590         case HEBREW:
591         case HIRAGANA:
592         case HIRAGANA_IROHA:
593         case KATAKANA:
594         case KATAKANA_IROHA:
595         case LDECIMAL:
596         case LOWER_ALPHA:
597         case LOWER_GREEK:
598         case LOWER_LATIN:
599         case LOWER_ROMAN:
600         case UPPER_ALPHA:
601         case UPPER_LATIN:
602         case UPPER_ROMAN:
603             break;
604     }
605     if (m_text.isEmpty())
606         return;
607
608     TextRun textRun(m_text);
609
610     // Text is not arbitrary. We can judge whether it's RTL from the first character,
611     // and we only need to handle the direction RightToLeft for now.
612     bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
613     Vector<UChar> reversedText;
614     if (textNeedsReversing) {
615         int length = m_text.length();
616         reversedText.grow(length);
617         for (int i = 0; i < length; ++i)
618             reversedText[length - i - 1] = m_text[i];
619         textRun = TextRun(reversedText.data(), length);
620     }
621
622     const Font& font = style()->font();
623     if (style()->direction() == LTR) {
624         int width = font.width(textRun);
625         context->drawText(style()->font(), textRun, marker.location());
626         const UChar periodSpace[2] = { '.', ' ' };
627         context->drawText(style()->font(), TextRun(periodSpace, 2), marker.location() + IntSize(width, 0));
628     } else {
629         const UChar spacePeriod[2] = { ' ', '.' };
630         TextRun spacePeriodRun(spacePeriod, 2);
631         int width = font.width(spacePeriodRun);
632         context->drawText(style()->font(), spacePeriodRun, marker.location());
633         context->drawText(style()->font(), textRun, marker.location() + IntSize(width, 0));
634     }
635 }
636
637 void RenderListMarker::layout()
638 {
639     ASSERT(needsLayout());
640     ASSERT(!prefWidthsDirty());
641
642     if (isImage()) {
643         setWidth(m_image->imageSize(this, style()->effectiveZoom()).width());
644         setHeight(m_image->imageSize(this, style()->effectiveZoom()).height());
645     } else {
646         setWidth(minPrefWidth());
647         setHeight(style()->font().height());
648     }
649
650     m_marginLeft = m_marginRight = 0;
651
652     Length leftMargin = style()->marginLeft();
653     Length rightMargin = style()->marginRight();
654     if (leftMargin.isFixed())
655         m_marginLeft = leftMargin.value();
656     if (rightMargin.isFixed())
657         m_marginRight = rightMargin.value();
658
659     setNeedsLayout(false);
660 }
661
662 void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*)
663 {
664     // A list marker can't have a background or border image, so no need to call the base class method.
665     if (o != m_image->data())
666         return;
667
668     if (width() != m_image->imageSize(this, style()->effectiveZoom()).width() || height() != m_image->imageSize(this, style()->effectiveZoom()).height() || m_image->errorOccurred())
669         setNeedsLayoutAndPrefWidthsRecalc();
670     else
671         repaint();
672 }
673
674 void RenderListMarker::calcPrefWidths()
675 {
676     ASSERT(prefWidthsDirty());
677
678     m_text = "";
679
680     const Font& font = style()->font();
681
682     if (isImage()) {
683         // FIXME: This is a somewhat arbitrary width.  Generated images for markers really won't become particularly useful
684         // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box.
685         int bulletWidth = font.ascent() / 2;
686         m_image->setImageContainerSize(IntSize(bulletWidth, bulletWidth));
687         m_minPrefWidth = m_maxPrefWidth = m_image->imageSize(this, style()->effectiveZoom()).width();
688         setPrefWidthsDirty(false);
689         updateMargins();
690         return;
691     }
692
693     int width = 0;
694     EListStyleType type = style()->listStyleType();
695     switch (type) {
696         case LNONE:
697             break;
698         case CIRCLE:
699         case DISC:
700         case SQUARE:
701             m_text = listMarkerText(type, 0); // value is ignored for these types
702             width = (font.ascent() * 2 / 3 + 1) / 2 + 2;
703             break;
704         case ARMENIAN:
705         case CJK_IDEOGRAPHIC:
706         case DECIMAL_LEADING_ZERO:
707         case GEORGIAN:
708         case HEBREW:
709         case HIRAGANA:
710         case HIRAGANA_IROHA:
711         case KATAKANA:
712         case KATAKANA_IROHA:
713         case LDECIMAL:
714         case LOWER_ALPHA:
715         case LOWER_GREEK:
716         case LOWER_LATIN:
717         case LOWER_ROMAN:
718         case UPPER_ALPHA:
719         case UPPER_LATIN:
720         case UPPER_ROMAN:
721             m_text = listMarkerText(type, m_listItem->value());
722             if (m_text.isEmpty())
723                 width = 0;
724             else {
725                 int itemWidth = font.width(m_text);
726                 const UChar periodSpace[2] = { '.', ' ' };
727                 int periodSpaceWidth = font.width(TextRun(periodSpace, 2));
728                 width = itemWidth + periodSpaceWidth;
729             }
730             break;
731     }
732
733     m_minPrefWidth = width;
734     m_maxPrefWidth = width;
735
736     setPrefWidthsDirty(false);
737     
738     updateMargins();
739 }
740
741 void RenderListMarker::updateMargins()
742 {
743     const Font& font = style()->font();
744
745     int marginLeft = 0;
746     int marginRight = 0;
747
748     if (isInside()) {
749         if (isImage()) {
750             if (style()->direction() == LTR)
751                 marginRight = cMarkerPadding;
752             else
753                 marginLeft = cMarkerPadding;
754         } else switch (style()->listStyleType()) {
755             case DISC:
756             case CIRCLE:
757             case SQUARE:
758                 if (style()->direction() == LTR) {
759                     marginLeft = -1;
760                     marginRight = font.ascent() - minPrefWidth() + 1;
761                 } else {
762                     marginLeft = font.ascent() - minPrefWidth() + 1;
763                     marginRight = -1;
764                 }
765                 break;
766             default:
767                 break;
768         }
769     } else {
770         if (style()->direction() == LTR) {
771             if (isImage())
772                 marginLeft = -minPrefWidth() - cMarkerPadding;
773             else {
774                 int offset = font.ascent() * 2 / 3;
775                 switch (style()->listStyleType()) {
776                     case DISC:
777                     case CIRCLE:
778                     case SQUARE:
779                         marginLeft = -offset - cMarkerPadding - 1;
780                         break;
781                     case LNONE:
782                         break;
783                     default:
784                         marginLeft = m_text.isEmpty() ? 0 : -minPrefWidth() - offset / 2;
785                 }
786             }
787         } else {
788             if (isImage())
789                 marginLeft = cMarkerPadding;
790             else {
791                 int offset = font.ascent() * 2 / 3;
792                 switch (style()->listStyleType()) {
793                     case DISC:
794                     case CIRCLE:
795                     case SQUARE:
796                         marginLeft = offset + cMarkerPadding + 1 - minPrefWidth();
797                         break;
798                     case LNONE:
799                         break;
800                     default:
801                         marginLeft = m_text.isEmpty() ? 0 : offset / 2;
802                 }
803             }
804         }
805         marginRight = -marginLeft - minPrefWidth();
806     }
807
808     style()->setMarginLeft(Length(marginLeft, Fixed));
809     style()->setMarginRight(Length(marginRight, Fixed));
810 }
811
812 int RenderListMarker::lineHeight(bool, bool) const
813 {
814     if (!isImage())
815         return m_listItem->lineHeight(false, true);
816     return height();
817 }
818
819 int RenderListMarker::baselinePosition(bool, bool) const
820 {
821     if (!isImage()) {
822         const Font& font = style()->font();
823         return font.ascent() + (lineHeight(false) - font.height())/2;
824     }
825     return height();
826 }
827
828 bool RenderListMarker::isInside() const
829 {
830     return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
831 }
832
833 IntRect RenderListMarker::getRelativeMarkerRect()
834 {
835     if (isImage())
836         return IntRect(x(), y(), m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height());
837
838     switch (style()->listStyleType()) {
839         case DISC:
840         case CIRCLE:
841         case SQUARE: {
842             // FIXME: Are these particular rounding rules necessary?
843             const Font& font = style()->font();
844             int ascent = font.ascent();
845             int bulletWidth = (ascent * 2 / 3 + 1) / 2;
846             return IntRect(x() + 1, y() + 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
847         }
848         case LNONE:
849             return IntRect();
850         case ARMENIAN:
851         case CJK_IDEOGRAPHIC:
852         case DECIMAL_LEADING_ZERO:
853         case GEORGIAN:
854         case HEBREW:
855         case HIRAGANA:
856         case HIRAGANA_IROHA:
857         case KATAKANA:
858         case KATAKANA_IROHA:
859         case LDECIMAL:
860         case LOWER_ALPHA:
861         case LOWER_GREEK:
862         case LOWER_LATIN:
863         case LOWER_ROMAN:
864         case UPPER_ALPHA:
865         case UPPER_LATIN:
866         case UPPER_ROMAN:
867             if (m_text.isEmpty())
868                 return IntRect();
869             const Font& font = style()->font();
870             int itemWidth = font.width(m_text);
871             const UChar periodSpace[2] = { '.', ' ' };
872             int periodSpaceWidth = font.width(TextRun(periodSpace, 2));
873             return IntRect(x(), y() + font.ascent(), itemWidth + periodSpaceWidth, font.height());
874     }
875
876     return IntRect();
877 }
878
879 void RenderListMarker::setSelectionState(SelectionState state)
880 {
881     RenderBox::setSelectionState(state);
882     if (InlineBox* box = inlineBoxWrapper())
883         if (RootInlineBox* root = box->root())
884             root->setHasSelectedChildren(state != SelectionNone);
885     containingBlock()->setSelectionState(state);
886 }
887
888 IntRect RenderListMarker::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
889 {
890     ASSERT(!needsLayout());
891
892     if (selectionState() == SelectionNone || !inlineBoxWrapper())
893         return IntRect();
894
895     RootInlineBox* root = inlineBoxWrapper()->root();
896     IntRect rect(0, root->selectionTop() - y(), width(), root->selectionHeight());
897             
898     if (clipToVisibleContent)
899         computeRectForRepaint(repaintContainer, rect);
900     else
901         rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
902     
903     return rect;
904 }
905
906 } // namespace WebCore