webengine/osswebengine/WebCore/rendering/RenderListMarker.cpp
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /**
       
     2  * This file is part of the DOM implementation for KDE.
       
     3  *
       
     4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
       
     5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
       
     6  * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
       
     7  * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
       
     8  *
       
     9  * This library is free software; you can redistribute it and/or
       
    10  * modify it under the terms of the GNU Library General Public
       
    11  * License as published by the Free Software Foundation; either
       
    12  * version 2 of the License, or (at your option) any later version.
       
    13  *
       
    14  * This library is distributed in the hope that it will be useful,
       
    15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    17  * Library General Public License for more details.
       
    18  *
       
    19  * You should have received a copy of the GNU Library General Public License
       
    20  * along with this library; see the file COPYING.LIB.  If not, write to
       
    21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    22  * Boston, MA 02110-1301, USA.
       
    23  *
       
    24  */
       
    25 
       
    26 #include "config.h"
       
    27 #include "RenderListMarker.h"
       
    28 
       
    29 #include "CachedImage.h"
       
    30 #include "CharacterNames.h"
       
    31 #include "Document.h"
       
    32 #include "GraphicsContext.h"
       
    33 #include "ListMarkerBox.h"
       
    34 #include "RenderLayer.h"
       
    35 #include "RenderListItem.h"
       
    36 #include "RenderView.h"
       
    37 
       
    38 using namespace std;
       
    39 using namespace WTF;
       
    40 using namespace Unicode;
       
    41 
       
    42 namespace WebCore {
       
    43 
       
    44 const int cMarkerPadding = 7;
       
    45 
       
    46 static String toRoman(int number, bool upper)
       
    47 {
       
    48     // FIXME: CSS3 describes how to make this work for much larger numbers,
       
    49     // using overbars and special characters. It also specifies the characters
       
    50     // in the range U+2160 to U+217F instead of standard ASCII ones.
       
    51     if (number < 1 || number > 3999)
       
    52         return String::number(number);
       
    53 
       
    54     const int lettersSize = 12; // big enough for three each of I, X, C, and M
       
    55     UChar letters[lettersSize];
       
    56 
       
    57     int length = 0;
       
    58     const UChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
       
    59     const UChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
       
    60     const UChar* digits = upper ? udigits : ldigits;
       
    61     int d = 0;
       
    62     do {
       
    63         int num = number % 10;
       
    64         if (num % 5 < 4)
       
    65             for (int i = num % 5; i > 0; i--)
       
    66                 letters[lettersSize - ++length] = digits[d];
       
    67         if (num >= 4 && num <= 8)
       
    68             letters[lettersSize - ++length] = digits[d + 1];
       
    69         if (num == 9)
       
    70             letters[lettersSize - ++length] = digits[d + 2];
       
    71         if (num % 5 == 4)
       
    72             letters[lettersSize - ++length] = digits[d];
       
    73         number /= 10;
       
    74         d += 2;
       
    75     } while (number);
       
    76 
       
    77     ASSERT(length <= lettersSize);
       
    78     return String(&letters[lettersSize - length], length);
       
    79 }
       
    80 
       
    81 static String toAlphabetic(int number, const UChar* alphabet, int alphabetSize)
       
    82 {
       
    83     ASSERT(alphabetSize >= 10);
       
    84 
       
    85     if (number < 1)
       
    86         return String::number(number);
       
    87 
       
    88     const int lettersSize = 10; // big enough for a 32-bit int, with a 10-letter alphabet
       
    89     UChar letters[lettersSize];
       
    90 
       
    91     --number;
       
    92     letters[lettersSize - 1] = alphabet[number % alphabetSize];
       
    93     int length = 1;
       
    94     while ((number /= alphabetSize) > 0)
       
    95         letters[lettersSize - ++length] = alphabet[number % alphabetSize - 1];
       
    96 
       
    97     ASSERT(length <= lettersSize);
       
    98     return String(&letters[lettersSize - length], length);
       
    99 }
       
   100 
       
   101 static int toHebrewUnder1000(int number, UChar letters[5])
       
   102 {
       
   103     // FIXME: CSS3 mentions various refinements not implemented here.
       
   104     // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
       
   105     ASSERT(number >= 0 && number < 1000);
       
   106     int length = 0;
       
   107     int fourHundreds = number / 400;
       
   108     for (int i = 0; i < fourHundreds; i++)
       
   109         letters[length++] = 1511 + 3;
       
   110     number %= 400;
       
   111     if (number / 100)
       
   112         letters[length++] = 1511 + (number / 100) - 1;
       
   113     number %= 100;
       
   114     if (number == 15 || number == 16) {
       
   115         letters[length++] = 1487 + 9;
       
   116         letters[length++] = 1487 + number - 9;
       
   117     } else {
       
   118         if (int tens = number / 10) {
       
   119             static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
       
   120             letters[length++] = hebrewTens[tens - 1];
       
   121         }
       
   122         if (int ones = number % 10)
       
   123             letters[length++] = 1487 + ones;
       
   124     }
       
   125     ASSERT(length <= 5);
       
   126     return length;
       
   127 }
       
   128 
       
   129 static String toHebrew(int number)
       
   130 {
       
   131     // FIXME: CSS3 mentions ways to make this work for much larger numbers.
       
   132     if (number < 0 || number > 999999)
       
   133         return String::number(number);
       
   134 
       
   135     if (number == 0) {
       
   136         static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
       
   137         return String(hebrewZero, 3);
       
   138     }
       
   139 
       
   140     const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
       
   141     UChar letters[lettersSize];
       
   142 
       
   143     int length;
       
   144     if (number < 1000)
       
   145         length = 0;
       
   146     else {
       
   147         length = toHebrewUnder1000(number / 1000, letters);
       
   148         letters[length++] = '\'';
       
   149         number = number % 1000;
       
   150     }
       
   151     length += toHebrewUnder1000(number, letters + length);
       
   152 
       
   153     ASSERT(length <= lettersSize);
       
   154     return String(letters, length);
       
   155 }
       
   156 
       
   157 static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
       
   158 {
       
   159     ASSERT(number >= 0 && number < 10000);
       
   160     int length = 0;
       
   161 
       
   162     int lowerOffset = upper ? 0 : 0x0030;
       
   163 
       
   164     if (int thousands = number / 1000)
       
   165         if (thousands == 7) {
       
   166             letters[length++] = 0x0548 + lowerOffset;
       
   167             letters[length++] = 0x0552 + lowerOffset;
       
   168             if (addCircumflex)
       
   169                 letters[length++] = 0x0302;
       
   170         } else {
       
   171             letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
       
   172             if (addCircumflex)
       
   173                 letters[length++] = 0x0302;
       
   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_image(0)
       
   477     , m_listItem(item)
       
   478     , m_selectionState(SelectionNone)
       
   479 {
       
   480     // init RenderObject attributes
       
   481     setInline(true);   // our object is Inline
       
   482     setReplaced(true); // pretend to be replaced
       
   483 }
       
   484 
       
   485 RenderListMarker::~RenderListMarker()
       
   486 {
       
   487     if (m_image)
       
   488         m_image->deref(this);
       
   489 }
       
   490 
       
   491 void RenderListMarker::setStyle(RenderStyle* s)
       
   492 {
       
   493     if (style() && (s->listStylePosition() != style()->listStylePosition() || s->listStyleType() != style()->listStyleType()))
       
   494         setNeedsLayoutAndPrefWidthsRecalc();
       
   495     
       
   496     RenderBox::setStyle(s);
       
   497 
       
   498     if (m_image != style()->listStyleImage()) {
       
   499         if (m_image)
       
   500             m_image->deref(this);
       
   501         m_image = style()->listStyleImage();
       
   502         if (m_image)
       
   503             m_image->ref(this);
       
   504     }
       
   505 }
       
   506 
       
   507 InlineBox* RenderListMarker::createInlineBox(bool, bool isRootLineBox, bool)
       
   508 {
       
   509     ASSERT(!isRootLineBox);
       
   510     ListMarkerBox* box = new (renderArena()) ListMarkerBox(this);
       
   511     m_inlineBoxWrapper = box;
       
   512     return box;
       
   513 }
       
   514 
       
   515 bool RenderListMarker::isImage() const
       
   516 {
       
   517     return m_image && !m_image->errorOccurred();
       
   518 }
       
   519 
       
   520 void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty)
       
   521 {
       
   522     if (paintInfo.phase != PaintPhaseForeground)
       
   523         return;
       
   524     
       
   525     if (style()->visibility() != VISIBLE)
       
   526         return;
       
   527 
       
   528     IntRect marker = getRelativeMarkerRect();
       
   529     marker.move(tx, ty);
       
   530 
       
   531     IntRect box(tx + m_x, ty + m_y, m_width, m_height);
       
   532 
       
   533     if (box.y() > paintInfo.rect.bottom() || box.y() + box.height() < paintInfo.rect.y())
       
   534         return;
       
   535 
       
   536     if (hasBoxDecorations()) 
       
   537         paintBoxDecorations(paintInfo, box.x(), box.y());
       
   538 
       
   539     GraphicsContext* context = paintInfo.context;
       
   540     context->setFont(style()->font());
       
   541 
       
   542     if (isImage()) {
       
   543 #if PLATFORM(MAC)
       
   544         if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
       
   545             paintCustomHighlight(tx, ty, style()->highlight(), true);
       
   546 #endif
       
   547         context->drawImage(m_image->image(), marker.location());
       
   548         if (selectionState() != SelectionNone)
       
   549             context->fillRect(selectionRect(), selectionBackgroundColor());
       
   550         return;
       
   551     }
       
   552 
       
   553 #if PLATFORM(MAC)
       
   554     // FIXME: paint gap between marker and list item proper
       
   555     if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
       
   556         paintCustomHighlight(tx, ty, style()->highlight(), true);
       
   557 #endif
       
   558 
       
   559     if (selectionState() != SelectionNone)
       
   560         context->fillRect(selectionRect(), selectionBackgroundColor());
       
   561 
       
   562     const Color color(style()->color());
       
   563     context->setStrokeColor(color);
       
   564     context->setStrokeStyle(SolidStroke);
       
   565     context->setStrokeThickness(1.0f);
       
   566     context->setFillColor(color);
       
   567 
       
   568     switch (style()->listStyleType()) {
       
   569         case DISC:
       
   570             context->drawEllipse(marker);
       
   571             return;
       
   572         case CIRCLE:
       
   573             context->setFillColor(Color::transparent);
       
   574             context->drawEllipse(marker);
       
   575             return;
       
   576         case SQUARE:
       
   577             context->drawRect(marker);
       
   578             return;
       
   579         case LNONE:
       
   580             return;
       
   581         case ARMENIAN:
       
   582         case CJK_IDEOGRAPHIC:
       
   583         case DECIMAL_LEADING_ZERO:
       
   584         case GEORGIAN:
       
   585         case HEBREW:
       
   586         case HIRAGANA:
       
   587         case HIRAGANA_IROHA:
       
   588         case KATAKANA:
       
   589         case KATAKANA_IROHA:
       
   590         case LDECIMAL:
       
   591         case LOWER_ALPHA:
       
   592         case LOWER_GREEK:
       
   593         case LOWER_LATIN:
       
   594         case LOWER_ROMAN:
       
   595         case UPPER_ALPHA:
       
   596         case UPPER_LATIN:
       
   597         case UPPER_ROMAN:
       
   598             break;
       
   599     }
       
   600     if (m_text.isEmpty())
       
   601         return;
       
   602 
       
   603     TextRun textRun(m_text);
       
   604 
       
   605     // Text is not arbitrary. We can judge whether it's RTL from the first character,
       
   606     // and we only need to handle the direction RightToLeft for now.
       
   607     bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
       
   608     Vector<UChar> reversedText;
       
   609     if (textNeedsReversing) {
       
   610         int length = m_text.length();
       
   611         reversedText.resize(length);
       
   612         for (int i = 0; i < length; ++i)
       
   613             reversedText[length - i - 1] = m_text[i];
       
   614         textRun = TextRun(reversedText.data(), length);
       
   615     }
       
   616 
       
   617     const Font& font = style()->font();
       
   618     if (style()->direction() == LTR) {
       
   619         int width = font.width(textRun);
       
   620         context->drawText(textRun, marker.location());
       
   621         const UChar periodSpace[2] = { '.', ' ' };
       
   622         context->drawText(TextRun(periodSpace, 2), marker.location() + IntSize(width, 0));
       
   623     } else {
       
   624         const UChar spacePeriod[2] = { ' ', '.' };
       
   625         TextRun spacePeriodRun(spacePeriod, 2);
       
   626         int width = font.width(spacePeriodRun);
       
   627         context->drawText(spacePeriodRun, marker.location());
       
   628         context->drawText(textRun, marker.location() + IntSize(width, 0));
       
   629     }
       
   630 }
       
   631 
       
   632 void RenderListMarker::layout()
       
   633 {
       
   634     ASSERT(needsLayout());
       
   635     ASSERT(!prefWidthsDirty());
       
   636 
       
   637     if (isImage()) {
       
   638         m_width = m_image->image()->width();
       
   639         m_height = m_image->image()->height();
       
   640     } else {
       
   641         m_width = minPrefWidth();
       
   642         m_height = style()->font().height();
       
   643     }
       
   644 
       
   645     m_marginLeft = m_marginRight = 0;
       
   646 
       
   647     Length leftMargin = style()->marginLeft();
       
   648     Length rightMargin = style()->marginRight();
       
   649     if (leftMargin.isFixed())
       
   650         m_marginLeft = leftMargin.value();
       
   651     if (rightMargin.isFixed())
       
   652         m_marginRight = rightMargin.value();
       
   653 
       
   654     setNeedsLayout(false);
       
   655 }
       
   656 
       
   657 void RenderListMarker::imageChanged(CachedImage* o)
       
   658 {
       
   659     // A list marker can't have a background or border image, so no need to call the base class method.
       
   660     if (o != m_image)
       
   661         return;
       
   662 
       
   663     if (m_width != m_image->imageSize().width() || m_height != m_image->imageSize().height() || m_image->errorOccurred())
       
   664         setNeedsLayoutAndPrefWidthsRecalc();
       
   665     else
       
   666         repaint();
       
   667 }
       
   668 
       
   669 void RenderListMarker::calcPrefWidths()
       
   670 {
       
   671     ASSERT(prefWidthsDirty());
       
   672 
       
   673     m_text = "";
       
   674 
       
   675     if (isImage()) {
       
   676         m_minPrefWidth = m_maxPrefWidth = m_image->image()->width();
       
   677         setPrefWidthsDirty(false);
       
   678         updateMargins();
       
   679         return;
       
   680     }
       
   681 
       
   682     const Font& font = style()->font();
       
   683 
       
   684     int width = 0;
       
   685     EListStyleType type = style()->listStyleType();
       
   686     switch (type) {
       
   687         case LNONE:
       
   688             break;
       
   689         case CIRCLE:
       
   690         case DISC:
       
   691         case SQUARE:
       
   692             m_text = listMarkerText(type, 0); // value is ignored for these types
       
   693             width = (font.ascent() * 2 / 3 + 1) / 2 + 2;
       
   694             break;
       
   695         case ARMENIAN:
       
   696         case CJK_IDEOGRAPHIC:
       
   697         case DECIMAL_LEADING_ZERO:
       
   698         case GEORGIAN:
       
   699         case HEBREW:
       
   700         case HIRAGANA:
       
   701         case HIRAGANA_IROHA:
       
   702         case KATAKANA:
       
   703         case KATAKANA_IROHA:
       
   704         case LDECIMAL:
       
   705         case LOWER_ALPHA:
       
   706         case LOWER_GREEK:
       
   707         case LOWER_LATIN:
       
   708         case LOWER_ROMAN:
       
   709         case UPPER_ALPHA:
       
   710         case UPPER_LATIN:
       
   711         case UPPER_ROMAN:
       
   712             m_text = listMarkerText(type, m_listItem->value());
       
   713             if (m_text.isEmpty())
       
   714                 width = 0;
       
   715             else {
       
   716                 int itemWidth = font.width(m_text);
       
   717                 const UChar periodSpace[2] = { '.', ' ' };
       
   718                 int periodSpaceWidth = font.width(TextRun(periodSpace, 2));
       
   719                 width = itemWidth + periodSpaceWidth;
       
   720             }
       
   721             break;
       
   722     }
       
   723 
       
   724     m_minPrefWidth = width;
       
   725     m_maxPrefWidth = width;
       
   726 
       
   727     setPrefWidthsDirty(false);
       
   728     
       
   729     updateMargins();
       
   730 }
       
   731 
       
   732 void RenderListMarker::updateMargins()
       
   733 {
       
   734     const Font& font = style()->font();
       
   735 
       
   736     int marginLeft = 0;
       
   737     int marginRight = 0;
       
   738 
       
   739     if (isInside()) {
       
   740         if (isImage()) {
       
   741             if (style()->direction() == LTR)
       
   742                 marginRight = cMarkerPadding;
       
   743             else
       
   744                 marginLeft = cMarkerPadding;
       
   745         } else switch (style()->listStyleType()) {
       
   746             case DISC:
       
   747             case CIRCLE:
       
   748             case SQUARE:
       
   749                 if (style()->direction() == LTR) {
       
   750                     marginLeft = -1;
       
   751                     marginRight = font.ascent() - minPrefWidth() + 1;
       
   752                 } else {
       
   753                     marginLeft = font.ascent() - minPrefWidth() + 1;
       
   754                     marginRight = -1;
       
   755                 }
       
   756                 break;
       
   757             default:
       
   758                 break;
       
   759         }
       
   760     } else {
       
   761         if (style()->direction() == LTR) {
       
   762             if (isImage())
       
   763                 marginLeft = -minPrefWidth() - cMarkerPadding;
       
   764             else {
       
   765                 int offset = font.ascent() * 2 / 3;
       
   766                 switch (style()->listStyleType()) {
       
   767                     case DISC:
       
   768                     case CIRCLE:
       
   769                     case SQUARE:
       
   770                         marginLeft = -offset - cMarkerPadding - 1;
       
   771                         break;
       
   772                     case LNONE:
       
   773                         break;
       
   774                     default:
       
   775                         marginLeft = m_text.isEmpty() ? 0 : -minPrefWidth() - offset / 2;
       
   776                 }
       
   777             }
       
   778         } else {
       
   779             if (isImage())
       
   780                 marginLeft = cMarkerPadding;
       
   781             else {
       
   782                 int offset = font.ascent() * 2 / 3;
       
   783                 switch (style()->listStyleType()) {
       
   784                     case DISC:
       
   785                     case CIRCLE:
       
   786                     case SQUARE:
       
   787                         marginLeft = offset + cMarkerPadding + 1 - minPrefWidth();
       
   788                         break;
       
   789                     case LNONE:
       
   790                         break;
       
   791                     default:
       
   792                         marginLeft = m_text.isEmpty() ? 0 : offset / 2;
       
   793                 }
       
   794             }
       
   795         }
       
   796         marginRight = -marginLeft - minPrefWidth();
       
   797     }
       
   798 
       
   799     style()->setMarginLeft(Length(marginLeft, Fixed));
       
   800     style()->setMarginRight(Length(marginRight, Fixed));
       
   801 }
       
   802 
       
   803 short RenderListMarker::lineHeight(bool, bool) const
       
   804 {
       
   805     if (!isImage())
       
   806         return m_listItem->lineHeight(false, true);
       
   807     return height();
       
   808 }
       
   809 
       
   810 short RenderListMarker::baselinePosition(bool, bool) const
       
   811 {
       
   812     if (!isImage()) {
       
   813         const Font& font = style()->font();
       
   814         return font.ascent() + (lineHeight(false) - font.height())/2;
       
   815     }
       
   816     return height();
       
   817 }
       
   818 
       
   819 bool RenderListMarker::isInside() const
       
   820 {
       
   821     return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
       
   822 }
       
   823 
       
   824 IntRect RenderListMarker::getRelativeMarkerRect()
       
   825 {
       
   826     if (isImage())
       
   827         return IntRect(m_x, m_y, m_image->imageSize().width(), m_image->imageSize().height());
       
   828 
       
   829     switch (style()->listStyleType()) {
       
   830         case DISC:
       
   831         case CIRCLE:
       
   832         case SQUARE: {
       
   833             // FIXME: Are these particular rounding rules necessary?
       
   834             const Font& font = style()->font();
       
   835             int ascent = font.ascent();
       
   836             int bulletWidth = (ascent * 2 / 3 + 1) / 2;
       
   837             return IntRect(m_x + 1, m_y + 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
       
   838         }
       
   839         case LNONE:
       
   840             return IntRect();
       
   841         case ARMENIAN:
       
   842         case CJK_IDEOGRAPHIC:
       
   843         case DECIMAL_LEADING_ZERO:
       
   844         case GEORGIAN:
       
   845         case HEBREW:
       
   846         case HIRAGANA:
       
   847         case HIRAGANA_IROHA:
       
   848         case KATAKANA:
       
   849         case KATAKANA_IROHA:
       
   850         case LDECIMAL:
       
   851         case LOWER_ALPHA:
       
   852         case LOWER_GREEK:
       
   853         case LOWER_LATIN:
       
   854         case LOWER_ROMAN:
       
   855         case UPPER_ALPHA:
       
   856         case UPPER_LATIN:
       
   857         case UPPER_ROMAN:
       
   858             if (m_text.isEmpty())
       
   859                 return IntRect();
       
   860             const Font& font = style()->font();
       
   861             int itemWidth = font.width(m_text);
       
   862             const UChar periodSpace[2] = { '.', ' ' };
       
   863             int periodSpaceWidth = font.width(TextRun(periodSpace, 2));
       
   864             return IntRect(m_x, m_y + font.ascent(), itemWidth + periodSpaceWidth, font.height());
       
   865     }
       
   866 
       
   867     return IntRect();
       
   868 }
       
   869 
       
   870 void RenderListMarker::setSelectionState(SelectionState state)
       
   871 {
       
   872     m_selectionState = state;
       
   873     if (InlineBox* box = inlineBoxWrapper())
       
   874         if (RootInlineBox* root = box->root())
       
   875             root->setHasSelectedChildren(state != SelectionNone);
       
   876     containingBlock()->setSelectionState(state);
       
   877 }
       
   878 
       
   879 IntRect RenderListMarker::selectionRect(bool clipToVisibleContent)
       
   880 {
       
   881     ASSERT(!needsLayout());
       
   882 
       
   883     if (selectionState() == SelectionNone || !inlineBoxWrapper())
       
   884         return IntRect();
       
   885 
       
   886     RootInlineBox* root = inlineBoxWrapper()->root();
       
   887     IntRect rect(0, root->selectionTop() - yPos(), width(), root->selectionHeight());
       
   888             
       
   889     if (clipToVisibleContent)
       
   890         computeAbsoluteRepaintRect(rect);
       
   891     else {
       
   892         int absx, absy;
       
   893         absolutePosition(absx, absy);
       
   894         rect.move(absx, absy);
       
   895     }
       
   896     
       
   897     return rect;
       
   898 }
       
   899 
       
   900 } // namespace WebCore