1 // Copyright 2014 The Chromium 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 #include "third_party/blink/renderer/core/css/parser/css_property_parser.h"
6 
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "third_party/blink/renderer/core/css/css_color_value.h"
9 #include "third_party/blink/renderer/core/css/css_grid_integer_repeat_value.h"
10 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
11 #include "third_party/blink/renderer/core/css/css_value_list.h"
12 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
13 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
14 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
15 #include "third_party/blink/renderer/core/html/html_html_element.h"
16 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
17 #include "third_party/blink/renderer/platform/heap/heap.h"
18 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
19 
20 namespace blink {
21 
ComputeNumberOfTracks(const CSSValueList * value_list)22 static int ComputeNumberOfTracks(const CSSValueList* value_list) {
23   int number_of_tracks = 0;
24   for (auto& value : *value_list) {
25     if (value->IsGridLineNamesValue())
26       continue;
27     if (auto* repeat_value =
28             DynamicTo<cssvalue::CSSGridIntegerRepeatValue>(*value)) {
29       number_of_tracks +=
30           repeat_value->Repetitions() * ComputeNumberOfTracks(repeat_value);
31       continue;
32     }
33     ++number_of_tracks;
34   }
35   return number_of_tracks;
36 }
37 
IsValidPropertyValueForStyleRule(CSSPropertyID property_id,const String & value)38 static bool IsValidPropertyValueForStyleRule(CSSPropertyID property_id,
39                                              const String& value) {
40   CSSTokenizer tokenizer(value);
41   const auto tokens = tokenizer.TokenizeToEOF();
42   const CSSParserTokenRange range(tokens);
43   HeapVector<CSSPropertyValue, 256> parsed_properties;
44   return CSSPropertyParser::ParseValue(
45       property_id, false, range,
46       StrictCSSParserContext(SecureContextMode::kSecureContext),
47       parsed_properties, StyleRule::RuleType::kStyle);
48 }
49 
TEST(CSSPropertyParserTest,CSSPaint_Functions)50 TEST(CSSPropertyParserTest, CSSPaint_Functions) {
51   const CSSValue* value = CSSParser::ParseSingleValue(
52       CSSPropertyID::kBackgroundImage, "paint(foo, func1(1px, 3px), red)",
53       StrictCSSParserContext(SecureContextMode::kSecureContext));
54   ASSERT_TRUE(value);
55   ASSERT_TRUE(value->IsValueList());
56   EXPECT_EQ(value->CssText(), "paint(foo, func1(1px, 3px), red)");
57 }
58 
TEST(CSSPropertyParserTest,CSSPaint_NoArguments)59 TEST(CSSPropertyParserTest, CSSPaint_NoArguments) {
60   const CSSValue* value = CSSParser::ParseSingleValue(
61       CSSPropertyID::kBackgroundImage, "paint(foo)",
62       StrictCSSParserContext(SecureContextMode::kSecureContext));
63   ASSERT_TRUE(value);
64   ASSERT_TRUE(value->IsValueList());
65   EXPECT_EQ(value->CssText(), "paint(foo)");
66 }
67 
TEST(CSSPropertyParserTest,CSSPaint_ValidArguments)68 TEST(CSSPropertyParserTest, CSSPaint_ValidArguments) {
69   const CSSValue* value = CSSParser::ParseSingleValue(
70       CSSPropertyID::kBackgroundImage, "paint(bar, 10px, red)",
71       StrictCSSParserContext(SecureContextMode::kSecureContext));
72   ASSERT_TRUE(value);
73   ASSERT_TRUE(value->IsValueList());
74   EXPECT_EQ(value->CssText(), "paint(bar, 10px, red)");
75 }
76 
TEST(CSSPropertyParserTest,CSSPaint_InvalidFormat)77 TEST(CSSPropertyParserTest, CSSPaint_InvalidFormat) {
78   const CSSValue* value = CSSParser::ParseSingleValue(
79       CSSPropertyID::kBackgroundImage, "paint(foo bar)",
80       StrictCSSParserContext(SecureContextMode::kSecureContext));
81   // Illegal format should not be parsed.
82   ASSERT_FALSE(value);
83 }
84 
TEST(CSSPropertyParserTest,CSSPaint_TrailingComma)85 TEST(CSSPropertyParserTest, CSSPaint_TrailingComma) {
86   const CSSValue* value = CSSParser::ParseSingleValue(
87       CSSPropertyID::kBackgroundImage, "paint(bar, 10px, red,)",
88       StrictCSSParserContext(SecureContextMode::kSecureContext));
89   ASSERT_FALSE(value);
90 }
91 
TEST(CSSPropertyParserTest,CSSPaint_PaintArgumentsDiabled)92 TEST(CSSPropertyParserTest, CSSPaint_PaintArgumentsDiabled) {
93   ScopedCSSPaintAPIArgumentsForTest css_paint_api_arguments(false);
94   const CSSValue* value = CSSParser::ParseSingleValue(
95       CSSPropertyID::kBackgroundImage, "paint(bar, 10px, red)",
96       StrictCSSParserContext(SecureContextMode::kSecureContext));
97   ASSERT_FALSE(value);
98 }
99 
TEST(CSSPropertyParserTest,GridTrackLimit1)100 TEST(CSSPropertyParserTest, GridTrackLimit1) {
101   const CSSValue* value = CSSParser::ParseSingleValue(
102       CSSPropertyID::kGridTemplateColumns, "repeat(999, 20px)",
103       StrictCSSParserContext(SecureContextMode::kSecureContext));
104   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 999);
105 }
106 
TEST(CSSPropertyParserTest,GridTrackLimit2)107 TEST(CSSPropertyParserTest, GridTrackLimit2) {
108   const CSSValue* value = CSSParser::ParseSingleValue(
109       CSSPropertyID::kGridTemplateRows, "repeat(999, 20px)",
110       StrictCSSParserContext(SecureContextMode::kSecureContext));
111   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 999);
112 }
113 
TEST(CSSPropertyParserTest,GridTrackLimit3)114 TEST(CSSPropertyParserTest, GridTrackLimit3) {
115   const CSSValue* value = CSSParser::ParseSingleValue(
116       CSSPropertyID::kGridTemplateColumns, "repeat(1000000, 10%)",
117       StrictCSSParserContext(SecureContextMode::kSecureContext));
118   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000);
119 }
120 
TEST(CSSPropertyParserTest,GridTrackLimit4)121 TEST(CSSPropertyParserTest, GridTrackLimit4) {
122   const CSSValue* value = CSSParser::ParseSingleValue(
123       CSSPropertyID::kGridTemplateRows, "repeat(1000000, 10%)",
124       StrictCSSParserContext(SecureContextMode::kSecureContext));
125   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000);
126 }
127 
TEST(CSSPropertyParserTest,GridTrackLimit5)128 TEST(CSSPropertyParserTest, GridTrackLimit5) {
129   const CSSValue* value = CSSParser::ParseSingleValue(
130       CSSPropertyID::kGridTemplateColumns,
131       "repeat(1000000, [first] min-content [last])",
132       StrictCSSParserContext(SecureContextMode::kSecureContext));
133   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000);
134 }
135 
TEST(CSSPropertyParserTest,GridTrackLimit6)136 TEST(CSSPropertyParserTest, GridTrackLimit6) {
137   const CSSValue* value = CSSParser::ParseSingleValue(
138       CSSPropertyID::kGridTemplateRows,
139       "repeat(1000000, [first] min-content [last])",
140       StrictCSSParserContext(SecureContextMode::kSecureContext));
141   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000);
142 }
143 
TEST(CSSPropertyParserTest,GridTrackLimit7)144 TEST(CSSPropertyParserTest, GridTrackLimit7) {
145   const CSSValue* value = CSSParser::ParseSingleValue(
146       CSSPropertyID::kGridTemplateColumns, "repeat(1000001, auto)",
147       StrictCSSParserContext(SecureContextMode::kSecureContext));
148   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000);
149 }
150 
TEST(CSSPropertyParserTest,GridTrackLimit8)151 TEST(CSSPropertyParserTest, GridTrackLimit8) {
152   const CSSValue* value = CSSParser::ParseSingleValue(
153       CSSPropertyID::kGridTemplateRows, "repeat(1000001, auto)",
154       StrictCSSParserContext(SecureContextMode::kSecureContext));
155   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000);
156 }
157 
TEST(CSSPropertyParserTest,GridTrackLimit9)158 TEST(CSSPropertyParserTest, GridTrackLimit9) {
159   const CSSValue* value = CSSParser::ParseSingleValue(
160       CSSPropertyID::kGridTemplateColumns,
161       "repeat(400000, 2em minmax(10px, max-content) 0.5fr)",
162       StrictCSSParserContext(SecureContextMode::kSecureContext));
163   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 999);
164 }
165 
TEST(CSSPropertyParserTest,GridTrackLimit10)166 TEST(CSSPropertyParserTest, GridTrackLimit10) {
167   const CSSValue* value = CSSParser::ParseSingleValue(
168       CSSPropertyID::kGridTemplateRows,
169       "repeat(400000, 2em minmax(10px, max-content) 0.5fr)",
170       StrictCSSParserContext(SecureContextMode::kSecureContext));
171   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 999);
172 }
173 
TEST(CSSPropertyParserTest,GridTrackLimit11)174 TEST(CSSPropertyParserTest, GridTrackLimit11) {
175   const CSSValue* value = CSSParser::ParseSingleValue(
176       CSSPropertyID::kGridTemplateColumns,
177       "repeat(600000, [first] 3vh 10% 2fr [nav] 10px auto 1fr 6em [last])",
178       StrictCSSParserContext(SecureContextMode::kSecureContext));
179   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 994);
180 }
181 
TEST(CSSPropertyParserTest,GridTrackLimit12)182 TEST(CSSPropertyParserTest, GridTrackLimit12) {
183   const CSSValue* value = CSSParser::ParseSingleValue(
184       CSSPropertyID::kGridTemplateRows,
185       "repeat(600000, [first] 3vh 10% 2fr [nav] 10px auto 1fr 6em [last])",
186       StrictCSSParserContext(SecureContextMode::kSecureContext));
187   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 994);
188 }
189 
TEST(CSSPropertyParserTest,GridTrackLimit13)190 TEST(CSSPropertyParserTest, GridTrackLimit13) {
191   const CSSValue* value = CSSParser::ParseSingleValue(
192       CSSPropertyID::kGridTemplateColumns,
193       "repeat(100000000000000000000, 10% 1fr)",
194       StrictCSSParserContext(SecureContextMode::kSecureContext));
195   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000);
196 }
197 
TEST(CSSPropertyParserTest,GridTrackLimit14)198 TEST(CSSPropertyParserTest, GridTrackLimit14) {
199   const CSSValue* value = CSSParser::ParseSingleValue(
200       CSSPropertyID::kGridTemplateRows,
201       "repeat(100000000000000000000, 10% 1fr)",
202       StrictCSSParserContext(SecureContextMode::kSecureContext));
203   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000);
204 }
205 
TEST(CSSPropertyParserTest,GridTrackLimit15)206 TEST(CSSPropertyParserTest, GridTrackLimit15) {
207   const CSSValue* value = CSSParser::ParseSingleValue(
208       CSSPropertyID::kGridTemplateColumns,
209       "repeat(100000000000000000000, 10% 5em 1fr auto auto 15px min-content)",
210       StrictCSSParserContext(SecureContextMode::kSecureContext));
211   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 994);
212 }
213 
TEST(CSSPropertyParserTest,GridTrackLimit16)214 TEST(CSSPropertyParserTest, GridTrackLimit16) {
215   const CSSValue* value = CSSParser::ParseSingleValue(
216       CSSPropertyID::kGridTemplateRows,
217       "repeat(100000000000000000000, 10% 5em 1fr auto auto 15px min-content)",
218       StrictCSSParserContext(SecureContextMode::kSecureContext));
219   EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 994);
220 }
221 
GetGridPositionInteger(const CSSValue & value)222 static int GetGridPositionInteger(const CSSValue& value) {
223   const auto& list = To<CSSValueList>(value);
224   DCHECK_EQ(list.length(), static_cast<size_t>(1));
225   const auto& primitive_value = To<CSSPrimitiveValue>(list.Item(0));
226   DCHECK(primitive_value.IsNumber());
227   return primitive_value.GetIntValue();
228 }
229 
TEST(CSSPropertyParserTest,GridPositionLimit1)230 TEST(CSSPropertyParserTest, GridPositionLimit1) {
231   const CSSValue* value = CSSParser::ParseSingleValue(
232       CSSPropertyID::kGridColumnStart, "999",
233       StrictCSSParserContext(SecureContextMode::kSecureContext));
234   DCHECK(value);
235   EXPECT_EQ(GetGridPositionInteger(*value), 999);
236 }
237 
TEST(CSSPropertyParserTest,GridPositionLimit2)238 TEST(CSSPropertyParserTest, GridPositionLimit2) {
239   const CSSValue* value = CSSParser::ParseSingleValue(
240       CSSPropertyID::kGridColumnEnd, "1000000",
241       StrictCSSParserContext(SecureContextMode::kSecureContext));
242   DCHECK(value);
243   EXPECT_EQ(GetGridPositionInteger(*value), 1000);
244 }
245 
TEST(CSSPropertyParserTest,GridPositionLimit3)246 TEST(CSSPropertyParserTest, GridPositionLimit3) {
247   const CSSValue* value = CSSParser::ParseSingleValue(
248       CSSPropertyID::kGridRowStart, "1000001",
249       StrictCSSParserContext(SecureContextMode::kSecureContext));
250   DCHECK(value);
251   EXPECT_EQ(GetGridPositionInteger(*value), 1000);
252 }
253 
TEST(CSSPropertyParserTest,GridPositionLimit4)254 TEST(CSSPropertyParserTest, GridPositionLimit4) {
255   const CSSValue* value = CSSParser::ParseSingleValue(
256       CSSPropertyID::kGridRowEnd, "5000000000",
257       StrictCSSParserContext(SecureContextMode::kSecureContext));
258   DCHECK(value);
259   EXPECT_EQ(GetGridPositionInteger(*value), 1000);
260 }
261 
TEST(CSSPropertyParserTest,GridPositionLimit5)262 TEST(CSSPropertyParserTest, GridPositionLimit5) {
263   const CSSValue* value = CSSParser::ParseSingleValue(
264       CSSPropertyID::kGridColumnStart, "-999",
265       StrictCSSParserContext(SecureContextMode::kSecureContext));
266   DCHECK(value);
267   EXPECT_EQ(GetGridPositionInteger(*value), -999);
268 }
269 
TEST(CSSPropertyParserTest,GridPositionLimit6)270 TEST(CSSPropertyParserTest, GridPositionLimit6) {
271   const CSSValue* value = CSSParser::ParseSingleValue(
272       CSSPropertyID::kGridColumnEnd, "-1000000",
273       StrictCSSParserContext(SecureContextMode::kSecureContext));
274   DCHECK(value);
275   EXPECT_EQ(GetGridPositionInteger(*value), -1000);
276 }
277 
TEST(CSSPropertyParserTest,GridPositionLimit7)278 TEST(CSSPropertyParserTest, GridPositionLimit7) {
279   const CSSValue* value = CSSParser::ParseSingleValue(
280       CSSPropertyID::kGridRowStart, "-1000001",
281       StrictCSSParserContext(SecureContextMode::kSecureContext));
282   DCHECK(value);
283   EXPECT_EQ(GetGridPositionInteger(*value), -1000);
284 }
285 
TEST(CSSPropertyParserTest,GridPositionLimit8)286 TEST(CSSPropertyParserTest, GridPositionLimit8) {
287   const CSSValue* value = CSSParser::ParseSingleValue(
288       CSSPropertyID::kGridRowEnd, "-5000000000",
289       StrictCSSParserContext(SecureContextMode::kSecureContext));
290   DCHECK(value);
291   EXPECT_EQ(GetGridPositionInteger(*value), -1000);
292 }
293 
TEST(CSSPropertyParserTest,ColorFunction)294 TEST(CSSPropertyParserTest, ColorFunction) {
295   const CSSValue* value = CSSParser::ParseSingleValue(
296       CSSPropertyID::kBackgroundColor, "rgba(0, 0, 0, 1)",
297       StrictCSSParserContext(SecureContextMode::kSecureContext));
298   ASSERT_TRUE(value);
299   EXPECT_EQ(Color::kBlack, To<cssvalue::CSSColorValue>(*value).Value());
300 }
301 
TEST(CSSPropertyParserTest,IncompleteColor)302 TEST(CSSPropertyParserTest, IncompleteColor) {
303   const CSSValue* value = CSSParser::ParseSingleValue(
304       CSSPropertyID::kBackgroundColor, "rgba(123 45",
305       StrictCSSParserContext(SecureContextMode::kSecureContext));
306   ASSERT_FALSE(value);
307 }
308 
TEST(CSSPropertyParserTest,ClipPathEllipse)309 TEST(CSSPropertyParserTest, ClipPathEllipse) {
310   auto dummy_holder = std::make_unique<DummyPageHolder>(IntSize(500, 500));
311   Document* doc = &dummy_holder->GetDocument();
312   Page::InsertOrdinaryPageForTesting(&dummy_holder->GetPage());
313   auto* context = MakeGarbageCollected<CSSParserContext>(
314       kHTMLStandardMode, SecureContextMode::kSecureContext,
315       CSSParserContext::kLiveProfile, doc);
316 
317   CSSParser::ParseSingleValue(CSSPropertyID::kClipPath,
318                               "ellipse(1px 2px at invalid)", context);
319 
320   EXPECT_FALSE(doc->IsUseCounted(WebFeature::kBasicShapeEllipseTwoRadius));
321   CSSParser::ParseSingleValue(CSSPropertyID::kClipPath, "ellipse(1px 2px)",
322                               context);
323   EXPECT_TRUE(doc->IsUseCounted(WebFeature::kBasicShapeEllipseTwoRadius));
324 
325   EXPECT_FALSE(doc->IsUseCounted(WebFeature::kBasicShapeEllipseNoRadius));
326   CSSParser::ParseSingleValue(CSSPropertyID::kClipPath, "ellipse()", context);
327   EXPECT_TRUE(doc->IsUseCounted(WebFeature::kBasicShapeEllipseNoRadius));
328 }
329 
TEST(CSSPropertyParserTest,ScrollCustomizationPropertySingleValue)330 TEST(CSSPropertyParserTest, ScrollCustomizationPropertySingleValue) {
331   ScopedScrollCustomizationForTest scoped_feature(true);
332   const CSSValue* value = CSSParser::ParseSingleValue(
333       CSSPropertyID::kScrollCustomization, "pan-down",
334       StrictCSSParserContext(SecureContextMode::kSecureContext));
335   const auto* list = To<CSSValueList>(value);
336   EXPECT_EQ(1U, list->length());
337   EXPECT_EQ(CSSValueID::kPanDown,
338             To<CSSIdentifierValue>(list->Item(0U)).GetValueID());
339 }
340 
TEST(CSSPropertyParserTest,ScrollCustomizationPropertyTwoValuesCombined)341 TEST(CSSPropertyParserTest, ScrollCustomizationPropertyTwoValuesCombined) {
342   ScopedScrollCustomizationForTest scoped_feature(true);
343   const CSSValue* value = CSSParser::ParseSingleValue(
344       CSSPropertyID::kScrollCustomization, "pan-left pan-y",
345       StrictCSSParserContext(SecureContextMode::kSecureContext));
346   const auto* list = To<CSSValueList>(value);
347   EXPECT_EQ(2U, list->length());
348   EXPECT_EQ(CSSValueID::kPanLeft,
349             To<CSSIdentifierValue>(list->Item(0U)).GetValueID());
350   EXPECT_EQ(CSSValueID::kPanY,
351             To<CSSIdentifierValue>(list->Item(1U)).GetValueID());
352 }
353 
TEST(CSSPropertyParserTest,ScrollCustomizationPropertyInvalidEntries)354 TEST(CSSPropertyParserTest, ScrollCustomizationPropertyInvalidEntries) {
355   // We expect exactly one property value per coordinate.
356   ScopedScrollCustomizationForTest scoped_feature(true);
357   const CSSValue* value = CSSParser::ParseSingleValue(
358       CSSPropertyID::kScrollCustomization, "pan-left pan-right",
359       StrictCSSParserContext(SecureContextMode::kSecureContext));
360   EXPECT_FALSE(value);
361   value = CSSParser::ParseSingleValue(
362       CSSPropertyID::kScrollCustomization, "pan-up pan-down",
363       StrictCSSParserContext(SecureContextMode::kSecureContext));
364   EXPECT_FALSE(value);
365   value = CSSParser::ParseSingleValue(
366       CSSPropertyID::kScrollCustomization, "pan-x pan-left",
367       StrictCSSParserContext(SecureContextMode::kSecureContext));
368   EXPECT_FALSE(value);
369   value = CSSParser::ParseSingleValue(
370       CSSPropertyID::kScrollCustomization, "pan-x pan-x",
371       StrictCSSParserContext(SecureContextMode::kSecureContext));
372   EXPECT_FALSE(value);
373 }
374 
TEST(CSSPropertyParserTest,GradientUseCount)375 TEST(CSSPropertyParserTest, GradientUseCount) {
376   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
377   Document& document = dummy_page_holder->GetDocument();
378   Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
379   WebFeature feature = WebFeature::kCSSGradient;
380   EXPECT_FALSE(document.IsUseCounted(feature));
381   document.documentElement()->setInnerHTML(
382       "<style>* { background-image: linear-gradient(red, blue); }</style>");
383   EXPECT_TRUE(document.IsUseCounted(feature));
384 }
385 
TEST(CSSPropertyParserTest,PaintUseCount)386 TEST(CSSPropertyParserTest, PaintUseCount) {
387   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
388   Document& document = dummy_page_holder->GetDocument();
389   Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
390   document.SetSecureContextModeForTesting(SecureContextMode::kSecureContext);
391   WebFeature feature = WebFeature::kCSSPaintFunction;
392   EXPECT_FALSE(document.IsUseCounted(feature));
393   document.documentElement()->setInnerHTML(
394       "<style>span { background-image: paint(geometry); }</style>");
395   EXPECT_TRUE(document.IsUseCounted(feature));
396 }
397 
TEST(CSSPropertyParserTest,CrossFadeUseCount)398 TEST(CSSPropertyParserTest, CrossFadeUseCount) {
399   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
400   Document& document = dummy_page_holder->GetDocument();
401   Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
402   WebFeature feature = WebFeature::kWebkitCrossFade;
403   EXPECT_FALSE(document.IsUseCounted(feature));
404   document.documentElement()->setInnerHTML(
405       "<style>div { background-image: -webkit-cross-fade(url('from.png'), "
406       "url('to.png'), 0.2); }</style>");
407   EXPECT_TRUE(document.IsUseCounted(feature));
408 }
409 
TEST(CSSPropertyParserTest,TwoValueOverflowOverlayCount)410 TEST(CSSPropertyParserTest, TwoValueOverflowOverlayCount) {
411   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
412   Document& document = dummy_page_holder->GetDocument();
413   Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
414   WebFeature feature = WebFeature::kCSSValueOverflowOverlay;
415   WebFeature feature2 = WebFeature::kTwoValuedOverflow;
416   EXPECT_FALSE(document.IsUseCounted(feature));
417   EXPECT_FALSE(document.IsUseCounted(feature2));
418   document.documentElement()->setInnerHTML(
419       "<div style=\"height: 10px; width: 10px; overflow: overlay overlay;\">"
420       "<div style=\"height: 50px; width: 50px;\"></div></div>");
421   EXPECT_TRUE(document.IsUseCounted(feature));
422   EXPECT_TRUE(document.IsUseCounted(feature2));
423 }
424 
TEST(CSSPropertyParserTest,OneValueOverflowOverlayCount)425 TEST(CSSPropertyParserTest, OneValueOverflowOverlayCount) {
426   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
427   Document& document = dummy_page_holder->GetDocument();
428   Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
429   WebFeature feature = WebFeature::kCSSValueOverflowOverlay;
430   WebFeature feature2 = WebFeature::kTwoValuedOverflow;
431   EXPECT_FALSE(document.IsUseCounted(feature));
432   EXPECT_FALSE(document.IsUseCounted(feature2));
433   document.documentElement()->setInnerHTML(
434       "<div style=\"height: 10px; width: 10px; overflow: overlay;\">"
435       "<div style=\"height: 50px; width: 50px;\"></div></div>");
436   EXPECT_TRUE(document.IsUseCounted(feature));
437   EXPECT_FALSE(document.IsUseCounted(feature2));
438 }
439 
TEST(CSSPropertyParserTest,OverflowXOverlayCount)440 TEST(CSSPropertyParserTest, OverflowXOverlayCount) {
441   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
442   Document& document = dummy_page_holder->GetDocument();
443   Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
444   WebFeature feature = WebFeature::kCSSValueOverflowOverlay;
445   WebFeature feature2 = WebFeature::kTwoValuedOverflow;
446   EXPECT_FALSE(document.IsUseCounted(feature));
447   EXPECT_FALSE(document.IsUseCounted(feature2));
448   document.documentElement()->setInnerHTML(
449       "<div style=\"height: 10px; width: 10px; overflow-x: overlay;\">"
450       "<div style=\"height: 50px; width: 50px;\"></div></div>");
451   EXPECT_TRUE(document.IsUseCounted(feature));
452   EXPECT_FALSE(document.IsUseCounted(feature2));
453 }
454 
TEST(CSSPropertyParserTest,OverflowYOverlayCount)455 TEST(CSSPropertyParserTest, OverflowYOverlayCount) {
456   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
457   Document& document = dummy_page_holder->GetDocument();
458   Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
459   WebFeature feature = WebFeature::kCSSValueOverflowOverlay;
460   WebFeature feature2 = WebFeature::kTwoValuedOverflow;
461   EXPECT_FALSE(document.IsUseCounted(feature));
462   EXPECT_FALSE(document.IsUseCounted(feature2));
463   document.documentElement()->setInnerHTML(
464       "<div style=\"height: 10px; width: 10px; overflow-y: overlay;\">"
465       "<div style=\"height: 50px; width: 50px;\"></div></div>");
466   EXPECT_TRUE(document.IsUseCounted(feature));
467   EXPECT_FALSE(document.IsUseCounted(feature2));
468 }
469 
TEST(CSSPropertyParserTest,OverflowFirstValueOverlayCount)470 TEST(CSSPropertyParserTest, OverflowFirstValueOverlayCount) {
471   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
472   Document& document = dummy_page_holder->GetDocument();
473   Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
474   WebFeature feature = WebFeature::kCSSValueOverflowOverlay;
475   WebFeature feature2 = WebFeature::kTwoValuedOverflow;
476   EXPECT_FALSE(document.IsUseCounted(feature));
477   EXPECT_FALSE(document.IsUseCounted(feature2));
478   document.documentElement()->setInnerHTML(
479       "<div style=\"height: 10px; width: 10px; overflow: overlay scroll;\">"
480       "<div style=\"height: 50px; width: 50px;\"></div></div>");
481   EXPECT_TRUE(document.IsUseCounted(feature));
482   EXPECT_TRUE(document.IsUseCounted(feature2));
483 }
484 
TEST(CSSPropertyParserTest,OverflowSecondValueOverlayCount)485 TEST(CSSPropertyParserTest, OverflowSecondValueOverlayCount) {
486   auto dummy_page_holder = std::make_unique<DummyPageHolder>(IntSize(800, 600));
487   Document& document = dummy_page_holder->GetDocument();
488   Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage());
489   WebFeature feature = WebFeature::kCSSValueOverflowOverlay;
490   WebFeature feature2 = WebFeature::kTwoValuedOverflow;
491   EXPECT_FALSE(document.IsUseCounted(feature));
492   EXPECT_FALSE(document.IsUseCounted(feature2));
493   document.documentElement()->setInnerHTML(
494       "<div style=\"height: 10px; width: 10px; overflow: scroll overlay;\">"
495       "<div style=\"height: 50px; width: 50px;\"></div></div>");
496   EXPECT_TRUE(document.IsUseCounted(feature));
497   EXPECT_TRUE(document.IsUseCounted(feature2));
498 }
499 
TEST(CSSPropertyParserTest,DropViewportDescriptor)500 TEST(CSSPropertyParserTest, DropViewportDescriptor) {
501   EXPECT_FALSE(IsValidPropertyValueForStyleRule(CSSPropertyID::kOrientation,
502                                                 "portrait"));
503   EXPECT_FALSE(
504       IsValidPropertyValueForStyleRule(CSSPropertyID::kOrientation, "inherit"));
505   EXPECT_FALSE(IsValidPropertyValueForStyleRule(CSSPropertyID::kOrientation,
506                                                 "var(--dummy)"));
507 }
508 
TEST(CSSPropertyParserTest,DropFontfaceDescriptor)509 TEST(CSSPropertyParserTest, DropFontfaceDescriptor) {
510   EXPECT_FALSE(
511       IsValidPropertyValueForStyleRule(CSSPropertyID::kSrc, "url(blah)"));
512   EXPECT_FALSE(
513       IsValidPropertyValueForStyleRule(CSSPropertyID::kSrc, "inherit"));
514   EXPECT_FALSE(
515       IsValidPropertyValueForStyleRule(CSSPropertyID::kSrc, "var(--dummy)"));
516 }
517 
518 class CSSPropertyUseCounterTest : public ::testing::Test {
519  public:
SetUp()520   void SetUp() override {
521     dummy_page_holder_ = std::make_unique<DummyPageHolder>(IntSize(800, 600));
522     Page::InsertOrdinaryPageForTesting(&dummy_page_holder_->GetPage());
523     // Use strict mode.
524     GetDocument().SetCompatibilityMode(Document::kNoQuirksMode);
525   }
TearDown()526   void TearDown() override { dummy_page_holder_ = nullptr; }
527 
ParseProperty(CSSPropertyID property,const char * value_string)528   void ParseProperty(CSSPropertyID property, const char* value_string) {
529     const CSSValue* value = CSSParser::ParseSingleValue(
530         property, String(value_string),
531         MakeGarbageCollected<CSSParserContext>(GetDocument()));
532     DCHECK(value);
533   }
534 
IsCounted(WebFeature feature)535   bool IsCounted(WebFeature feature) {
536     return GetDocument().IsUseCounted(feature);
537   }
538 
GetDocument()539   Document& GetDocument() { return dummy_page_holder_->GetDocument(); }
540 
541  private:
542   std::unique_ptr<DummyPageHolder> dummy_page_holder_;
543 };
544 
TEST_F(CSSPropertyUseCounterTest,CSSPropertyXUnitlessUseCount)545 TEST_F(CSSPropertyUseCounterTest, CSSPropertyXUnitlessUseCount) {
546   WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue;
547   EXPECT_FALSE(IsCounted(feature));
548   ParseProperty(CSSPropertyID::kX, "0");
549   // Unitless zero should not register.
550   EXPECT_FALSE(IsCounted(feature));
551   ParseProperty(CSSPropertyID::kX, "42");
552   EXPECT_TRUE(IsCounted(feature));
553 }
554 
TEST_F(CSSPropertyUseCounterTest,CSSPropertyYUnitlessUseCount)555 TEST_F(CSSPropertyUseCounterTest, CSSPropertyYUnitlessUseCount) {
556   WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue;
557   EXPECT_FALSE(IsCounted(feature));
558   ParseProperty(CSSPropertyID::kY, "0");
559   // Unitless zero should not register.
560   EXPECT_FALSE(IsCounted(feature));
561   ParseProperty(CSSPropertyID::kY, "42");
562   EXPECT_TRUE(IsCounted(feature));
563 }
564 
TEST_F(CSSPropertyUseCounterTest,CSSPropertyRUnitlessUseCount)565 TEST_F(CSSPropertyUseCounterTest, CSSPropertyRUnitlessUseCount) {
566   WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue;
567   EXPECT_FALSE(IsCounted(feature));
568   ParseProperty(CSSPropertyID::kR, "0");
569   // Unitless zero should not register.
570   EXPECT_FALSE(IsCounted(feature));
571   ParseProperty(CSSPropertyID::kR, "42");
572   EXPECT_TRUE(IsCounted(feature));
573 }
574 
TEST_F(CSSPropertyUseCounterTest,CSSPropertyRxUnitlessUseCount)575 TEST_F(CSSPropertyUseCounterTest, CSSPropertyRxUnitlessUseCount) {
576   WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue;
577   EXPECT_FALSE(IsCounted(feature));
578   ParseProperty(CSSPropertyID::kRx, "0");
579   // Unitless zero should not register.
580   EXPECT_FALSE(IsCounted(feature));
581   ParseProperty(CSSPropertyID::kRx, "42");
582   EXPECT_TRUE(IsCounted(feature));
583 }
584 
TEST_F(CSSPropertyUseCounterTest,CSSPropertyRyUnitlessUseCount)585 TEST_F(CSSPropertyUseCounterTest, CSSPropertyRyUnitlessUseCount) {
586   WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue;
587   EXPECT_FALSE(IsCounted(feature));
588   ParseProperty(CSSPropertyID::kRy, "0");
589   // Unitless zero should not register.
590   EXPECT_FALSE(IsCounted(feature));
591   ParseProperty(CSSPropertyID::kRy, "42");
592   EXPECT_TRUE(IsCounted(feature));
593 }
594 
TEST_F(CSSPropertyUseCounterTest,CSSPropertyCxUnitlessUseCount)595 TEST_F(CSSPropertyUseCounterTest, CSSPropertyCxUnitlessUseCount) {
596   WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue;
597   EXPECT_FALSE(IsCounted(feature));
598   ParseProperty(CSSPropertyID::kCx, "0");
599   // Unitless zero should not register.
600   EXPECT_FALSE(IsCounted(feature));
601   ParseProperty(CSSPropertyID::kCx, "42");
602   EXPECT_TRUE(IsCounted(feature));
603 }
604 
TEST_F(CSSPropertyUseCounterTest,CSSPropertyCyUnitlessUseCount)605 TEST_F(CSSPropertyUseCounterTest, CSSPropertyCyUnitlessUseCount) {
606   WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue;
607   EXPECT_FALSE(IsCounted(feature));
608   ParseProperty(CSSPropertyID::kCy, "0");
609   // Unitless zero should not register.
610   EXPECT_FALSE(IsCounted(feature));
611   ParseProperty(CSSPropertyID::kCy, "42");
612   EXPECT_TRUE(IsCounted(feature));
613 }
614 
TEST_F(CSSPropertyUseCounterTest,UnitlessPresentationAttributesNotCounted)615 TEST_F(CSSPropertyUseCounterTest, UnitlessPresentationAttributesNotCounted) {
616   WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue;
617   EXPECT_FALSE(IsCounted(feature));
618   GetDocument().body()->setInnerHTML(R"HTML(
619     <svg>
620       <rect x="42" y="42" rx="42" ry="42"/>
621       <circle cx="42" cy="42" r="42"/>
622     </svg>
623   )HTML");
624   EXPECT_FALSE(IsCounted(feature));
625 }
626 
TEST_F(CSSPropertyUseCounterTest,CSSPropertyDefaultAnimationNameUseCount)627 TEST_F(CSSPropertyUseCounterTest, CSSPropertyDefaultAnimationNameUseCount) {
628   WebFeature feature = WebFeature::kDefaultInCustomIdent;
629   EXPECT_FALSE(IsCounted(feature));
630 
631   ParseProperty(CSSPropertyID::kAnimationName, "initial");
632   EXPECT_FALSE(IsCounted(feature));
633 
634   ParseProperty(CSSPropertyID::kAnimationName, "test");
635   EXPECT_FALSE(IsCounted(feature));
636 
637   ParseProperty(CSSPropertyID::kAnimationName, "default");
638   EXPECT_TRUE(IsCounted(feature));
639 }
640 
TEST_F(CSSPropertyUseCounterTest,CSSPropertyRevertAnimationNameUseCount)641 TEST_F(CSSPropertyUseCounterTest, CSSPropertyRevertAnimationNameUseCount) {
642   WebFeature feature = WebFeature::kRevertInCustomIdent;
643   EXPECT_FALSE(IsCounted(feature));
644 
645   ParseProperty(CSSPropertyID::kAnimationName, "initial");
646   EXPECT_FALSE(IsCounted(feature));
647 
648   ParseProperty(CSSPropertyID::kAnimationName, "test");
649   EXPECT_FALSE(IsCounted(feature));
650 
651   ParseProperty(CSSPropertyID::kAnimationName, "revert");
652   EXPECT_TRUE(IsCounted(feature));
653 }
654 
TEST_F(CSSPropertyUseCounterTest,CSSPropertyContainStyleUseCount)655 TEST_F(CSSPropertyUseCounterTest, CSSPropertyContainStyleUseCount) {
656   WebFeature feature = WebFeature::kCSSValueContainStyle;
657   EXPECT_FALSE(IsCounted(feature));
658   ParseProperty(CSSPropertyID::kContain, "strict");
659   EXPECT_FALSE(IsCounted(feature));
660   ParseProperty(CSSPropertyID::kContain, "content");
661   EXPECT_FALSE(IsCounted(feature));
662   ParseProperty(CSSPropertyID::kContain, "style paint");
663   EXPECT_TRUE(IsCounted(feature));
664 }
665 
TEST_F(CSSPropertyUseCounterTest,CSSPropertyFontSizeWebkitXxxLargeUseCount)666 TEST_F(CSSPropertyUseCounterTest, CSSPropertyFontSizeWebkitXxxLargeUseCount) {
667   WebFeature feature = WebFeature::kFontSizeWebkitXxxLarge;
668   ParseProperty(CSSPropertyID::kFontSize, "xx-small");
669   ParseProperty(CSSPropertyID::kFontSize, "larger");
670   ParseProperty(CSSPropertyID::kFontSize, "smaller");
671   ParseProperty(CSSPropertyID::kFontSize, "10%");
672   ParseProperty(CSSPropertyID::kFontSize, "20px");
673   EXPECT_FALSE(IsCounted(feature));
674   ParseProperty(CSSPropertyID::kFontSize, "-webkit-xxx-large");
675   EXPECT_TRUE(IsCounted(feature));
676 }
677 
TEST(CSSPropertyParserTest,InternalLightDarkColorAuthor)678 TEST(CSSPropertyParserTest, InternalLightDarkColorAuthor) {
679   auto* context = MakeGarbageCollected<CSSParserContext>(
680       kHTMLStandardMode, SecureContextMode::kInsecureContext);
681   // -internal-light-dark-color() is only valid in UA sheets.
682   ASSERT_FALSE(CSSParser::ParseSingleValue(
683       CSSPropertyID::kColor, "-internal-light-dark-color(#000000, #ffffff)",
684       context));
685   ASSERT_FALSE(CSSParser::ParseSingleValue(
686       CSSPropertyID::kColor, "-internal-light-dark-color(red, green)",
687       context));
688 }
689 
TEST(CSSPropertyParserTest,UAInternalLightDarkColor)690 TEST(CSSPropertyParserTest, UAInternalLightDarkColor) {
691   auto* ua_context = MakeGarbageCollected<CSSParserContext>(
692       kUASheetMode, SecureContextMode::kInsecureContext);
693 
694   const struct {
695     const char* value;
696     bool valid;
697   } tests[] = {
698       {"-internal-light-dark-color()", false},
699       {"-internal-light-dark-color(#feedab)", false},
700       {"-internal-light-dark-color(red blue)", false},
701       {"-internal-light-dark-color(red,,blue)", false},
702       {"-internal-light-dark-color(red, blue)", true},
703       {"-internal-light-dark-color(#000000, #ffffff)", true},
704       {"-internal-light-dark-color(rgb(0, 0, 0), hsl(180, 75%, 50%))", true},
705       {"-internal-light-dark-color(rgba(0, 0, 0, 0.5), hsla(180, 75%, 50%, "
706        "0.7))",
707        true},
708   };
709 
710   for (const auto& test : tests) {
711     EXPECT_EQ(!!CSSParser::ParseSingleValue(CSSPropertyID::kColor, test.value,
712                                             ua_context),
713               test.valid);
714   }
715 }
716 
TEST(CSSPropertyParserTest,UAInternalLightDarkColorSerialization)717 TEST(CSSPropertyParserTest, UAInternalLightDarkColorSerialization) {
718   auto* ua_context = MakeGarbageCollected<CSSParserContext>(
719       kUASheetMode, SecureContextMode::kInsecureContext);
720   const CSSValue* value = CSSParser::ParseSingleValue(
721       CSSPropertyID::kColor, "-internal-light-dark-color(red,#aaa)",
722       ua_context);
723   ASSERT_TRUE(value);
724   EXPECT_EQ("-internal-light-dark-color(red, rgb(170, 170, 170))",
725             value->CssText());
726 }
727 
728 }  // namespace blink
729