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