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