xref: /aosp_15_r20/external/pdfium/core/fxcrt/css/cfx_cssstylesheet_unittest.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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