Home | History | Annotate | Download | only in css
      1 // Copyright 2014 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include "core/fxcrt/css/cfx_cssdeclaration.h"
      8 
      9 #include "core/fxcrt/css/cfx_csscolorvalue.h"
     10 #include "core/fxcrt/css/cfx_csscustomproperty.h"
     11 #include "core/fxcrt/css/cfx_cssenumvalue.h"
     12 #include "core/fxcrt/css/cfx_cssnumbervalue.h"
     13 #include "core/fxcrt/css/cfx_csspropertyholder.h"
     14 #include "core/fxcrt/css/cfx_cssstringvalue.h"
     15 #include "core/fxcrt/css/cfx_cssvaluelist.h"
     16 #include "core/fxcrt/css/cfx_cssvaluelistparser.h"
     17 #include "core/fxcrt/fx_extension.h"
     18 #include "third_party/base/logging.h"
     19 #include "third_party/base/ptr_util.h"
     20 
     21 namespace {
     22 
     23 uint8_t Hex2Dec(uint8_t hexHigh, uint8_t hexLow) {
     24   return (FXSYS_HexCharToInt(hexHigh) << 4) + FXSYS_HexCharToInt(hexLow);
     25 }
     26 
     27 struct CFX_CSSPropertyValueTable {
     28   CFX_CSSPropertyValue eName;
     29   const wchar_t* pszName;
     30   uint32_t dwHash;
     31 };
     32 const CFX_CSSPropertyValueTable g_CFX_CSSPropertyValues[] = {
     33     {CFX_CSSPropertyValue::Bolder, L"bolder", 0x009F1058},
     34     {CFX_CSSPropertyValue::None, L"none", 0x048B6670},
     35     {CFX_CSSPropertyValue::Dot, L"dot", 0x0A48CB27},
     36     {CFX_CSSPropertyValue::Sub, L"sub", 0x0BD37FAA},
     37     {CFX_CSSPropertyValue::Top, L"top", 0x0BEDAF33},
     38     {CFX_CSSPropertyValue::Right, L"right", 0x193ADE3E},
     39     {CFX_CSSPropertyValue::Normal, L"normal", 0x247CF3E9},
     40     {CFX_CSSPropertyValue::Auto, L"auto", 0x2B35B6D9},
     41     {CFX_CSSPropertyValue::Text, L"text", 0x2D08AF85},
     42     {CFX_CSSPropertyValue::XSmall, L"x-small", 0x2D2FCAFE},
     43     {CFX_CSSPropertyValue::Thin, L"thin", 0x2D574D53},
     44     {CFX_CSSPropertyValue::Small, L"small", 0x316A3739},
     45     {CFX_CSSPropertyValue::Bottom, L"bottom", 0x399F02B5},
     46     {CFX_CSSPropertyValue::Underline, L"underline", 0x3A0273A6},
     47     {CFX_CSSPropertyValue::Double, L"double", 0x3D98515B},
     48     {CFX_CSSPropertyValue::Lighter, L"lighter", 0x45BEB7AF},
     49     {CFX_CSSPropertyValue::Oblique, L"oblique", 0x53EBDDB1},
     50     {CFX_CSSPropertyValue::Super, L"super", 0x6A4F842F},
     51     {CFX_CSSPropertyValue::Center, L"center", 0x6C51AFC1},
     52     {CFX_CSSPropertyValue::XxLarge, L"xx-large", 0x70BB1508},
     53     {CFX_CSSPropertyValue::Smaller, L"smaller", 0x849769F0},
     54     {CFX_CSSPropertyValue::Baseline, L"baseline", 0x87436BA3},
     55     {CFX_CSSPropertyValue::Thick, L"thick", 0x8CC35EB3},
     56     {CFX_CSSPropertyValue::Justify, L"justify", 0x8D269CAE},
     57     {CFX_CSSPropertyValue::Middle, L"middle", 0x947FA00F},
     58     {CFX_CSSPropertyValue::Medium, L"medium", 0xA084A381},
     59     {CFX_CSSPropertyValue::ListItem, L"list-item", 0xA32382B8},
     60     {CFX_CSSPropertyValue::XxSmall, L"xx-small", 0xADE1FC76},
     61     {CFX_CSSPropertyValue::Bold, L"bold", 0xB18313A1},
     62     {CFX_CSSPropertyValue::SmallCaps, L"small-caps", 0xB299428D},
     63     {CFX_CSSPropertyValue::Inline, L"inline", 0xC02D649F},
     64     {CFX_CSSPropertyValue::Overline, L"overline", 0xC0EC9FA4},
     65     {CFX_CSSPropertyValue::TextBottom, L"text-bottom", 0xC7D08D87},
     66     {CFX_CSSPropertyValue::Larger, L"larger", 0xCD3C409D},
     67     {CFX_CSSPropertyValue::InlineTable, L"inline-table", 0xD131F494},
     68     {CFX_CSSPropertyValue::InlineBlock, L"inline-block", 0xD26A8BD7},
     69     {CFX_CSSPropertyValue::Blink, L"blink", 0xDC36E390},
     70     {CFX_CSSPropertyValue::Block, L"block", 0xDCD480AB},
     71     {CFX_CSSPropertyValue::Italic, L"italic", 0xE31D5396},
     72     {CFX_CSSPropertyValue::LineThrough, L"line-through", 0xE4C5A276},
     73     {CFX_CSSPropertyValue::XLarge, L"x-large", 0xF008E390},
     74     {CFX_CSSPropertyValue::Large, L"large", 0xF4434FCB},
     75     {CFX_CSSPropertyValue::Left, L"left", 0xF5AD782B},
     76     {CFX_CSSPropertyValue::TextTop, L"text-top", 0xFCB58D45},
     77 };
     78 const int32_t g_iCSSPropertyValueCount =
     79     sizeof(g_CFX_CSSPropertyValues) / sizeof(CFX_CSSPropertyValueTable);
     80 static_assert(g_iCSSPropertyValueCount ==
     81                   static_cast<int32_t>(CFX_CSSPropertyValue::LAST_MARKER),
     82               "Property value table differs in size from property value enum");
     83 
     84 struct CFX_CSSLengthUnitTable {
     85   uint16_t wHash;
     86   CFX_CSSNumberType wValue;
     87 };
     88 const CFX_CSSLengthUnitTable g_CFX_CSSLengthUnits[] = {
     89     {0x0672, CFX_CSSNumberType::EMS},
     90     {0x067D, CFX_CSSNumberType::EXS},
     91     {0x1AF7, CFX_CSSNumberType::Inches},
     92     {0x2F7A, CFX_CSSNumberType::MilliMeters},
     93     {0x3ED3, CFX_CSSNumberType::Picas},
     94     {0x3EE4, CFX_CSSNumberType::Points},
     95     {0x3EE8, CFX_CSSNumberType::Pixels},
     96     {0xFC30, CFX_CSSNumberType::CentiMeters},
     97 };
     98 
     99 struct CFX_CSSColorTable {
    100   uint32_t dwHash;
    101   FX_ARGB dwValue;
    102 };
    103 const CFX_CSSColorTable g_CFX_CSSColors[] = {
    104     {0x031B47FE, 0xff000080}, {0x0BB8DF5B, 0xffff0000},
    105     {0x0D82A78C, 0xff800000}, {0x2ACC82E8, 0xff00ffff},
    106     {0x2D083986, 0xff008080}, {0x4A6A6195, 0xffc0c0c0},
    107     {0x546A8EF3, 0xff808080}, {0x65C9169C, 0xffffa500},
    108     {0x8422BB61, 0xffffffff}, {0x9271A558, 0xff800080},
    109     {0xA65A3EE3, 0xffff00ff}, {0xB1345708, 0xff0000ff},
    110     {0xB6D2CF1F, 0xff808000}, {0xD19B5E1C, 0xffffff00},
    111     {0xDB64391D, 0xff000000}, {0xF616D507, 0xff00ff00},
    112     {0xF6EFFF31, 0xff008000},
    113 };
    114 
    115 const CFX_CSSPropertyValueTable* GetCSSPropertyValueByName(
    116     const WideStringView& wsName) {
    117   ASSERT(!wsName.IsEmpty());
    118   uint32_t dwHash = FX_HashCode_GetW(wsName, true);
    119   int32_t iEnd = g_iCSSPropertyValueCount;
    120   int32_t iMid, iStart = 0;
    121   uint32_t dwMid;
    122   do {
    123     iMid = (iStart + iEnd) / 2;
    124     dwMid = g_CFX_CSSPropertyValues[iMid].dwHash;
    125     if (dwHash == dwMid) {
    126       return g_CFX_CSSPropertyValues + iMid;
    127     } else if (dwHash > dwMid) {
    128       iStart = iMid + 1;
    129     } else {
    130       iEnd = iMid - 1;
    131     }
    132   } while (iStart <= iEnd);
    133   return nullptr;
    134 }
    135 
    136 const CFX_CSSLengthUnitTable* GetCSSLengthUnitByName(
    137     const WideStringView& wsName) {
    138   ASSERT(!wsName.IsEmpty());
    139   uint16_t wHash = FX_HashCode_GetW(wsName, true);
    140   int32_t iEnd =
    141       sizeof(g_CFX_CSSLengthUnits) / sizeof(CFX_CSSLengthUnitTable) - 1;
    142   int32_t iMid, iStart = 0;
    143   uint16_t wMid;
    144   do {
    145     iMid = (iStart + iEnd) / 2;
    146     wMid = g_CFX_CSSLengthUnits[iMid].wHash;
    147     if (wHash == wMid) {
    148       return g_CFX_CSSLengthUnits + iMid;
    149     } else if (wHash > wMid) {
    150       iStart = iMid + 1;
    151     } else {
    152       iEnd = iMid - 1;
    153     }
    154   } while (iStart <= iEnd);
    155   return nullptr;
    156 }
    157 
    158 const CFX_CSSColorTable* GetCSSColorByName(const WideStringView& wsName) {
    159   ASSERT(!wsName.IsEmpty());
    160   uint32_t dwHash = FX_HashCode_GetW(wsName, true);
    161   int32_t iEnd = sizeof(g_CFX_CSSColors) / sizeof(CFX_CSSColorTable) - 1;
    162   int32_t iMid, iStart = 0;
    163   uint32_t dwMid;
    164   do {
    165     iMid = (iStart + iEnd) / 2;
    166     dwMid = g_CFX_CSSColors[iMid].dwHash;
    167     if (dwHash == dwMid) {
    168       return g_CFX_CSSColors + iMid;
    169     } else if (dwHash > dwMid) {
    170       iStart = iMid + 1;
    171     } else {
    172       iEnd = iMid - 1;
    173     }
    174   } while (iStart <= iEnd);
    175   return nullptr;
    176 }
    177 
    178 bool ParseCSSNumber(const wchar_t* pszValue,
    179                     int32_t iValueLen,
    180                     float& fValue,
    181                     CFX_CSSNumberType& eUnit) {
    182   ASSERT(pszValue && iValueLen > 0);
    183   int32_t iUsedLen = 0;
    184   fValue = FXSYS_wcstof(pszValue, iValueLen, &iUsedLen);
    185   if (iUsedLen <= 0)
    186     return false;
    187 
    188   iValueLen -= iUsedLen;
    189   pszValue += iUsedLen;
    190   eUnit = CFX_CSSNumberType::Number;
    191   if (iValueLen >= 1 && *pszValue == '%') {
    192     eUnit = CFX_CSSNumberType::Percent;
    193   } else if (iValueLen == 2) {
    194     const CFX_CSSLengthUnitTable* pUnit =
    195         GetCSSLengthUnitByName(WideStringView(pszValue, 2));
    196     if (pUnit)
    197       eUnit = pUnit->wValue;
    198   }
    199   return true;
    200 }
    201 
    202 }  // namespace
    203 
    204 // static
    205 bool CFX_CSSDeclaration::ParseCSSString(const wchar_t* pszValue,
    206                                         int32_t iValueLen,
    207                                         int32_t* iOffset,
    208                                         int32_t* iLength) {
    209   ASSERT(pszValue && iValueLen > 0);
    210   *iOffset = 0;
    211   *iLength = iValueLen;
    212   if (iValueLen >= 2) {
    213     wchar_t first = pszValue[0], last = pszValue[iValueLen - 1];
    214     if ((first == '\"' && last == '\"') || (first == '\'' && last == '\'')) {
    215       *iOffset = 1;
    216       *iLength -= 2;
    217     }
    218   }
    219   return iValueLen > 0;
    220 }
    221 
    222 // static.
    223 bool CFX_CSSDeclaration::ParseCSSColor(const wchar_t* pszValue,
    224                                        int32_t iValueLen,
    225                                        FX_ARGB* dwColor) {
    226   ASSERT(pszValue && iValueLen > 0);
    227   ASSERT(dwColor);
    228 
    229   if (*pszValue == '#') {
    230     switch (iValueLen) {
    231       case 4: {
    232         uint8_t red = Hex2Dec((uint8_t)pszValue[1], (uint8_t)pszValue[1]);
    233         uint8_t green = Hex2Dec((uint8_t)pszValue[2], (uint8_t)pszValue[2]);
    234         uint8_t blue = Hex2Dec((uint8_t)pszValue[3], (uint8_t)pszValue[3]);
    235         *dwColor = ArgbEncode(255, red, green, blue);
    236         return true;
    237       }
    238       case 7: {
    239         uint8_t red = Hex2Dec((uint8_t)pszValue[1], (uint8_t)pszValue[2]);
    240         uint8_t green = Hex2Dec((uint8_t)pszValue[3], (uint8_t)pszValue[4]);
    241         uint8_t blue = Hex2Dec((uint8_t)pszValue[5], (uint8_t)pszValue[6]);
    242         *dwColor = ArgbEncode(255, red, green, blue);
    243         return true;
    244       }
    245       default:
    246         return false;
    247     }
    248   }
    249 
    250   if (iValueLen >= 10) {
    251     if (pszValue[iValueLen - 1] != ')' || FXSYS_wcsnicmp(L"rgb(", pszValue, 4))
    252       return false;
    253 
    254     uint8_t rgb[3] = {0};
    255     float fValue;
    256     CFX_CSSPrimitiveType eType;
    257     CFX_CSSValueListParser list(pszValue + 4, iValueLen - 5, ',');
    258     for (int32_t i = 0; i < 3; ++i) {
    259       if (!list.NextValue(&eType, &pszValue, &iValueLen))
    260         return false;
    261       if (eType != CFX_CSSPrimitiveType::Number)
    262         return false;
    263       CFX_CSSNumberType eNumType;
    264       if (!ParseCSSNumber(pszValue, iValueLen, fValue, eNumType))
    265         return false;
    266 
    267       rgb[i] = eNumType == CFX_CSSNumberType::Percent
    268                    ? FXSYS_round(fValue * 2.55f)
    269                    : FXSYS_round(fValue);
    270     }
    271     *dwColor = ArgbEncode(255, rgb[0], rgb[1], rgb[2]);
    272     return true;
    273   }
    274 
    275   const CFX_CSSColorTable* pColor =
    276       GetCSSColorByName(WideStringView(pszValue, iValueLen));
    277   if (!pColor)
    278     return false;
    279 
    280   *dwColor = pColor->dwValue;
    281   return true;
    282 }
    283 
    284 CFX_CSSDeclaration::CFX_CSSDeclaration() {}
    285 
    286 CFX_CSSDeclaration::~CFX_CSSDeclaration() {}
    287 
    288 RetainPtr<CFX_CSSValue> CFX_CSSDeclaration::GetProperty(
    289     CFX_CSSProperty eProperty,
    290     bool* bImportant) const {
    291   for (const auto& p : properties_) {
    292     if (p->eProperty == eProperty) {
    293       *bImportant = p->bImportant;
    294       return p->pValue;
    295     }
    296   }
    297   return nullptr;
    298 }
    299 
    300 void CFX_CSSDeclaration::AddPropertyHolder(CFX_CSSProperty eProperty,
    301                                            RetainPtr<CFX_CSSValue> pValue,
    302                                            bool bImportant) {
    303   auto pHolder = pdfium::MakeUnique<CFX_CSSPropertyHolder>();
    304   pHolder->bImportant = bImportant;
    305   pHolder->eProperty = eProperty;
    306   pHolder->pValue = pValue;
    307   properties_.push_back(std::move(pHolder));
    308 }
    309 
    310 void CFX_CSSDeclaration::AddProperty(const CFX_CSSPropertyTable* pTable,
    311                                      const WideStringView& value) {
    312   ASSERT(!value.IsEmpty());
    313 
    314   const wchar_t* pszValue = value.unterminated_c_str();
    315   int32_t iValueLen = value.GetLength();
    316   bool bImportant = false;
    317   if (iValueLen >= 10 && pszValue[iValueLen - 10] == '!' &&
    318       FXSYS_wcsnicmp(L"important", pszValue + iValueLen - 9, 9) == 0) {
    319     if ((iValueLen -= 10) == 0)
    320       return;
    321 
    322     bImportant = true;
    323   }
    324   const uint32_t dwType = pTable->dwType;
    325   switch (dwType & 0x0F) {
    326     case CFX_CSSVALUETYPE_Primitive: {
    327       static const uint32_t g_ValueGuessOrder[] = {
    328           CFX_CSSVALUETYPE_MaybeNumber, CFX_CSSVALUETYPE_MaybeEnum,
    329           CFX_CSSVALUETYPE_MaybeColor, CFX_CSSVALUETYPE_MaybeString,
    330       };
    331       static const int32_t g_ValueGuessCount =
    332           sizeof(g_ValueGuessOrder) / sizeof(uint32_t);
    333       for (int32_t i = 0; i < g_ValueGuessCount; ++i) {
    334         const uint32_t dwMatch = dwType & g_ValueGuessOrder[i];
    335         if (dwMatch == 0) {
    336           continue;
    337         }
    338         RetainPtr<CFX_CSSValue> pCSSValue;
    339         switch (dwMatch) {
    340           case CFX_CSSVALUETYPE_MaybeNumber:
    341             pCSSValue = ParseNumber(pszValue, iValueLen);
    342             break;
    343           case CFX_CSSVALUETYPE_MaybeEnum:
    344             pCSSValue = ParseEnum(pszValue, iValueLen);
    345             break;
    346           case CFX_CSSVALUETYPE_MaybeColor:
    347             pCSSValue = ParseColor(pszValue, iValueLen);
    348             break;
    349           case CFX_CSSVALUETYPE_MaybeString:
    350             pCSSValue = ParseString(pszValue, iValueLen);
    351             break;
    352           default:
    353             break;
    354         }
    355         if (pCSSValue) {
    356           AddPropertyHolder(pTable->eName, pCSSValue, bImportant);
    357           return;
    358         }
    359 
    360         if ((dwType & ~(g_ValueGuessOrder[i])) == CFX_CSSVALUETYPE_Primitive)
    361           return;
    362       }
    363       break;
    364     }
    365     case CFX_CSSVALUETYPE_Shorthand: {
    366       RetainPtr<CFX_CSSValue> pWidth;
    367       switch (pTable->eName) {
    368         case CFX_CSSProperty::Font:
    369           ParseFontProperty(pszValue, iValueLen, bImportant);
    370           return;
    371         case CFX_CSSProperty::Border:
    372           if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
    373             AddPropertyHolder(CFX_CSSProperty::BorderLeftWidth, pWidth,
    374                               bImportant);
    375             AddPropertyHolder(CFX_CSSProperty::BorderTopWidth, pWidth,
    376                               bImportant);
    377             AddPropertyHolder(CFX_CSSProperty::BorderRightWidth, pWidth,
    378                               bImportant);
    379             AddPropertyHolder(CFX_CSSProperty::BorderBottomWidth, pWidth,
    380                               bImportant);
    381             return;
    382           }
    383           break;
    384         case CFX_CSSProperty::BorderLeft:
    385           if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
    386             AddPropertyHolder(CFX_CSSProperty::BorderLeftWidth, pWidth,
    387                               bImportant);
    388             return;
    389           }
    390           break;
    391         case CFX_CSSProperty::BorderTop:
    392           if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
    393             AddPropertyHolder(CFX_CSSProperty::BorderTopWidth, pWidth,
    394                               bImportant);
    395             return;
    396           }
    397           break;
    398         case CFX_CSSProperty::BorderRight:
    399           if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
    400             AddPropertyHolder(CFX_CSSProperty::BorderRightWidth, pWidth,
    401                               bImportant);
    402             return;
    403           }
    404           break;
    405         case CFX_CSSProperty::BorderBottom:
    406           if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
    407             AddPropertyHolder(CFX_CSSProperty::BorderBottomWidth, pWidth,
    408                               bImportant);
    409             return;
    410           }
    411           break;
    412         default:
    413           break;
    414       }
    415     } break;
    416     case CFX_CSSVALUETYPE_List:
    417       ParseValueListProperty(pTable, pszValue, iValueLen, bImportant);
    418       return;
    419     default:
    420       NOTREACHED();
    421       break;
    422   }
    423 }
    424 
    425 void CFX_CSSDeclaration::AddProperty(const WideString& prop,
    426                                      const WideString& value) {
    427   custom_properties_.push_back(
    428       pdfium::MakeUnique<CFX_CSSCustomProperty>(prop, value));
    429 }
    430 
    431 RetainPtr<CFX_CSSValue> CFX_CSSDeclaration::ParseNumber(const wchar_t* pszValue,
    432                                                         int32_t iValueLen) {
    433   float fValue;
    434   CFX_CSSNumberType eUnit;
    435   if (!ParseCSSNumber(pszValue, iValueLen, fValue, eUnit))
    436     return nullptr;
    437   return pdfium::MakeRetain<CFX_CSSNumberValue>(eUnit, fValue);
    438 }
    439 
    440 RetainPtr<CFX_CSSValue> CFX_CSSDeclaration::ParseEnum(const wchar_t* pszValue,
    441                                                       int32_t iValueLen) {
    442   const CFX_CSSPropertyValueTable* pValue =
    443       GetCSSPropertyValueByName(WideStringView(pszValue, iValueLen));
    444   return pValue ? pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName) : nullptr;
    445 }
    446 
    447 RetainPtr<CFX_CSSValue> CFX_CSSDeclaration::ParseColor(const wchar_t* pszValue,
    448                                                        int32_t iValueLen) {
    449   FX_ARGB dwColor;
    450   if (!ParseCSSColor(pszValue, iValueLen, &dwColor))
    451     return nullptr;
    452   return pdfium::MakeRetain<CFX_CSSColorValue>(dwColor);
    453 }
    454 
    455 RetainPtr<CFX_CSSValue> CFX_CSSDeclaration::ParseString(const wchar_t* pszValue,
    456                                                         int32_t iValueLen) {
    457   int32_t iOffset;
    458   if (!ParseCSSString(pszValue, iValueLen, &iOffset, &iValueLen))
    459     return nullptr;
    460 
    461   if (iValueLen <= 0)
    462     return nullptr;
    463 
    464   return pdfium::MakeRetain<CFX_CSSStringValue>(
    465       WideString(pszValue + iOffset, iValueLen));
    466 }
    467 
    468 void CFX_CSSDeclaration::ParseValueListProperty(
    469     const CFX_CSSPropertyTable* pTable,
    470     const wchar_t* pszValue,
    471     int32_t iValueLen,
    472     bool bImportant) {
    473   wchar_t separator =
    474       (pTable->eName == CFX_CSSProperty::FontFamily) ? ',' : ' ';
    475   CFX_CSSValueListParser parser(pszValue, iValueLen, separator);
    476 
    477   const uint32_t dwType = pTable->dwType;
    478   CFX_CSSPrimitiveType eType;
    479   std::vector<RetainPtr<CFX_CSSValue>> list;
    480   while (parser.NextValue(&eType, &pszValue, &iValueLen)) {
    481     switch (eType) {
    482       case CFX_CSSPrimitiveType::Number:
    483         if (dwType & CFX_CSSVALUETYPE_MaybeNumber) {
    484           float fValue;
    485           CFX_CSSNumberType eNumType;
    486           if (ParseCSSNumber(pszValue, iValueLen, fValue, eNumType))
    487             list.push_back(
    488                 pdfium::MakeRetain<CFX_CSSNumberValue>(eNumType, fValue));
    489         }
    490         break;
    491       case CFX_CSSPrimitiveType::String:
    492         if (dwType & CFX_CSSVALUETYPE_MaybeColor) {
    493           FX_ARGB dwColor;
    494           if (ParseCSSColor(pszValue, iValueLen, &dwColor)) {
    495             list.push_back(pdfium::MakeRetain<CFX_CSSColorValue>(dwColor));
    496             continue;
    497           }
    498         }
    499         if (dwType & CFX_CSSVALUETYPE_MaybeEnum) {
    500           const CFX_CSSPropertyValueTable* pValue =
    501               GetCSSPropertyValueByName(WideStringView(pszValue, iValueLen));
    502           if (pValue) {
    503             list.push_back(pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName));
    504             continue;
    505           }
    506         }
    507         if (dwType & CFX_CSSVALUETYPE_MaybeString) {
    508           list.push_back(pdfium::MakeRetain<CFX_CSSStringValue>(
    509               WideString(pszValue, iValueLen)));
    510         }
    511         break;
    512       case CFX_CSSPrimitiveType::RGB:
    513         if (dwType & CFX_CSSVALUETYPE_MaybeColor) {
    514           FX_ARGB dwColor;
    515           if (ParseCSSColor(pszValue, iValueLen, &dwColor)) {
    516             list.push_back(pdfium::MakeRetain<CFX_CSSColorValue>(dwColor));
    517           }
    518         }
    519         break;
    520       default:
    521         break;
    522     }
    523   }
    524   if (list.empty())
    525     return;
    526 
    527   switch (pTable->eName) {
    528     case CFX_CSSProperty::BorderWidth:
    529       Add4ValuesProperty(list, bImportant, CFX_CSSProperty::BorderLeftWidth,
    530                          CFX_CSSProperty::BorderTopWidth,
    531                          CFX_CSSProperty::BorderRightWidth,
    532                          CFX_CSSProperty::BorderBottomWidth);
    533       return;
    534     case CFX_CSSProperty::Margin:
    535       Add4ValuesProperty(list, bImportant, CFX_CSSProperty::MarginLeft,
    536                          CFX_CSSProperty::MarginTop,
    537                          CFX_CSSProperty::MarginRight,
    538                          CFX_CSSProperty::MarginBottom);
    539       return;
    540     case CFX_CSSProperty::Padding:
    541       Add4ValuesProperty(list, bImportant, CFX_CSSProperty::PaddingLeft,
    542                          CFX_CSSProperty::PaddingTop,
    543                          CFX_CSSProperty::PaddingRight,
    544                          CFX_CSSProperty::PaddingBottom);
    545       return;
    546     default: {
    547       auto pList = pdfium::MakeRetain<CFX_CSSValueList>(list);
    548       AddPropertyHolder(pTable->eName, pList, bImportant);
    549       return;
    550     }
    551   }
    552 }
    553 
    554 void CFX_CSSDeclaration::Add4ValuesProperty(
    555     const std::vector<RetainPtr<CFX_CSSValue>>& list,
    556     bool bImportant,
    557     CFX_CSSProperty eLeft,
    558     CFX_CSSProperty eTop,
    559     CFX_CSSProperty eRight,
    560     CFX_CSSProperty eBottom) {
    561   switch (list.size()) {
    562     case 1:
    563       AddPropertyHolder(eLeft, list[0], bImportant);
    564       AddPropertyHolder(eTop, list[0], bImportant);
    565       AddPropertyHolder(eRight, list[0], bImportant);
    566       AddPropertyHolder(eBottom, list[0], bImportant);
    567       return;
    568     case 2:
    569       AddPropertyHolder(eLeft, list[1], bImportant);
    570       AddPropertyHolder(eTop, list[0], bImportant);
    571       AddPropertyHolder(eRight, list[1], bImportant);
    572       AddPropertyHolder(eBottom, list[0], bImportant);
    573       return;
    574     case 3:
    575       AddPropertyHolder(eLeft, list[1], bImportant);
    576       AddPropertyHolder(eTop, list[0], bImportant);
    577       AddPropertyHolder(eRight, list[1], bImportant);
    578       AddPropertyHolder(eBottom, list[2], bImportant);
    579       return;
    580     case 4:
    581       AddPropertyHolder(eLeft, list[3], bImportant);
    582       AddPropertyHolder(eTop, list[0], bImportant);
    583       AddPropertyHolder(eRight, list[1], bImportant);
    584       AddPropertyHolder(eBottom, list[2], bImportant);
    585       return;
    586     default:
    587       break;
    588   }
    589 }
    590 
    591 bool CFX_CSSDeclaration::ParseBorderProperty(
    592     const wchar_t* pszValue,
    593     int32_t iValueLen,
    594     RetainPtr<CFX_CSSValue>& pWidth) const {
    595   pWidth.Reset(nullptr);
    596 
    597   CFX_CSSValueListParser parser(pszValue, iValueLen, ' ');
    598   CFX_CSSPrimitiveType eType;
    599   while (parser.NextValue(&eType, &pszValue, &iValueLen)) {
    600     switch (eType) {
    601       case CFX_CSSPrimitiveType::Number: {
    602         if (pWidth)
    603           continue;
    604 
    605         float fValue;
    606         CFX_CSSNumberType eNumType;
    607         if (ParseCSSNumber(pszValue, iValueLen, fValue, eNumType))
    608           pWidth = pdfium::MakeRetain<CFX_CSSNumberValue>(eNumType, fValue);
    609         break;
    610       }
    611       case CFX_CSSPrimitiveType::String: {
    612         const CFX_CSSColorTable* pColorItem =
    613             GetCSSColorByName(WideStringView(pszValue, iValueLen));
    614         if (pColorItem)
    615           continue;
    616 
    617         const CFX_CSSPropertyValueTable* pValue =
    618             GetCSSPropertyValueByName(WideStringView(pszValue, iValueLen));
    619         if (!pValue)
    620           continue;
    621 
    622         switch (pValue->eName) {
    623           case CFX_CSSPropertyValue::Thin:
    624           case CFX_CSSPropertyValue::Thick:
    625           case CFX_CSSPropertyValue::Medium:
    626             if (!pWidth)
    627               pWidth = pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName);
    628             break;
    629           default:
    630             break;
    631         }
    632         break;
    633       }
    634       default:
    635         break;
    636     }
    637   }
    638   if (!pWidth)
    639     pWidth =
    640         pdfium::MakeRetain<CFX_CSSNumberValue>(CFX_CSSNumberType::Number, 0.0f);
    641 
    642   return true;
    643 }
    644 
    645 void CFX_CSSDeclaration::ParseFontProperty(const wchar_t* pszValue,
    646                                            int32_t iValueLen,
    647                                            bool bImportant) {
    648   CFX_CSSValueListParser parser(pszValue, iValueLen, '/');
    649   RetainPtr<CFX_CSSValue> pStyle;
    650   RetainPtr<CFX_CSSValue> pVariant;
    651   RetainPtr<CFX_CSSValue> pWeight;
    652   RetainPtr<CFX_CSSValue> pFontSize;
    653   RetainPtr<CFX_CSSValue> pLineHeight;
    654   std::vector<RetainPtr<CFX_CSSValue>> familyList;
    655   CFX_CSSPrimitiveType eType;
    656   while (parser.NextValue(&eType, &pszValue, &iValueLen)) {
    657     switch (eType) {
    658       case CFX_CSSPrimitiveType::String: {
    659         const CFX_CSSPropertyValueTable* pValue =
    660             GetCSSPropertyValueByName(WideStringView(pszValue, iValueLen));
    661         if (pValue) {
    662           switch (pValue->eName) {
    663             case CFX_CSSPropertyValue::XxSmall:
    664             case CFX_CSSPropertyValue::XSmall:
    665             case CFX_CSSPropertyValue::Small:
    666             case CFX_CSSPropertyValue::Medium:
    667             case CFX_CSSPropertyValue::Large:
    668             case CFX_CSSPropertyValue::XLarge:
    669             case CFX_CSSPropertyValue::XxLarge:
    670             case CFX_CSSPropertyValue::Smaller:
    671             case CFX_CSSPropertyValue::Larger:
    672               if (!pFontSize)
    673                 pFontSize = pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName);
    674               continue;
    675             case CFX_CSSPropertyValue::Bold:
    676             case CFX_CSSPropertyValue::Bolder:
    677             case CFX_CSSPropertyValue::Lighter:
    678               if (!pWeight)
    679                 pWeight = pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName);
    680               continue;
    681             case CFX_CSSPropertyValue::Italic:
    682             case CFX_CSSPropertyValue::Oblique:
    683               if (!pStyle)
    684                 pStyle = pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName);
    685               continue;
    686             case CFX_CSSPropertyValue::SmallCaps:
    687               if (!pVariant)
    688                 pVariant = pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName);
    689               continue;
    690             case CFX_CSSPropertyValue::Normal:
    691               if (!pStyle)
    692                 pStyle = pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName);
    693               else if (!pVariant)
    694                 pVariant = pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName);
    695               else if (!pWeight)
    696                 pWeight = pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName);
    697               else if (!pFontSize)
    698                 pFontSize = pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName);
    699               else if (!pLineHeight)
    700                 pLineHeight =
    701                     pdfium::MakeRetain<CFX_CSSEnumValue>(pValue->eName);
    702               continue;
    703             default:
    704               break;
    705           }
    706         }
    707         if (pFontSize) {
    708           familyList.push_back(pdfium::MakeRetain<CFX_CSSStringValue>(
    709               WideString(pszValue, iValueLen)));
    710         }
    711         parser.UseCommaSeparator();
    712         break;
    713       }
    714       case CFX_CSSPrimitiveType::Number: {
    715         float fValue;
    716         CFX_CSSNumberType eNumType;
    717         if (!ParseCSSNumber(pszValue, iValueLen, fValue, eNumType))
    718           break;
    719         if (eType == CFX_CSSPrimitiveType::Number) {
    720           switch ((int32_t)fValue) {
    721             case 100:
    722             case 200:
    723             case 300:
    724             case 400:
    725             case 500:
    726             case 600:
    727             case 700:
    728             case 800:
    729             case 900:
    730               if (!pWeight)
    731                 pWeight = pdfium::MakeRetain<CFX_CSSNumberValue>(
    732                     CFX_CSSNumberType::Number, fValue);
    733               continue;
    734           }
    735         }
    736         if (!pFontSize)
    737           pFontSize = pdfium::MakeRetain<CFX_CSSNumberValue>(eNumType, fValue);
    738         else if (!pLineHeight)
    739           pLineHeight =
    740               pdfium::MakeRetain<CFX_CSSNumberValue>(eNumType, fValue);
    741         break;
    742       }
    743       default:
    744         break;
    745     }
    746   }
    747 
    748   if (!pStyle) {
    749     pStyle = pdfium::MakeRetain<CFX_CSSEnumValue>(CFX_CSSPropertyValue::Normal);
    750   }
    751   if (!pVariant) {
    752     pVariant =
    753         pdfium::MakeRetain<CFX_CSSEnumValue>(CFX_CSSPropertyValue::Normal);
    754   }
    755   if (!pWeight) {
    756     pWeight =
    757         pdfium::MakeRetain<CFX_CSSEnumValue>(CFX_CSSPropertyValue::Normal);
    758   }
    759   if (!pFontSize) {
    760     pFontSize =
    761         pdfium::MakeRetain<CFX_CSSEnumValue>(CFX_CSSPropertyValue::Medium);
    762   }
    763   if (!pLineHeight) {
    764     pLineHeight =
    765         pdfium::MakeRetain<CFX_CSSEnumValue>(CFX_CSSPropertyValue::Normal);
    766   }
    767 
    768   AddPropertyHolder(CFX_CSSProperty::FontStyle, pStyle, bImportant);
    769   AddPropertyHolder(CFX_CSSProperty::FontVariant, pVariant, bImportant);
    770   AddPropertyHolder(CFX_CSSProperty::FontWeight, pWeight, bImportant);
    771   AddPropertyHolder(CFX_CSSProperty::FontSize, pFontSize, bImportant);
    772   AddPropertyHolder(CFX_CSSProperty::LineHeight, pLineHeight, bImportant);
    773   if (!familyList.empty()) {
    774     auto pList = pdfium::MakeRetain<CFX_CSSValueList>(familyList);
    775     AddPropertyHolder(CFX_CSSProperty::FontFamily, pList, bImportant);
    776   }
    777 }
    778 
    779 size_t CFX_CSSDeclaration::PropertyCountForTesting() const {
    780   return properties_.size();
    781 }
    782