| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/core/css/parser/css_property_parser.h" |
| |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/renderer/core/css/css_color.h" |
| #include "third_party/blink/renderer/core/css/css_grid_integer_repeat_value.h" |
| #include "third_party/blink/renderer/core/css/css_identifier_value.h" |
| #include "third_party/blink/renderer/core/css/css_image_set_value.h" |
| #include "third_party/blink/renderer/core/css/css_value_list.h" |
| #include "third_party/blink/renderer/core/css/parser/css_parser.h" |
| #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h" |
| #include "third_party/blink/renderer/core/css/style_sheet_contents.h" |
| #include "third_party/blink/renderer/core/execution_context/security_context.h" |
| #include "third_party/blink/renderer/core/frame/local_dom_window.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/html/html_html_element.h" |
| #include "third_party/blink/renderer/core/testing/dummy_page_holder.h" |
| #include "third_party/blink/renderer/platform/heap/garbage_collected.h" |
| #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" |
| |
| namespace blink { |
| |
| static int ComputeNumberOfTracks(const CSSValueList* value_list) { |
| int number_of_tracks = 0; |
| for (auto& value : *value_list) { |
| if (value->IsGridLineNamesValue()) { |
| continue; |
| } |
| if (auto* repeat_value = |
| DynamicTo<cssvalue::CSSGridIntegerRepeatValue>(*value)) { |
| number_of_tracks += |
| repeat_value->Repetitions() * ComputeNumberOfTracks(repeat_value); |
| continue; |
| } |
| ++number_of_tracks; |
| } |
| return number_of_tracks; |
| } |
| |
| static bool IsValidPropertyValueForStyleRule(CSSPropertyID property_id, |
| const String& value) { |
| CSSTokenizer tokenizer(value); |
| const auto tokens = tokenizer.TokenizeToEOF(); |
| const CSSParserTokenRange range(tokens); |
| HeapVector<CSSPropertyValue, 64> parsed_properties; |
| return CSSPropertyParser::ParseValue( |
| property_id, false, {range, value}, |
| StrictCSSParserContext(SecureContextMode::kSecureContext), |
| parsed_properties, StyleRule::RuleType::kStyle); |
| } |
| |
| TEST(CSSPropertyParserTest, CSSPaint_Functions) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kBackgroundImage, "paint(foo, func1(1px, 3px), red)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| ASSERT_TRUE(value); |
| ASSERT_TRUE(value->IsValueList()); |
| EXPECT_EQ(value->CssText(), "paint(foo, func1(1px, 3px), red)"); |
| } |
| |
| TEST(CSSPropertyParserTest, CSSPaint_NoArguments) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kBackgroundImage, "paint(foo)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| ASSERT_TRUE(value); |
| ASSERT_TRUE(value->IsValueList()); |
| EXPECT_EQ(value->CssText(), "paint(foo)"); |
| } |
| |
| TEST(CSSPropertyParserTest, CSSPaint_ValidArguments) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kBackgroundImage, "paint(bar, 10px, red)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| ASSERT_TRUE(value); |
| ASSERT_TRUE(value->IsValueList()); |
| EXPECT_EQ(value->CssText(), "paint(bar, 10px, red)"); |
| } |
| |
| TEST(CSSPropertyParserTest, CSSPaint_InvalidFormat) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kBackgroundImage, "paint(foo bar)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| // Illegal format should not be parsed. |
| ASSERT_FALSE(value); |
| } |
| |
| TEST(CSSPropertyParserTest, CSSPaint_TrailingComma) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kBackgroundImage, "paint(bar, 10px, red,)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| ASSERT_FALSE(value); |
| } |
| |
| TEST(CSSPropertyParserTest, CSSPaint_PaintArgumentsDiabled) { |
| ScopedCSSPaintAPIArgumentsForTest css_paint_api_arguments(false); |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kBackgroundImage, "paint(bar, 10px, red)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| ASSERT_FALSE(value); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit1) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateColumns, "repeat(999, 20px)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 999); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit2) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateRows, "repeat(999, 20px)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 999); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit3) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateColumns, "repeat(1000000, 10%)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000000); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit4) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateRows, "repeat(1000000, 10%)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000000); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit5) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateColumns, |
| "repeat(1000000, [first] min-content [last])", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000000); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit6) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateRows, |
| "repeat(1000000, [first] min-content [last])", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000000); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit7) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateColumns, "repeat(1000001, auto)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000001); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit8) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateRows, "repeat(1000001, auto)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1000001); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit9) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateColumns, |
| "repeat(400000, 2em minmax(10px, max-content) 0.5fr)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1200000); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit10) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateRows, |
| "repeat(400000, 2em minmax(10px, max-content) 0.5fr)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 1200000); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit11) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateColumns, |
| "repeat(600000, [first] 3vh 10% 2fr [nav] 10px auto 1fr 6em [last])", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 4200000); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit12) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateRows, |
| "repeat(600000, [first] 3vh 10% 2fr [nav] 10px auto 1fr 6em [last])", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 4200000); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit13) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateColumns, |
| "repeat(100000000000000000000, 10% 1fr)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 10000000); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit14) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateRows, |
| "repeat(100000000000000000000, 10% 1fr)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 10000000); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit15) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateColumns, |
| "repeat(100000000000000000000, 10% 5em 1fr auto auto 15px min-content)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 9999997); |
| } |
| |
| TEST(CSSPropertyParserTest, GridTrackLimit16) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridTemplateRows, |
| "repeat(100000000000000000000, 10% 5em 1fr auto auto 15px min-content)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_EQ(ComputeNumberOfTracks(To<CSSValueList>(value)), 9999997); |
| } |
| |
| static int GetGridPositionInteger(const CSSValue& value) { |
| const auto& list = To<CSSValueList>(value); |
| DCHECK_EQ(list.length(), static_cast<size_t>(1)); |
| const auto& primitive_value = To<CSSPrimitiveValue>(list.Item(0)); |
| DCHECK(primitive_value.IsNumber()); |
| return primitive_value.GetIntValue(); |
| } |
| |
| TEST(CSSPropertyParserTest, GridPositionLimit1) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridColumnStart, "999", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| DCHECK(value); |
| EXPECT_EQ(GetGridPositionInteger(*value), 999); |
| } |
| |
| TEST(CSSPropertyParserTest, GridPositionLimit2) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridColumnEnd, "1000000", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| DCHECK(value); |
| EXPECT_EQ(GetGridPositionInteger(*value), 1000000); |
| } |
| |
| TEST(CSSPropertyParserTest, GridPositionLimit3) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridRowStart, "1000001", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| DCHECK(value); |
| EXPECT_EQ(GetGridPositionInteger(*value), 1000001); |
| } |
| |
| TEST(CSSPropertyParserTest, GridPositionLimit4) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridRowEnd, "5000000000", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| DCHECK(value); |
| EXPECT_EQ(GetGridPositionInteger(*value), 10000000); |
| } |
| |
| TEST(CSSPropertyParserTest, GridPositionLimit5) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridColumnStart, "-999", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| DCHECK(value); |
| EXPECT_EQ(GetGridPositionInteger(*value), -999); |
| } |
| |
| TEST(CSSPropertyParserTest, GridPositionLimit6) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridColumnEnd, "-1000000", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| DCHECK(value); |
| EXPECT_EQ(GetGridPositionInteger(*value), -1000000); |
| } |
| |
| TEST(CSSPropertyParserTest, GridPositionLimit7) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridRowStart, "-1000001", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| DCHECK(value); |
| EXPECT_EQ(GetGridPositionInteger(*value), -1000001); |
| } |
| |
| TEST(CSSPropertyParserTest, GridPositionLimit8) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kGridRowEnd, "-5000000000", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| DCHECK(value); |
| EXPECT_EQ(GetGridPositionInteger(*value), -10000000); |
| } |
| |
| TEST(CSSPropertyParserTest, ColorFunction) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kBackgroundColor, "rgba(0, 0, 0, 1)", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| ASSERT_TRUE(value); |
| EXPECT_EQ(Color::kBlack, To<cssvalue::CSSColor>(*value).Value()); |
| } |
| |
| TEST(CSSPropertyParserTest, IncompleteColor) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kBackgroundColor, "rgba(123 45", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| ASSERT_FALSE(value); |
| } |
| |
| TEST(CSSPropertyParserTest, ClipPathEllipse) { |
| auto dummy_holder = std::make_unique<DummyPageHolder>(gfx::Size(500, 500)); |
| Document* doc = &dummy_holder->GetDocument(); |
| Page::InsertOrdinaryPageForTesting(&dummy_holder->GetPage()); |
| auto* context = MakeGarbageCollected<CSSParserContext>( |
| kHTMLStandardMode, SecureContextMode::kSecureContext, |
| CSSParserContext::kLiveProfile, doc); |
| |
| CSSParser::ParseSingleValue(CSSPropertyID::kClipPath, |
| "ellipse(1px 2px at invalid)", context); |
| |
| EXPECT_FALSE(doc->IsUseCounted(WebFeature::kBasicShapeEllipseTwoRadius)); |
| CSSParser::ParseSingleValue(CSSPropertyID::kClipPath, "ellipse(1px 2px)", |
| context); |
| EXPECT_TRUE(doc->IsUseCounted(WebFeature::kBasicShapeEllipseTwoRadius)); |
| |
| EXPECT_FALSE(doc->IsUseCounted(WebFeature::kBasicShapeEllipseNoRadius)); |
| CSSParser::ParseSingleValue(CSSPropertyID::kClipPath, "ellipse()", context); |
| EXPECT_TRUE(doc->IsUseCounted(WebFeature::kBasicShapeEllipseNoRadius)); |
| } |
| |
| TEST(CSSPropertyParserTest, ScrollCustomizationPropertySingleValue) { |
| ScopedScrollCustomizationForTest scoped_feature(true); |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kScrollCustomization, "pan-down", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| const auto* list = To<CSSValueList>(value); |
| EXPECT_EQ(1U, list->length()); |
| EXPECT_EQ(CSSValueID::kPanDown, |
| To<CSSIdentifierValue>(list->Item(0U)).GetValueID()); |
| } |
| |
| TEST(CSSPropertyParserTest, ScrollCustomizationPropertyTwoValuesCombined) { |
| ScopedScrollCustomizationForTest scoped_feature(true); |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kScrollCustomization, "pan-left pan-y", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| const auto* list = To<CSSValueList>(value); |
| EXPECT_EQ(2U, list->length()); |
| EXPECT_EQ(CSSValueID::kPanLeft, |
| To<CSSIdentifierValue>(list->Item(0U)).GetValueID()); |
| EXPECT_EQ(CSSValueID::kPanY, |
| To<CSSIdentifierValue>(list->Item(1U)).GetValueID()); |
| } |
| |
| TEST(CSSPropertyParserTest, ScrollCustomizationPropertyInvalidEntries) { |
| // We expect exactly one property value per coordinate. |
| ScopedScrollCustomizationForTest scoped_feature(true); |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kScrollCustomization, "pan-left pan-right", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_FALSE(value); |
| value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kScrollCustomization, "pan-up pan-down", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_FALSE(value); |
| value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kScrollCustomization, "pan-x pan-left", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_FALSE(value); |
| value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kScrollCustomization, "pan-x pan-x", |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| EXPECT_FALSE(value); |
| } |
| |
| TEST(CSSPropertyParserTest, GradientUseCount) { |
| auto dummy_page_holder = |
| std::make_unique<DummyPageHolder>(gfx::Size(800, 600)); |
| Document& document = dummy_page_holder->GetDocument(); |
| Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage()); |
| WebFeature feature = WebFeature::kCSSGradient; |
| EXPECT_FALSE(document.IsUseCounted(feature)); |
| document.documentElement()->setInnerHTML( |
| "<style>* { background-image: linear-gradient(red, blue); }</style>"); |
| EXPECT_TRUE(document.IsUseCounted(feature)); |
| } |
| |
| TEST(CSSPropertyParserTest, PaintUseCount) { |
| auto dummy_page_holder = |
| std::make_unique<DummyPageHolder>(gfx::Size(800, 600)); |
| dummy_page_holder->GetFrame().Loader().CommitNavigation( |
| WebNavigationParams::CreateWithHTMLBufferForTesting( |
| SharedBuffer::Create(), KURL("https://example.com")), |
| nullptr /* extra_data */); |
| Document& document = dummy_page_holder->GetDocument(); |
| Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage()); |
| WebFeature feature = WebFeature::kCSSPaintFunction; |
| EXPECT_FALSE(document.IsUseCounted(feature)); |
| document.documentElement()->setInnerHTML( |
| "<style>span { background-image: paint(geometry); }</style>"); |
| EXPECT_TRUE(document.IsUseCounted(feature)); |
| } |
| |
| TEST(CSSPropertyParserTest, CrossFadeUseCount) { |
| auto dummy_page_holder = |
| std::make_unique<DummyPageHolder>(gfx::Size(800, 600)); |
| Document& document = dummy_page_holder->GetDocument(); |
| Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage()); |
| WebFeature feature = WebFeature::kWebkitCrossFade; |
| EXPECT_FALSE(document.IsUseCounted(feature)); |
| document.documentElement()->setInnerHTML( |
| "<style>div { background-image: -webkit-cross-fade(url('from.png'), " |
| "url('to.png'), 0.2); }</style>"); |
| EXPECT_TRUE(document.IsUseCounted(feature)); |
| } |
| |
| TEST(CSSPropertyParserTest, TwoValueOverflowOverlayCount) { |
| auto dummy_page_holder = |
| std::make_unique<DummyPageHolder>(gfx::Size(800, 600)); |
| Document& document = dummy_page_holder->GetDocument(); |
| Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage()); |
| WebFeature feature = WebFeature::kCSSValueOverflowOverlay; |
| WebFeature feature2 = WebFeature::kTwoValuedOverflow; |
| EXPECT_FALSE(document.IsUseCounted(feature)); |
| EXPECT_FALSE(document.IsUseCounted(feature2)); |
| document.documentElement()->setInnerHTML( |
| "<div style=\"height: 10px; width: 10px; overflow: overlay overlay;\">" |
| "<div style=\"height: 50px; width: 50px;\"></div></div>"); |
| EXPECT_TRUE(document.IsUseCounted(feature)); |
| EXPECT_TRUE(document.IsUseCounted(feature2)); |
| } |
| |
| TEST(CSSPropertyParserTest, OneValueOverflowOverlayCount) { |
| auto dummy_page_holder = |
| std::make_unique<DummyPageHolder>(gfx::Size(800, 600)); |
| Document& document = dummy_page_holder->GetDocument(); |
| Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage()); |
| WebFeature feature = WebFeature::kCSSValueOverflowOverlay; |
| WebFeature feature2 = WebFeature::kTwoValuedOverflow; |
| EXPECT_FALSE(document.IsUseCounted(feature)); |
| EXPECT_FALSE(document.IsUseCounted(feature2)); |
| document.documentElement()->setInnerHTML( |
| "<div style=\"height: 10px; width: 10px; overflow: overlay;\">" |
| "<div style=\"height: 50px; width: 50px;\"></div></div>"); |
| EXPECT_TRUE(document.IsUseCounted(feature)); |
| EXPECT_FALSE(document.IsUseCounted(feature2)); |
| } |
| |
| TEST(CSSPropertyParserTest, OverflowXOverlayCount) { |
| auto dummy_page_holder = |
| std::make_unique<DummyPageHolder>(gfx::Size(800, 600)); |
| Document& document = dummy_page_holder->GetDocument(); |
| Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage()); |
| WebFeature feature = WebFeature::kCSSValueOverflowOverlay; |
| WebFeature feature2 = WebFeature::kTwoValuedOverflow; |
| EXPECT_FALSE(document.IsUseCounted(feature)); |
| EXPECT_FALSE(document.IsUseCounted(feature2)); |
| document.documentElement()->setInnerHTML( |
| "<div style=\"height: 10px; width: 10px; overflow-x: overlay;\">" |
| "<div style=\"height: 50px; width: 50px;\"></div></div>"); |
| EXPECT_TRUE(document.IsUseCounted(feature)); |
| EXPECT_FALSE(document.IsUseCounted(feature2)); |
| } |
| |
| TEST(CSSPropertyParserTest, OverflowYOverlayCount) { |
| auto dummy_page_holder = |
| std::make_unique<DummyPageHolder>(gfx::Size(800, 600)); |
| Document& document = dummy_page_holder->GetDocument(); |
| Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage()); |
| WebFeature feature = WebFeature::kCSSValueOverflowOverlay; |
| WebFeature feature2 = WebFeature::kTwoValuedOverflow; |
| EXPECT_FALSE(document.IsUseCounted(feature)); |
| EXPECT_FALSE(document.IsUseCounted(feature2)); |
| document.documentElement()->setInnerHTML( |
| "<div style=\"height: 10px; width: 10px; overflow-y: overlay;\">" |
| "<div style=\"height: 50px; width: 50px;\"></div></div>"); |
| EXPECT_TRUE(document.IsUseCounted(feature)); |
| EXPECT_FALSE(document.IsUseCounted(feature2)); |
| } |
| |
| TEST(CSSPropertyParserTest, OverflowFirstValueOverlayCount) { |
| auto dummy_page_holder = |
| std::make_unique<DummyPageHolder>(gfx::Size(800, 600)); |
| Document& document = dummy_page_holder->GetDocument(); |
| Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage()); |
| WebFeature feature = WebFeature::kCSSValueOverflowOverlay; |
| WebFeature feature2 = WebFeature::kTwoValuedOverflow; |
| EXPECT_FALSE(document.IsUseCounted(feature)); |
| EXPECT_FALSE(document.IsUseCounted(feature2)); |
| document.documentElement()->setInnerHTML( |
| "<div style=\"height: 10px; width: 10px; overflow: overlay scroll;\">" |
| "<div style=\"height: 50px; width: 50px;\"></div></div>"); |
| EXPECT_TRUE(document.IsUseCounted(feature)); |
| EXPECT_TRUE(document.IsUseCounted(feature2)); |
| } |
| |
| TEST(CSSPropertyParserTest, OverflowSecondValueOverlayCount) { |
| auto dummy_page_holder = |
| std::make_unique<DummyPageHolder>(gfx::Size(800, 600)); |
| Document& document = dummy_page_holder->GetDocument(); |
| Page::InsertOrdinaryPageForTesting(&dummy_page_holder->GetPage()); |
| WebFeature feature = WebFeature::kCSSValueOverflowOverlay; |
| WebFeature feature2 = WebFeature::kTwoValuedOverflow; |
| EXPECT_FALSE(document.IsUseCounted(feature)); |
| EXPECT_FALSE(document.IsUseCounted(feature2)); |
| document.documentElement()->setInnerHTML( |
| "<div style=\"height: 10px; width: 10px; overflow: scroll overlay;\">" |
| "<div style=\"height: 50px; width: 50px;\"></div></div>"); |
| EXPECT_TRUE(document.IsUseCounted(feature)); |
| EXPECT_TRUE(document.IsUseCounted(feature2)); |
| } |
| |
| TEST(CSSPropertyParserTest, DropFontfaceDescriptor) { |
| EXPECT_FALSE( |
| IsValidPropertyValueForStyleRule(CSSPropertyID::kSrc, "url(blah)")); |
| EXPECT_FALSE( |
| IsValidPropertyValueForStyleRule(CSSPropertyID::kSrc, "inherit")); |
| EXPECT_FALSE( |
| IsValidPropertyValueForStyleRule(CSSPropertyID::kSrc, "var(--dummy)")); |
| } |
| |
| class CSSPropertyUseCounterTest : public ::testing::Test { |
| public: |
| void SetUp() override { |
| dummy_page_holder_ = std::make_unique<DummyPageHolder>(gfx::Size(800, 600)); |
| Page::InsertOrdinaryPageForTesting(&dummy_page_holder_->GetPage()); |
| // Use strict mode. |
| GetDocument().SetCompatibilityMode(Document::kNoQuirksMode); |
| } |
| void TearDown() override { dummy_page_holder_ = nullptr; } |
| |
| void ParseProperty(CSSPropertyID property, const char* value_string) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| property, String(value_string), |
| MakeGarbageCollected<CSSParserContext>(GetDocument())); |
| DCHECK(value); |
| } |
| |
| bool IsCounted(WebFeature feature) { |
| return GetDocument().IsUseCounted(feature); |
| } |
| |
| Document& GetDocument() { return dummy_page_holder_->GetDocument(); } |
| |
| private: |
| std::unique_ptr<DummyPageHolder> dummy_page_holder_; |
| }; |
| |
| TEST_F(CSSPropertyUseCounterTest, CSSPropertyXUnitlessUseCount) { |
| WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue; |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kX, "0"); |
| // Unitless zero should not register. |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kX, "42"); |
| EXPECT_TRUE(IsCounted(feature)); |
| } |
| |
| TEST_F(CSSPropertyUseCounterTest, CSSPropertyYUnitlessUseCount) { |
| WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue; |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kY, "0"); |
| // Unitless zero should not register. |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kY, "42"); |
| EXPECT_TRUE(IsCounted(feature)); |
| } |
| |
| TEST_F(CSSPropertyUseCounterTest, CSSPropertyRUnitlessUseCount) { |
| WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue; |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kR, "0"); |
| // Unitless zero should not register. |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kR, "42"); |
| EXPECT_TRUE(IsCounted(feature)); |
| } |
| |
| TEST_F(CSSPropertyUseCounterTest, CSSPropertyRxUnitlessUseCount) { |
| WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue; |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kRx, "0"); |
| // Unitless zero should not register. |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kRx, "42"); |
| EXPECT_TRUE(IsCounted(feature)); |
| } |
| |
| TEST_F(CSSPropertyUseCounterTest, CSSPropertyRyUnitlessUseCount) { |
| WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue; |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kRy, "0"); |
| // Unitless zero should not register. |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kRy, "42"); |
| EXPECT_TRUE(IsCounted(feature)); |
| } |
| |
| TEST_F(CSSPropertyUseCounterTest, CSSPropertyCxUnitlessUseCount) { |
| WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue; |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kCx, "0"); |
| // Unitless zero should not register. |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kCx, "42"); |
| EXPECT_TRUE(IsCounted(feature)); |
| } |
| |
| TEST_F(CSSPropertyUseCounterTest, CSSPropertyCyUnitlessUseCount) { |
| WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue; |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kCy, "0"); |
| // Unitless zero should not register. |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kCy, "42"); |
| EXPECT_TRUE(IsCounted(feature)); |
| } |
| |
| TEST_F(CSSPropertyUseCounterTest, UnitlessPresentationAttributesNotCounted) { |
| WebFeature feature = WebFeature::kSVGGeometryPropertyHasNonZeroUnitlessValue; |
| EXPECT_FALSE(IsCounted(feature)); |
| GetDocument().body()->setInnerHTML(R"HTML( |
| <svg> |
| <rect x="42" y="42" rx="42" ry="42"/> |
| <circle cx="42" cy="42" r="42"/> |
| </svg> |
| )HTML"); |
| EXPECT_FALSE(IsCounted(feature)); |
| } |
| |
| TEST_F(CSSPropertyUseCounterTest, CSSPropertyContainStyleUseCount) { |
| WebFeature feature = WebFeature::kCSSValueContainStyle; |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kContain, "strict"); |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kContain, "content"); |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kContain, "style paint"); |
| EXPECT_TRUE(IsCounted(feature)); |
| } |
| |
| TEST_F(CSSPropertyUseCounterTest, CSSPropertyFontSizeWebkitXxxLargeUseCount) { |
| WebFeature feature = WebFeature::kFontSizeWebkitXxxLarge; |
| ParseProperty(CSSPropertyID::kFontSize, "xx-small"); |
| ParseProperty(CSSPropertyID::kFontSize, "larger"); |
| ParseProperty(CSSPropertyID::kFontSize, "smaller"); |
| ParseProperty(CSSPropertyID::kFontSize, "10%"); |
| ParseProperty(CSSPropertyID::kFontSize, "20px"); |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kFontSize, "-webkit-xxx-large"); |
| EXPECT_TRUE(IsCounted(feature)); |
| } |
| |
| TEST_F(CSSPropertyUseCounterTest, CSSPropertyBackgroundImageWebkitImageSet) { |
| WebFeature feature = WebFeature::kWebkitImageSet; |
| ParseProperty(CSSPropertyID::kBackgroundImage, "none"); |
| EXPECT_FALSE(IsCounted(feature)); |
| ParseProperty(CSSPropertyID::kBackgroundImage, |
| "-webkit-image-set(url(foo) 2x)"); |
| EXPECT_TRUE(IsCounted(feature)); |
| } |
| |
| TEST_F(CSSPropertyUseCounterTest, CSSPropertyBackgroundImageImageSet) { |
| WebFeature feature = WebFeature::kImageSet; |
| |
| ParseProperty(CSSPropertyID::kBackgroundImage, "none"); |
| EXPECT_FALSE(IsCounted(feature)); |
| |
| ParseProperty(CSSPropertyID::kBackgroundImage, "image-set(url(foo) 2x)"); |
| EXPECT_TRUE(IsCounted(feature)); |
| } |
| |
| void TestImageSetParsing(const String& testValue, |
| const String& expectedCssText) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kBackgroundImage, testValue, |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| ASSERT_NE(value, nullptr); |
| |
| const CSSValueList* val_list = To<CSSValueList>(value); |
| ASSERT_EQ(val_list->length(), 1U); |
| |
| const CSSImageSetValue& image_set_value = |
| To<CSSImageSetValue>(val_list->First()); |
| EXPECT_EQ(expectedCssText, image_set_value.CssText()); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetDefaultResolution) { |
| TestImageSetParsing("image-set(url(foo))", "image-set(url(\"foo\") 1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetResolutionUnitX) { |
| TestImageSetParsing("image-set(url(foo) 3x)", "image-set(url(\"foo\") 3x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetResolutionUnitDppx) { |
| TestImageSetParsing("image-set(url(foo) 3dppx)", |
| "image-set(url(\"foo\") 3dppx)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetResolutionUnitDpi) { |
| TestImageSetParsing("image-set(url(foo) 96dpi)", |
| "image-set(url(\"foo\") 96dpi)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetResolutionUnitDpcm) { |
| TestImageSetParsing("image-set(url(foo) 37dpcm)", |
| "image-set(url(\"foo\") 37dpcm)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetZeroResolution) { |
| TestImageSetParsing("image-set(url(foo) 0x)", "image-set(url(\"foo\") 0x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetNegativeResolution) { |
| TestImageSetParsing("image-set(url(foo) -1x)", "image-set(url(\"foo\") -1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetCalcResolutionUnitX) { |
| TestImageSetParsing("image-set(url(foo) calc(1x))", |
| "image-set(url(\"foo\") calc(1dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetAddCalcResolutionUnitX) { |
| TestImageSetParsing("image-set(url(foo) calc(2x + 3x))", |
| "image-set(url(\"foo\") calc(5dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetSubCalcResolutionUnitX) { |
| TestImageSetParsing("image-set(url(foo) calc(2x - 1x))", |
| "image-set(url(\"foo\") calc(1dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetMultCalcResolutionUnitX) { |
| TestImageSetParsing("image-set(url(foo) calc(2x * 3))", |
| "image-set(url(\"foo\") calc(6dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetDivCalcResolutionUnitX) { |
| TestImageSetParsing("image-set(url(foo) calc(6x / 3))", |
| "image-set(url(\"foo\") calc(2dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetAddCalcResolutionUnitDpiWithX) { |
| TestImageSetParsing("image-set(url(foo) calc(96dpi + 2x))", |
| "image-set(url(\"foo\") calc(3dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetAddCalcResolutionUnitDpiWithDpi) { |
| TestImageSetParsing("image-set(url(foo) calc(96dpi + 96dpi))", |
| "image-set(url(\"foo\") calc(2dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetSubCalcResolutionUnitDpiFromX) { |
| TestImageSetParsing("image-set(url(foo) calc(2x - 96dpi))", |
| "image-set(url(\"foo\") calc(1dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetCalcResolutionUnitDppx) { |
| TestImageSetParsing("image-set(url(foo) calc(2dppx * 3))", |
| "image-set(url(\"foo\") calc(6dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetCalcResolutionUnitDpi) { |
| TestImageSetParsing("image-set(url(foo) calc(32dpi * 3))", |
| "image-set(url(\"foo\") calc(1dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetCalcResolutionUnitDpcm) { |
| TestImageSetParsing("image-set(url(foo) calc(1dpcm * 37.79532))", |
| "image-set(url(\"foo\") calc(1dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetCalcMaxInf) { |
| TestImageSetParsing("image-set(url(foo) calc(1 * max(INFinity * 3x, 0dpcm)))", |
| "image-set(url(\"foo\") calc(infinity * 1dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetCalcMinInf) { |
| TestImageSetParsing("image-set(url(foo) calc(1 * min(inFInity * 4x, 0dpi)))", |
| "image-set(url(\"foo\") calc(0dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetCalcMinMaxNan) { |
| TestImageSetParsing("image-set(url(foo) calc(1dppx * max(0, min(10, NaN))))", |
| "image-set(url(\"foo\") calc(NaN * 1dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetCalcClamp) { |
| TestImageSetParsing( |
| "image-set(url(foo) calc(1dppx * clamp(-Infinity, 0, infinity)))", |
| "image-set(url(\"foo\") calc(0dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetCalcClampLeft) { |
| TestImageSetParsing( |
| "image-set(url(foo) calc(1dppx * clamp(0, -Infinity, infinity)))", |
| "image-set(url(\"foo\") calc(0dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetCalcClampRight) { |
| TestImageSetParsing( |
| "image-set(url(foo) calc(1dppx * clamp(-Infinity, infinity, 0)))", |
| "image-set(url(\"foo\") calc(0dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetCalcClampNan) { |
| TestImageSetParsing( |
| "image-set(url(foo) calc(1 * clamp(-INFINITY*0dppx, 0dppx, " |
| "infiniTY*0dppx)))", |
| "image-set(url(\"foo\") calc(NaN * 1dppx))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetUrlFunction) { |
| TestImageSetParsing("image-set(url('foo') 1x)", "image-set(url(\"foo\") 1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetUrlFunctionEmptyStrUrl) { |
| TestImageSetParsing("image-set(url('') 1x)", "image-set(url(\"\") 1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetUrlFunctionNoQuotationMarks) { |
| TestImageSetParsing("image-set(url(foo) 1x)", "image-set(url(\"foo\") 1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetNoUrlFunction) { |
| TestImageSetParsing("image-set('foo' 1x)", "image-set(url(\"foo\") 1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetEmptyStrUrl) { |
| TestImageSetParsing("image-set('' 1x)", "image-set(url(\"\") 1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetLinearGradient) { |
| TestImageSetParsing("image-set(linear-gradient(red, blue) 1x)", |
| "image-set(linear-gradient(red, blue) 1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetRepeatingLinearGradient) { |
| TestImageSetParsing("image-set(repeating-linear-gradient(red, blue 25%) 1x)", |
| "image-set(repeating-linear-gradient(red, blue 25%) 1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetRadialGradient) { |
| TestImageSetParsing("image-set(radial-gradient(red, blue) 1x)", |
| "image-set(radial-gradient(red, blue) 1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetRepeatingRadialGradient) { |
| TestImageSetParsing("image-set(repeating-radial-gradient(red, blue 25%) 1x)", |
| "image-set(repeating-radial-gradient(red, blue 25%) 1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetConicGradient) { |
| TestImageSetParsing("image-set(conic-gradient(red, blue) 1x)", |
| "image-set(conic-gradient(red, blue) 1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetRepeatingConicGradient) { |
| TestImageSetParsing("image-set(repeating-conic-gradient(red, blue 25%) 1x)", |
| "image-set(repeating-conic-gradient(red, blue 25%) 1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetType) { |
| TestImageSetParsing("image-set(url('foo') 1x type('image/png'))", |
| "image-set(url(\"foo\") 1x type(\"image/png\"))"); |
| } |
| |
| void TestImageSetParsingFailure(const String& testValue) { |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kBackgroundImage, testValue, |
| StrictCSSParserContext(SecureContextMode::kSecureContext)); |
| ASSERT_EQ(value, nullptr); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetEmpty) { |
| TestImageSetParsingFailure("image-set()"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetMissingUrl) { |
| TestImageSetParsingFailure("image-set(1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetOnlyOneGradientColor) { |
| TestImageSetParsingFailure("image-set(linear-gradient(red) 1x)"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetAddCalcMissingUnit1) { |
| TestImageSetParsingFailure("image-set(url(foo) calc(2 + 3x))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetAddCalcMissingUnit2) { |
| TestImageSetParsingFailure("image-set(url(foo) calc(2x + 3))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetSubCalcMissingUnit1) { |
| TestImageSetParsingFailure("image-set(url(foo) calc(2 - 1x))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetSubCalcMissingUnit2) { |
| TestImageSetParsingFailure("image-set(url(foo) calc(2x - 1))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetMultCalcDoubleX) { |
| TestImageSetParsingFailure("image-set(url(foo) calc(2x * 3x))"); |
| } |
| |
| TEST(CSSPropertyParserTest, ImageSetDivCalcDoubleX) { |
| TestImageSetParsingFailure("image-set(url(foo) calc(6x / 3x))"); |
| } |
| |
| TEST(CSSPropertyParserTest, InternalLightDarkAuthor) { |
| auto* context = MakeGarbageCollected<CSSParserContext>( |
| kHTMLStandardMode, SecureContextMode::kInsecureContext); |
| // -internal-light-dark() is only valid in UA sheets. |
| ASSERT_FALSE(CSSParser::ParseSingleValue( |
| CSSPropertyID::kColor, "-internal-light-dark(#000000, #ffffff)", |
| context)); |
| ASSERT_FALSE(CSSParser::ParseSingleValue( |
| CSSPropertyID::kColor, "-internal-light-dark(red, green)", context)); |
| ASSERT_FALSE(CSSParser::ParseSingleValue( |
| CSSPropertyID::kBackgroundImage, |
| "-internal-light-dark(url(light.png), url(dark.png))", context)); |
| } |
| |
| TEST(CSSPropertyParserTest, UAInternalLightDarkColor) { |
| auto* ua_context = MakeGarbageCollected<CSSParserContext>( |
| kUASheetMode, SecureContextMode::kInsecureContext); |
| |
| const struct { |
| const char* value; |
| bool valid; |
| } tests[] = { |
| {"-internal-light-dark()", false}, |
| {"-internal-light-dark(#feedab)", false}, |
| {"-internal-light-dark(red blue)", false}, |
| {"-internal-light-dark(red,,blue)", false}, |
| {"-internal-light-dark(red, blue)", true}, |
| {"-internal-light-dark(#000000, #ffffff)", true}, |
| {"-internal-light-dark(rgb(0, 0, 0), hsl(180, 75%, 50%))", true}, |
| {"-internal-light-dark(rgba(0, 0, 0, 0.5), hsla(180, 75%, 50%, " |
| "0.7))", |
| true}, |
| }; |
| |
| for (const auto& test : tests) { |
| EXPECT_EQ(!!CSSParser::ParseSingleValue(CSSPropertyID::kColor, test.value, |
| ua_context), |
| test.valid); |
| } |
| } |
| |
| TEST(CSSPropertyParserTest, UAInternalLightDarkColorSerialization) { |
| auto* ua_context = MakeGarbageCollected<CSSParserContext>( |
| kUASheetMode, SecureContextMode::kInsecureContext); |
| const CSSValue* value = CSSParser::ParseSingleValue( |
| CSSPropertyID::kColor, "-internal-light-dark(red,#aaa)", ua_context); |
| ASSERT_TRUE(value); |
| EXPECT_EQ("-internal-light-dark(red, rgb(170, 170, 170))", value->CssText()); |
| } |
| |
| TEST(CSSPropertyParserTest, UAInternalLightDarkBackgroundImage) { |
| auto* ua_context = MakeGarbageCollected<CSSParserContext>( |
| kUASheetMode, SecureContextMode::kInsecureContext); |
| |
| const struct { |
| const char* value; |
| bool valid; |
| } tests[] = { |
| {"-internal-light-dark()", false}, |
| {"-internal-light-dark(url(light.png))", false}, |
| {"-internal-light-dark(url(light.png) url(dark.png))", false}, |
| {"-internal-light-dark(url(light.png),,url(dark.png))", false}, |
| {"-internal-light-dark(url(light.png), url(dark.png))", true}, |
| {"-internal-light-dark(url(light.png), none)", true}, |
| {"-internal-light-dark(none, -webkit-image-set(url(dark.png) 1x))", true}, |
| {"-internal-light-dark(none, image-set(url(dark.png) 1x))", true}, |
| {"-internal-light-dark( none , none )", true}, |
| {"-internal-light-dark( url(light.png) , url(dark.png) )", true}, |
| }; |
| |
| for (const auto& test : tests) { |
| EXPECT_EQ(!!CSSParser::ParseSingleValue(CSSPropertyID::kBackgroundImage, |
| test.value, ua_context), |
| test.valid) |
| << test.value; |
| } |
| } |
| |
| namespace { |
| |
| bool ParseCSSValue(CSSPropertyID property_id, |
| const String& value, |
| const CSSParserContext* context) { |
| CSSTokenizer tokenizer(value); |
| const auto tokens = tokenizer.TokenizeToEOF(); |
| const CSSParserTokenRange range(tokens); |
| HeapVector<CSSPropertyValue, 64> parsed_properties; |
| return CSSPropertyParser::ParseValue(property_id, false, {range, value}, |
| context, parsed_properties, |
| StyleRule::RuleType::kStyle); |
| } |
| |
| } // namespace |
| |
| TEST(CSSPropertyParserTest, UAInternalLightDarkBackgroundShorthand) { |
| auto* ua_context = MakeGarbageCollected<CSSParserContext>( |
| kUASheetMode, SecureContextMode::kInsecureContext); |
| |
| const struct { |
| const char* value; |
| bool valid; |
| } tests[] = { |
| {"-internal-light-dark()", false}, |
| {"-internal-light-dark(url(light.png))", false}, |
| {"-internal-light-dark(url(light.png) url(dark.png))", false}, |
| {"-internal-light-dark(url(light.png),,url(dark.png))", false}, |
| {"-internal-light-dark(url(light.png), url(dark.png))", true}, |
| {"-internal-light-dark(url(light.png), none)", true}, |
| {"-internal-light-dark(none, -webkit-image-set(url(dark.png) 1x))", true}, |
| {"-internal-light-dark(none, image-set(url(dark.png) 1x))", true}, |
| {"-internal-light-dark( none , none )", true}, |
| {"-internal-light-dark( url(light.png) , url(dark.png) )", true}, |
| }; |
| |
| for (const auto& test : tests) { |
| EXPECT_EQ( |
| !!ParseCSSValue(CSSPropertyID::kBackground, test.value, ua_context), |
| test.valid) |
| << test.value; |
| } |
| } |
| |
| TEST(CSSPropertyParserTest, ParseRevert) { |
| auto* context = MakeGarbageCollected<CSSParserContext>( |
| kHTMLStandardMode, SecureContextMode::kInsecureContext); |
| |
| String string = " revert"; |
| CSSTokenizer tokenizer(string); |
| const auto tokens = tokenizer.TokenizeToEOF(); |
| |
| const CSSValue* value = CSSPropertyParser::ParseSingleValue( |
| CSSPropertyID::kMarginLeft, CSSParserTokenRange(tokens), context); |
| ASSERT_TRUE(value); |
| EXPECT_TRUE(value->IsRevertValue()); |
| } |
| |
| TEST(CSSPropertyParserTest, ParseRevertLayer) { |
| auto* context = MakeGarbageCollected<CSSParserContext>( |
| kHTMLStandardMode, SecureContextMode::kInsecureContext); |
| |
| String string = " revert-layer"; |
| CSSTokenizer tokenizer(string); |
| const auto tokens = tokenizer.TokenizeToEOF(); |
| |
| const CSSValue* value = CSSPropertyParser::ParseSingleValue( |
| CSSPropertyID::kMarginLeft, CSSParserTokenRange(tokens), context); |
| ASSERT_TRUE(value); |
| EXPECT_TRUE(value->IsRevertLayerValue()); |
| } |
| |
| // anchor() and anchor-size() shouldn't parse when the feature is disabled. |
| TEST(CSSPropertyParserTest, AnchorPositioningDisabled) { |
| ScopedHTMLSelectMenuElementForTest select_menu_disabled(false); |
| ScopedCSSAnchorPositioningForTest anchor_positioning_disabled(false); |
| |
| auto* context = MakeGarbageCollected<CSSParserContext>( |
| kHTMLStandardMode, SecureContextMode::kInsecureContext); |
| |
| EXPECT_FALSE( |
| ParseCSSValue(CSSPropertyID::kTop, "anchor(--foo top)", context)); |
| EXPECT_FALSE( |
| ParseCSSValue(CSSPropertyID::kBottom, "anchor(--foo bottom)", context)); |
| EXPECT_FALSE(ParseCSSValue(CSSPropertyID::kWidth, "anchor-size(--foo width)", |
| context)); |
| EXPECT_FALSE(ParseCSSValue(CSSPropertyID::kHeight, |
| "anchor-size(--foo height)", context)); |
| } |
| |
| } // namespace blink |