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