1 // Copyright 2017 The PDFium Authors
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/check.h"
19
20 class CFX_CSSStyleSheetTest : public testing::Test {
21 public:
SetUp()22 void SetUp() override {
23 sheet_ = std::make_unique<CFX_CSSStyleSheet>();
24 decl_ = nullptr;
25 }
26
TearDown()27 void TearDown() override { decl_ = nullptr; }
28
VerifyLoadFails(WideStringView buf)29 void VerifyLoadFails(WideStringView buf) {
30 DCHECK(sheet_);
31 EXPECT_FALSE(sheet_->LoadBuffer(buf));
32 }
33
LoadAndVerifyRuleCount(WideStringView buf,size_t rule_count)34 void LoadAndVerifyRuleCount(WideStringView buf, size_t rule_count) {
35 DCHECK(sheet_);
36 EXPECT_TRUE(sheet_->LoadBuffer(buf));
37 EXPECT_EQ(sheet_->CountRules(), rule_count);
38 }
39
LoadAndVerifyDecl(WideStringView buf,const std::vector<WideString> & selectors,size_t decl_count)40 void LoadAndVerifyDecl(WideStringView buf,
41 const std::vector<WideString>& selectors,
42 size_t decl_count) {
43 LoadAndVerifyRuleCount(buf, 1);
44 CFX_CSSStyleRule* style = sheet_->GetRule(0);
45 ASSERT_TRUE(style);
46 EXPECT_EQ(selectors.size(), style->CountSelectorLists());
47
48 for (size_t i = 0; i < selectors.size(); i++) {
49 uint32_t hash = FX_HashCode_GetLoweredW(selectors[i].AsStringView());
50 EXPECT_EQ(hash, style->GetSelectorList(i)->name_hash());
51 }
52
53 decl_ = style->GetDeclaration();
54 ASSERT_TRUE(decl_);
55 EXPECT_EQ(decl_->PropertyCountForTesting(), decl_count);
56 }
57
VerifyFloat(CFX_CSSProperty prop,float val,CFX_CSSNumberValue::Unit unit)58 void VerifyFloat(CFX_CSSProperty prop,
59 float val,
60 CFX_CSSNumberValue::Unit unit) {
61 DCHECK(decl_);
62
63 bool important;
64 RetainPtr<CFX_CSSValue> v = decl_->GetProperty(prop, &important);
65 EXPECT_EQ(v->GetType(), CFX_CSSValue::PrimitiveType::kNumber);
66 EXPECT_EQ(v.AsRaw<CFX_CSSNumberValue>()->unit(), unit);
67 EXPECT_EQ(v.AsRaw<CFX_CSSNumberValue>()->value(), val);
68 }
69
VerifyEnum(CFX_CSSProperty prop,CFX_CSSPropertyValue val)70 void VerifyEnum(CFX_CSSProperty prop, CFX_CSSPropertyValue val) {
71 DCHECK(decl_);
72
73 bool important;
74 RetainPtr<CFX_CSSValue> v = decl_->GetProperty(prop, &important);
75 EXPECT_EQ(v->GetType(), CFX_CSSValue::PrimitiveType::kEnum);
76 EXPECT_EQ(v.AsRaw<CFX_CSSEnumValue>()->Value(), val);
77 }
78
VerifyList(CFX_CSSProperty prop,std::vector<CFX_CSSPropertyValue> expected_values)79 void VerifyList(CFX_CSSProperty prop,
80 std::vector<CFX_CSSPropertyValue> expected_values) {
81 DCHECK(decl_);
82
83 bool important;
84 RetainPtr<CFX_CSSValueList> list =
85 decl_->GetProperty(prop, &important).As<CFX_CSSValueList>();
86 ASSERT_TRUE(list);
87 const std::vector<RetainPtr<CFX_CSSValue>>& values = list->values();
88 ASSERT_EQ(values.size(), expected_values.size());
89
90 for (size_t i = 0; i < expected_values.size(); ++i) {
91 const auto& val = values[i];
92 EXPECT_EQ(val->GetType(), CFX_CSSValue::PrimitiveType::kEnum);
93 EXPECT_EQ(val.AsRaw<CFX_CSSEnumValue>()->Value(), expected_values[i]);
94 }
95 }
96
HasSelector(CFX_CSSStyleRule * style,WideStringView selector)97 static bool HasSelector(CFX_CSSStyleRule* style, WideStringView selector) {
98 uint32_t hash = FX_HashCode_GetLoweredW(selector);
99 for (size_t i = 0; i < style->CountSelectorLists(); ++i) {
100 if (style->GetSelectorList(i)->name_hash() == hash)
101 return true;
102 }
103 return false;
104 }
105
106 std::unique_ptr<CFX_CSSStyleSheet> sheet_;
107 CFX_CSSDeclaration* decl_;
108 };
109
TEST_F(CFX_CSSStyleSheetTest,ParseEmpty)110 TEST_F(CFX_CSSStyleSheetTest, ParseEmpty) {
111 LoadAndVerifyRuleCount(L"", 0);
112 }
113
TEST_F(CFX_CSSStyleSheetTest,ParseBlankEmpty)114 TEST_F(CFX_CSSStyleSheetTest, ParseBlankEmpty) {
115 LoadAndVerifyRuleCount(L" \n\r\t", 0);
116 }
117
TEST_F(CFX_CSSStyleSheetTest,ParseStrayClose1)118 TEST_F(CFX_CSSStyleSheetTest, ParseStrayClose1) {
119 VerifyLoadFails(L"}");
120 }
121
TEST_F(CFX_CSSStyleSheetTest,ParseStrayClose2)122 TEST_F(CFX_CSSStyleSheetTest, ParseStrayClose2) {
123 LoadAndVerifyRuleCount(L"foo }", 0);
124 }
125
TEST_F(CFX_CSSStyleSheetTest,ParseStrayClose3)126 TEST_F(CFX_CSSStyleSheetTest, ParseStrayClose3) {
127 VerifyLoadFails(L"foo {a: b}}");
128 }
129
TEST_F(CFX_CSSStyleSheetTest,ParseEmptySelector)130 TEST_F(CFX_CSSStyleSheetTest, ParseEmptySelector) {
131 VerifyLoadFails(L"{a: b}");
132 }
133
TEST_F(CFX_CSSStyleSheetTest,ParseEmptyBody)134 TEST_F(CFX_CSSStyleSheetTest, ParseEmptyBody) {
135 LoadAndVerifyRuleCount(L"foo {}", 0);
136 }
137
TEST_F(CFX_CSSStyleSheetTest,ParseMultipleSelectors)138 TEST_F(CFX_CSSStyleSheetTest, ParseMultipleSelectors) {
139 const wchar_t* buf =
140 L"a { border: 10px; }\n"
141 L"bcdef { text-decoration: underline; }\n"
142 L"* { padding: 0; }\n";
143 EXPECT_TRUE(sheet_->LoadBuffer(buf));
144 ASSERT_EQ(3u, sheet_->CountRules());
145
146 CFX_CSSStyleRule* style = sheet_->GetRule(0);
147 ASSERT_TRUE(style);
148 EXPECT_EQ(1u, style->CountSelectorLists());
149 EXPECT_TRUE(HasSelector(style, L"a"));
150
151 decl_ = style->GetDeclaration();
152 ASSERT_TRUE(decl_);
153 EXPECT_EQ(4u, decl_->PropertyCountForTesting());
154
155 VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 10.0f,
156 CFX_CSSNumberValue::Unit::kPixels);
157 VerifyFloat(CFX_CSSProperty::BorderRightWidth, 10.0f,
158 CFX_CSSNumberValue::Unit::kPixels);
159 VerifyFloat(CFX_CSSProperty::BorderTopWidth, 10.0f,
160 CFX_CSSNumberValue::Unit::kPixels);
161 VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 10.0f,
162 CFX_CSSNumberValue::Unit::kPixels);
163
164 style = sheet_->GetRule(1);
165 ASSERT_TRUE(style);
166 EXPECT_EQ(1u, style->CountSelectorLists());
167 EXPECT_TRUE(HasSelector(style, L"bcdef"));
168 EXPECT_FALSE(HasSelector(style, L"bcde"));
169
170 decl_ = style->GetDeclaration();
171 ASSERT_TRUE(decl_);
172 EXPECT_EQ(1u, decl_->PropertyCountForTesting());
173 VerifyList(CFX_CSSProperty::TextDecoration,
174 {CFX_CSSPropertyValue::Underline});
175
176 style = sheet_->GetRule(2);
177 ASSERT_TRUE(style);
178 EXPECT_EQ(1u, style->CountSelectorLists());
179 EXPECT_TRUE(HasSelector(style, L"*"));
180
181 decl_ = style->GetDeclaration();
182 ASSERT_TRUE(decl_);
183 EXPECT_EQ(4u, decl_->PropertyCountForTesting());
184 VerifyFloat(CFX_CSSProperty::PaddingLeft, 0.0f,
185 CFX_CSSNumberValue::Unit::kNumber);
186 VerifyFloat(CFX_CSSProperty::PaddingRight, 0.0f,
187 CFX_CSSNumberValue::Unit::kNumber);
188 VerifyFloat(CFX_CSSProperty::PaddingTop, 0.0f,
189 CFX_CSSNumberValue::Unit::kNumber);
190 VerifyFloat(CFX_CSSProperty::PaddingBottom, 0.0f,
191 CFX_CSSNumberValue::Unit::kNumber);
192 }
193
TEST_F(CFX_CSSStyleSheetTest,ParseChildSelectors)194 TEST_F(CFX_CSSStyleSheetTest, ParseChildSelectors) {
195 const wchar_t* buf = L"a b c { border: 10px; }";
196 EXPECT_TRUE(sheet_->LoadBuffer(buf));
197 EXPECT_EQ(1u, sheet_->CountRules());
198
199 CFX_CSSStyleRule* style = sheet_->GetRule(0);
200 ASSERT_TRUE(style);
201 EXPECT_EQ(1u, style->CountSelectorLists());
202
203 const auto* sel = style->GetSelectorList(0);
204 ASSERT_TRUE(sel);
205 EXPECT_EQ(FX_HashCode_GetLoweredW(L"c"), sel->name_hash());
206
207 sel = sel->next_selector();
208 ASSERT_TRUE(sel);
209 EXPECT_EQ(FX_HashCode_GetLoweredW(L"b"), sel->name_hash());
210
211 sel = sel->next_selector();
212 ASSERT_TRUE(sel);
213 EXPECT_EQ(FX_HashCode_GetLoweredW(L"a"), sel->name_hash());
214
215 sel = sel->next_selector();
216 EXPECT_FALSE(sel);
217
218 decl_ = style->GetDeclaration();
219 ASSERT_TRUE(decl_);
220 EXPECT_EQ(4u, decl_->PropertyCountForTesting());
221
222 VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 10.0f,
223 CFX_CSSNumberValue::Unit::kPixels);
224 VerifyFloat(CFX_CSSProperty::BorderRightWidth, 10.0f,
225 CFX_CSSNumberValue::Unit::kPixels);
226 VerifyFloat(CFX_CSSProperty::BorderTopWidth, 10.0f,
227 CFX_CSSNumberValue::Unit::kPixels);
228 VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 10.0f,
229 CFX_CSSNumberValue::Unit::kPixels);
230 }
231
TEST_F(CFX_CSSStyleSheetTest,ParseUnhandledSelectors)232 TEST_F(CFX_CSSStyleSheetTest, ParseUnhandledSelectors) {
233 const wchar_t* buf = L"a > b { padding: 0; }";
234 EXPECT_TRUE(sheet_->LoadBuffer(buf));
235 EXPECT_EQ(0u, sheet_->CountRules());
236
237 buf = L"a[first] { padding: 0; }";
238 EXPECT_TRUE(sheet_->LoadBuffer(buf));
239 EXPECT_EQ(0u, sheet_->CountRules());
240
241 buf = L"a+b { padding: 0; }";
242 EXPECT_TRUE(sheet_->LoadBuffer(buf));
243 EXPECT_EQ(0u, sheet_->CountRules());
244
245 buf = L"a ^ b { padding: 0; }";
246 EXPECT_TRUE(sheet_->LoadBuffer(buf));
247 EXPECT_EQ(0u, sheet_->CountRules());
248 }
249
TEST_F(CFX_CSSStyleSheetTest,ParseMultipleSelectorsCombined)250 TEST_F(CFX_CSSStyleSheetTest, ParseMultipleSelectorsCombined) {
251 LoadAndVerifyDecl(L"a, b, c { border: 5px; }", {L"a", L"b", L"c"}, 4);
252 }
253
TEST_F(CFX_CSSStyleSheetTest,ParseBorder)254 TEST_F(CFX_CSSStyleSheetTest, ParseBorder) {
255 LoadAndVerifyDecl(L"a { border: 5px; }", {L"a"}, 4);
256 VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 5.0,
257 CFX_CSSNumberValue::Unit::kPixels);
258 VerifyFloat(CFX_CSSProperty::BorderRightWidth, 5.0,
259 CFX_CSSNumberValue::Unit::kPixels);
260 VerifyFloat(CFX_CSSProperty::BorderTopWidth, 5.0,
261 CFX_CSSNumberValue::Unit::kPixels);
262 VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 5.0,
263 CFX_CSSNumberValue::Unit::kPixels);
264 }
265
TEST_F(CFX_CSSStyleSheetTest,ParseBorderFull)266 TEST_F(CFX_CSSStyleSheetTest, ParseBorderFull) {
267 LoadAndVerifyDecl(L"a { border: 5px solid red; }", {L"a"}, 4);
268 VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 5.0,
269 CFX_CSSNumberValue::Unit::kPixels);
270 VerifyFloat(CFX_CSSProperty::BorderRightWidth, 5.0,
271 CFX_CSSNumberValue::Unit::kPixels);
272 VerifyFloat(CFX_CSSProperty::BorderTopWidth, 5.0,
273 CFX_CSSNumberValue::Unit::kPixels);
274 VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 5.0,
275 CFX_CSSNumberValue::Unit::kPixels);
276 }
277
TEST_F(CFX_CSSStyleSheetTest,ParseBorderLeft)278 TEST_F(CFX_CSSStyleSheetTest, ParseBorderLeft) {
279 LoadAndVerifyDecl(L"a { border-left: 2.5pc; }", {L"a"}, 1);
280 VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 2.5,
281 CFX_CSSNumberValue::Unit::kPicas);
282 }
283
TEST_F(CFX_CSSStyleSheetTest,ParseBorderLeftThick)284 TEST_F(CFX_CSSStyleSheetTest, ParseBorderLeftThick) {
285 LoadAndVerifyDecl(L"a { border-left: thick; }", {L"a"}, 1);
286 VerifyEnum(CFX_CSSProperty::BorderLeftWidth, CFX_CSSPropertyValue::Thick);
287 }
288
TEST_F(CFX_CSSStyleSheetTest,ParseBorderRight)289 TEST_F(CFX_CSSStyleSheetTest, ParseBorderRight) {
290 LoadAndVerifyDecl(L"a { border-right: 2.5pc; }", {L"a"}, 1);
291 VerifyFloat(CFX_CSSProperty::BorderRightWidth, 2.5,
292 CFX_CSSNumberValue::Unit::kPicas);
293 }
294
TEST_F(CFX_CSSStyleSheetTest,ParseBorderTop)295 TEST_F(CFX_CSSStyleSheetTest, ParseBorderTop) {
296 LoadAndVerifyDecl(L"a { border-top: 2.5pc; }", {L"a"}, 1);
297 VerifyFloat(CFX_CSSProperty::BorderTopWidth, 2.5,
298 CFX_CSSNumberValue::Unit::kPicas);
299 }
300
TEST_F(CFX_CSSStyleSheetTest,ParseBorderBottom)301 TEST_F(CFX_CSSStyleSheetTest, ParseBorderBottom) {
302 LoadAndVerifyDecl(L"a { border-bottom: 2.5pc; }", {L"a"}, 1);
303 VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 2.5,
304 CFX_CSSNumberValue::Unit::kPicas);
305 }
306
TEST_F(CFX_CSSStyleSheetTest,ParseWithCommentsInSelector)307 TEST_F(CFX_CSSStyleSheetTest, ParseWithCommentsInSelector) {
308 LoadAndVerifyDecl(L"/**{*/a/**}*/ { border-bottom: 2.5pc; }", {L"a"}, 1);
309 VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 2.5,
310 CFX_CSSNumberValue::Unit::kPicas);
311 }
312
TEST_F(CFX_CSSStyleSheetTest,ParseWithCommentsInProperty)313 TEST_F(CFX_CSSStyleSheetTest, ParseWithCommentsInProperty) {
314 LoadAndVerifyDecl(L"a { /*}*/border-bottom: 2.5pc; }", {L"a"}, 1);
315 VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 2.5,
316 CFX_CSSNumberValue::Unit::kPicas);
317 }
318
TEST_F(CFX_CSSStyleSheetTest,ParseWithCommentsInValue)319 TEST_F(CFX_CSSStyleSheetTest, ParseWithCommentsInValue) {
320 LoadAndVerifyDecl(L"a { border-bottom: /*;*/2.5pc;/* color:red;*/ }", {L"a"},
321 1);
322 VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 2.5,
323 CFX_CSSNumberValue::Unit::kPicas);
324 }
325
TEST_F(CFX_CSSStyleSheetTest,ParseWithUnterminatedCommentInSelector)326 TEST_F(CFX_CSSStyleSheetTest, ParseWithUnterminatedCommentInSelector) {
327 LoadAndVerifyRuleCount(L"a/* { border-bottom: 2.5pc; }", 0);
328 }
329
TEST_F(CFX_CSSStyleSheetTest,ParseWithUnterminatedCommentInProperty)330 TEST_F(CFX_CSSStyleSheetTest, ParseWithUnterminatedCommentInProperty) {
331 LoadAndVerifyRuleCount(L"a { /*border-bottom: 2.5pc; }", 1);
332 }
333
TEST_F(CFX_CSSStyleSheetTest,ParseWithUnterminatedCommentInValue)334 TEST_F(CFX_CSSStyleSheetTest, ParseWithUnterminatedCommentInValue) {
335 LoadAndVerifyRuleCount(L"a { border-bottom: /*2.5pc; }", 1);
336 }
337