Home | History | Annotate | Download | only in css
      1 // Copyright 2017 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_cssstylesheet.h"
      8 
      9 #include <memory>
     10 #include <vector>
     11 
     12 #include "core/fxcrt/css/cfx_cssdeclaration.h"
     13 #include "core/fxcrt/css/cfx_cssenumvalue.h"
     14 #include "core/fxcrt/css/cfx_cssnumbervalue.h"
     15 #include "core/fxcrt/css/cfx_cssstylerule.h"
     16 #include "core/fxcrt/css/cfx_cssvaluelist.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 #include "third_party/base/ptr_util.h"
     19 #include "third_party/base/stl_util.h"
     20 
     21 class CFX_CSSStyleSheetTest : public testing::Test {
     22  public:
     23   void SetUp() override {
     24     sheet_ = pdfium::MakeUnique<CFX_CSSStyleSheet>();
     25     decl_ = nullptr;
     26   }
     27 
     28   void TearDown() override { decl_ = nullptr; }
     29 
     30   void LoadAndVerifyDecl(const wchar_t* buf,
     31                          const std::vector<WideString>& selectors,
     32                          size_t decl_count) {
     33     ASSERT(sheet_);
     34 
     35     EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
     36     EXPECT_EQ(sheet_->CountRules(), 1);
     37 
     38     CFX_CSSStyleRule* style = sheet_->GetRule(0);
     39     EXPECT_EQ(selectors.size(), style->CountSelectorLists());
     40 
     41     for (size_t i = 0; i < selectors.size(); i++) {
     42       uint32_t hash = FX_HashCode_GetW(selectors[i].AsStringView(), true);
     43       EXPECT_EQ(hash, style->GetSelectorList(i)->GetNameHash());
     44     }
     45 
     46     decl_ = style->GetDeclaration();
     47     EXPECT_EQ(decl_->PropertyCountForTesting(), decl_count);
     48   }
     49 
     50   void VerifyFloat(CFX_CSSProperty prop, float val, CFX_CSSNumberType type) {
     51     ASSERT(decl_);
     52 
     53     bool important;
     54     RetainPtr<CFX_CSSValue> v = decl_->GetProperty(prop, &important);
     55     EXPECT_EQ(v->GetType(), CFX_CSSPrimitiveType::Number);
     56     EXPECT_EQ(v.As<CFX_CSSNumberValue>()->Kind(), type);
     57     EXPECT_EQ(v.As<CFX_CSSNumberValue>()->Value(), val);
     58   }
     59 
     60   void VerifyEnum(CFX_CSSProperty prop, CFX_CSSPropertyValue val) {
     61     ASSERT(decl_);
     62 
     63     bool important;
     64     RetainPtr<CFX_CSSValue> v = decl_->GetProperty(prop, &important);
     65     EXPECT_EQ(v->GetType(), CFX_CSSPrimitiveType::Enum);
     66     EXPECT_EQ(v.As<CFX_CSSEnumValue>()->Value(), val);
     67   }
     68 
     69   void VerifyList(CFX_CSSProperty prop,
     70                   std::vector<CFX_CSSPropertyValue> values) {
     71     ASSERT(decl_);
     72 
     73     bool important;
     74     RetainPtr<CFX_CSSValueList> list =
     75         decl_->GetProperty(prop, &important).As<CFX_CSSValueList>();
     76     EXPECT_EQ(list->CountValues(), pdfium::CollectionSize<int32_t>(values));
     77 
     78     for (size_t i = 0; i < values.size(); i++) {
     79       RetainPtr<CFX_CSSValue> val = list->GetValue(i);
     80       EXPECT_EQ(val->GetType(), CFX_CSSPrimitiveType::Enum);
     81       EXPECT_EQ(val.As<CFX_CSSEnumValue>()->Value(), values[i]);
     82     }
     83   }
     84 
     85   std::unique_ptr<CFX_CSSStyleSheet> sheet_;
     86   CFX_CSSDeclaration* decl_;
     87 };
     88 
     89 TEST_F(CFX_CSSStyleSheetTest, ParseMultipleSelectors) {
     90   const wchar_t* buf =
     91       L"a { border: 10px; }\nb { text-decoration: underline; }";
     92   EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
     93   EXPECT_EQ(2, sheet_->CountRules());
     94 
     95   CFX_CSSStyleRule* style = sheet_->GetRule(0);
     96   EXPECT_EQ(1UL, style->CountSelectorLists());
     97 
     98   bool found_selector = false;
     99   uint32_t hash = FX_HashCode_GetW(L"a", true);
    100   for (size_t i = 0; i < style->CountSelectorLists(); i++) {
    101     if (style->GetSelectorList(i)->GetNameHash() == hash) {
    102       found_selector = true;
    103       break;
    104     }
    105   }
    106   EXPECT_TRUE(found_selector);
    107 
    108   decl_ = style->GetDeclaration();
    109   EXPECT_EQ(4UL, decl_->PropertyCountForTesting());
    110 
    111   VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 10.0,
    112               CFX_CSSNumberType::Pixels);
    113   VerifyFloat(CFX_CSSProperty::BorderRightWidth, 10.0,
    114               CFX_CSSNumberType::Pixels);
    115   VerifyFloat(CFX_CSSProperty::BorderTopWidth, 10.0, CFX_CSSNumberType::Pixels);
    116   VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 10.0,
    117               CFX_CSSNumberType::Pixels);
    118 
    119   style = sheet_->GetRule(1);
    120   EXPECT_EQ(1UL, style->CountSelectorLists());
    121 
    122   found_selector = false;
    123   hash = FX_HashCode_GetW(L"b", true);
    124   for (size_t i = 0; i < style->CountSelectorLists(); i++) {
    125     if (style->GetSelectorList(i)->GetNameHash() == hash) {
    126       found_selector = true;
    127       break;
    128     }
    129   }
    130   EXPECT_TRUE(found_selector);
    131 
    132   decl_ = style->GetDeclaration();
    133   EXPECT_EQ(1UL, decl_->PropertyCountForTesting());
    134   VerifyList(CFX_CSSProperty::TextDecoration,
    135              {CFX_CSSPropertyValue::Underline});
    136 }
    137 
    138 TEST_F(CFX_CSSStyleSheetTest, ParseChildSelectors) {
    139   const wchar_t* buf = L"a b c { border: 10px; }";
    140   EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
    141   EXPECT_EQ(1, sheet_->CountRules());
    142 
    143   CFX_CSSStyleRule* style = sheet_->GetRule(0);
    144   EXPECT_EQ(1UL, style->CountSelectorLists());
    145 
    146   auto* sel = style->GetSelectorList(0);
    147   EXPECT_TRUE(sel != nullptr);
    148   EXPECT_EQ(FX_HashCode_GetW(L"c", true), sel->GetNameHash());
    149 
    150   sel = sel->GetNextSelector();
    151   EXPECT_TRUE(sel != nullptr);
    152   EXPECT_EQ(FX_HashCode_GetW(L"b", true), sel->GetNameHash());
    153 
    154   sel = sel->GetNextSelector();
    155   EXPECT_TRUE(sel != nullptr);
    156   EXPECT_EQ(FX_HashCode_GetW(L"a", true), sel->GetNameHash());
    157 
    158   sel = sel->GetNextSelector();
    159   EXPECT_TRUE(sel == nullptr);
    160 
    161   decl_ = style->GetDeclaration();
    162   EXPECT_EQ(4UL, decl_->PropertyCountForTesting());
    163 
    164   VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 10.0,
    165               CFX_CSSNumberType::Pixels);
    166   VerifyFloat(CFX_CSSProperty::BorderRightWidth, 10.0,
    167               CFX_CSSNumberType::Pixels);
    168   VerifyFloat(CFX_CSSProperty::BorderTopWidth, 10.0, CFX_CSSNumberType::Pixels);
    169   VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 10.0,
    170               CFX_CSSNumberType::Pixels);
    171 }
    172 
    173 TEST_F(CFX_CSSStyleSheetTest, ParseUnhandledSelectors) {
    174   const wchar_t* buf = L"a > b { padding: 0; }";
    175   EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
    176   EXPECT_EQ(0, sheet_->CountRules());
    177 
    178   buf = L"a[first] { padding: 0; }";
    179   EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
    180   EXPECT_EQ(0, sheet_->CountRules());
    181 
    182   buf = L"a+b { padding: 0; }";
    183   EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
    184   EXPECT_EQ(0, sheet_->CountRules());
    185 
    186   buf = L"a ^ b { padding: 0; }";
    187   EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf)));
    188   EXPECT_EQ(0, sheet_->CountRules());
    189 }
    190 
    191 TEST_F(CFX_CSSStyleSheetTest, ParseMultipleSelectorsCombined) {
    192   LoadAndVerifyDecl(L"a, b, c { border: 5px; }", {L"a", L"b", L"c"}, 4);
    193 }
    194 
    195 TEST_F(CFX_CSSStyleSheetTest, ParseBorder) {
    196   LoadAndVerifyDecl(L"a { border: 5px; }", {L"a"}, 4);
    197   VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 5.0, CFX_CSSNumberType::Pixels);
    198   VerifyFloat(CFX_CSSProperty::BorderRightWidth, 5.0,
    199               CFX_CSSNumberType::Pixels);
    200   VerifyFloat(CFX_CSSProperty::BorderTopWidth, 5.0, CFX_CSSNumberType::Pixels);
    201   VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 5.0,
    202               CFX_CSSNumberType::Pixels);
    203 }
    204 
    205 TEST_F(CFX_CSSStyleSheetTest, ParseBorderFull) {
    206   LoadAndVerifyDecl(L"a { border: 5px solid red; }", {L"a"}, 4);
    207   VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 5.0, CFX_CSSNumberType::Pixels);
    208   VerifyFloat(CFX_CSSProperty::BorderRightWidth, 5.0,
    209               CFX_CSSNumberType::Pixels);
    210   VerifyFloat(CFX_CSSProperty::BorderTopWidth, 5.0, CFX_CSSNumberType::Pixels);
    211   VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 5.0,
    212               CFX_CSSNumberType::Pixels);
    213 }
    214 
    215 TEST_F(CFX_CSSStyleSheetTest, ParseBorderLeft) {
    216   LoadAndVerifyDecl(L"a { border-left: 2.5pc; }", {L"a"}, 1);
    217   VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 2.5, CFX_CSSNumberType::Picas);
    218 }
    219 
    220 TEST_F(CFX_CSSStyleSheetTest, ParseBorderLeftThick) {
    221   LoadAndVerifyDecl(L"a { border-left: thick; }", {L"a"}, 1);
    222   VerifyEnum(CFX_CSSProperty::BorderLeftWidth, CFX_CSSPropertyValue::Thick);
    223 }
    224 
    225 TEST_F(CFX_CSSStyleSheetTest, ParseBorderRight) {
    226   LoadAndVerifyDecl(L"a { border-right: 2.5pc; }", {L"a"}, 1);
    227   VerifyFloat(CFX_CSSProperty::BorderRightWidth, 2.5, CFX_CSSNumberType::Picas);
    228 }
    229 
    230 TEST_F(CFX_CSSStyleSheetTest, ParseBorderTop) {
    231   LoadAndVerifyDecl(L"a { border-top: 2.5pc; }", {L"a"}, 1);
    232   VerifyFloat(CFX_CSSProperty::BorderTopWidth, 2.5, CFX_CSSNumberType::Picas);
    233 }
    234 
    235 TEST_F(CFX_CSSStyleSheetTest, ParseBorderBottom) {
    236   LoadAndVerifyDecl(L"a { border-bottom: 2.5pc; }", {L"a"}, 1);
    237   VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 2.5,
    238               CFX_CSSNumberType::Picas);
    239 }
    240