blob: 682bfe979af20f12560d5c909ce91be44167705a [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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_parser_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/css/css_test_helpers.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_observer.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.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/dom/document.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
namespace blink {
class TestCSSParserObserver : public CSSParserObserver {
public:
void StartRuleHeader(StyleRule::RuleType rule_type,
unsigned offset) override {
rule_type_ = rule_type;
rule_header_start_ = offset;
}
void EndRuleHeader(unsigned offset) override { rule_header_end_ = offset; }
void ObserveSelector(unsigned start_offset, unsigned end_offset) override {}
void StartRuleBody(unsigned offset) override { rule_body_start_ = offset; }
void EndRuleBody(unsigned offset) override { rule_body_end_ = offset; }
void ObserveProperty(unsigned start_offset,
unsigned end_offset,
bool is_important,
bool is_parsed) override {}
void ObserveComment(unsigned start_offset, unsigned end_offset) override {}
StyleRule::RuleType rule_type_ = StyleRule::RuleType::kStyle;
unsigned rule_header_start_ = 0;
unsigned rule_header_end_ = 0;
unsigned rule_body_start_ = 0;
unsigned rule_body_end_ = 0;
};
TEST(CSSParserImplTest, AtImportOffsets) {
String sheet_text = "@import 'test.css';";
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
TestCSSParserObserver test_css_parser_observer;
CSSParserImpl::ParseStyleSheetForInspector(sheet_text, context, style_sheet,
test_css_parser_observer);
EXPECT_EQ(style_sheet->ImportRules().size(), 1u);
EXPECT_EQ(test_css_parser_observer.rule_type_, StyleRule::RuleType::kImport);
EXPECT_EQ(test_css_parser_observer.rule_header_start_, 18u);
EXPECT_EQ(test_css_parser_observer.rule_header_end_, 18u);
EXPECT_EQ(test_css_parser_observer.rule_body_start_, 18u);
EXPECT_EQ(test_css_parser_observer.rule_body_end_, 18u);
}
TEST(CSSParserImplTest, AtMediaOffsets) {
String sheet_text = "@media screen { }";
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
TestCSSParserObserver test_css_parser_observer;
CSSParserImpl::ParseStyleSheetForInspector(sheet_text, context, style_sheet,
test_css_parser_observer);
EXPECT_EQ(style_sheet->ChildRules().size(), 1u);
EXPECT_EQ(test_css_parser_observer.rule_type_, StyleRule::RuleType::kMedia);
EXPECT_EQ(test_css_parser_observer.rule_header_start_, 7u);
EXPECT_EQ(test_css_parser_observer.rule_header_end_, 14u);
EXPECT_EQ(test_css_parser_observer.rule_body_start_, 15u);
EXPECT_EQ(test_css_parser_observer.rule_body_end_, 16u);
}
TEST(CSSParserImplTest, AtSupportsOffsets) {
String sheet_text = "@supports (display:none) { }";
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
TestCSSParserObserver test_css_parser_observer;
CSSParserImpl::ParseStyleSheetForInspector(sheet_text, context, style_sheet,
test_css_parser_observer);
EXPECT_EQ(style_sheet->ChildRules().size(), 1u);
EXPECT_EQ(test_css_parser_observer.rule_type_,
StyleRule::RuleType::kSupports);
EXPECT_EQ(test_css_parser_observer.rule_header_start_, 10u);
EXPECT_EQ(test_css_parser_observer.rule_header_end_, 25u);
EXPECT_EQ(test_css_parser_observer.rule_body_start_, 26u);
EXPECT_EQ(test_css_parser_observer.rule_body_end_, 27u);
}
TEST(CSSParserImplTest, AtViewportOffsets) {
String sheet_text = "@viewport { }";
auto* context = MakeGarbageCollected<CSSParserContext>(
kUASheetMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
TestCSSParserObserver test_css_parser_observer;
CSSParserImpl::ParseStyleSheetForInspector(sheet_text, context, style_sheet,
test_css_parser_observer);
EXPECT_EQ(style_sheet->ChildRules().size(), 1u);
EXPECT_EQ(test_css_parser_observer.rule_type_,
StyleRule::RuleType::kViewport);
EXPECT_EQ(test_css_parser_observer.rule_header_start_, 10u);
EXPECT_EQ(test_css_parser_observer.rule_header_end_, 10u);
EXPECT_EQ(test_css_parser_observer.rule_body_start_, 10u);
EXPECT_EQ(test_css_parser_observer.rule_body_end_, 10u);
}
TEST(CSSParserImplTest, AtFontFaceOffsets) {
String sheet_text = "@font-face { }";
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
TestCSSParserObserver test_css_parser_observer;
CSSParserImpl::ParseStyleSheetForInspector(sheet_text, context, style_sheet,
test_css_parser_observer);
EXPECT_EQ(style_sheet->ChildRules().size(), 1u);
EXPECT_EQ(test_css_parser_observer.rule_type_,
StyleRule::RuleType::kFontFace);
EXPECT_EQ(test_css_parser_observer.rule_header_start_, 11u);
EXPECT_EQ(test_css_parser_observer.rule_header_end_, 11u);
EXPECT_EQ(test_css_parser_observer.rule_body_start_, 11u);
EXPECT_EQ(test_css_parser_observer.rule_body_end_, 11u);
}
TEST(CSSParserImplTest, AtKeyframesOffsets) {
String sheet_text = "@keyframes test { }";
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
TestCSSParserObserver test_css_parser_observer;
CSSParserImpl::ParseStyleSheetForInspector(sheet_text, context, style_sheet,
test_css_parser_observer);
EXPECT_EQ(style_sheet->ChildRules().size(), 1u);
EXPECT_EQ(test_css_parser_observer.rule_type_,
StyleRule::RuleType::kKeyframes);
EXPECT_EQ(test_css_parser_observer.rule_header_start_, 11u);
EXPECT_EQ(test_css_parser_observer.rule_header_end_, 16u);
EXPECT_EQ(test_css_parser_observer.rule_body_start_, 17u);
EXPECT_EQ(test_css_parser_observer.rule_body_end_, 18u);
}
TEST(CSSParserImplTest, AtPageOffsets) {
String sheet_text = "@page :first { }";
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
TestCSSParserObserver test_css_parser_observer;
CSSParserImpl::ParseStyleSheetForInspector(sheet_text, context, style_sheet,
test_css_parser_observer);
EXPECT_EQ(style_sheet->ChildRules().size(), 1u);
EXPECT_EQ(test_css_parser_observer.rule_type_, StyleRule::RuleType::kPage);
EXPECT_EQ(test_css_parser_observer.rule_header_start_, 6u);
EXPECT_EQ(test_css_parser_observer.rule_header_end_, 13u);
EXPECT_EQ(test_css_parser_observer.rule_body_start_, 14u);
EXPECT_EQ(test_css_parser_observer.rule_body_end_, 15u);
}
TEST(CSSParserImplTest, AtPropertyOffsets) {
ScopedCSSVariables2AtPropertyForTest scoped_feature(true);
String sheet_text = "@property --test { }";
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
TestCSSParserObserver test_css_parser_observer;
CSSParserImpl::ParseStyleSheetForInspector(sheet_text, context, style_sheet,
test_css_parser_observer);
EXPECT_EQ(style_sheet->ChildRules().size(), 1u);
EXPECT_EQ(test_css_parser_observer.rule_type_,
StyleRule::RuleType::kProperty);
EXPECT_EQ(test_css_parser_observer.rule_header_start_, 10u);
EXPECT_EQ(test_css_parser_observer.rule_header_end_, 17u);
EXPECT_EQ(test_css_parser_observer.rule_body_start_, 18u);
EXPECT_EQ(test_css_parser_observer.rule_body_end_, 19u);
}
TEST(CSSParserImplTest, AtScrollTimelineOffsets) {
ScopedCSSScrollTimelineForTest scoped_feature(true);
String sheet_text = "@scroll-timeline test { }";
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
TestCSSParserObserver test_css_parser_observer;
CSSParserImpl::ParseStyleSheetForInspector(sheet_text, context, style_sheet,
test_css_parser_observer);
EXPECT_EQ(style_sheet->ChildRules().size(), 1u);
EXPECT_EQ(test_css_parser_observer.rule_type_,
StyleRule::RuleType::kScrollTimeline);
EXPECT_EQ(test_css_parser_observer.rule_header_start_, 17u);
EXPECT_EQ(test_css_parser_observer.rule_header_end_, 22u);
EXPECT_EQ(test_css_parser_observer.rule_body_start_, 23u);
EXPECT_EQ(test_css_parser_observer.rule_body_end_, 24u);
}
TEST(CSSParserImplTest, AtCounterStyleOffsets) {
ScopedCSSAtRuleCounterStyleForTest scoped_feature(true);
String sheet_text = "@counter-style test { }";
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
TestCSSParserObserver test_css_parser_observer;
CSSParserImpl::ParseStyleSheetForInspector(sheet_text, context, style_sheet,
test_css_parser_observer);
EXPECT_EQ(style_sheet->ChildRules().size(), 1u);
EXPECT_EQ(test_css_parser_observer.rule_type_,
StyleRule::RuleType::kCounterStyle);
EXPECT_EQ(test_css_parser_observer.rule_header_start_, 15u);
EXPECT_EQ(test_css_parser_observer.rule_header_end_, 20u);
EXPECT_EQ(test_css_parser_observer.rule_body_start_, 21u);
EXPECT_EQ(test_css_parser_observer.rule_body_end_, 22u);
}
TEST(CSSParserImplTest, AtCounterStyleDisabled) {
ScopedCSSAtRuleCounterStyleForTest scoped_feature(false);
String sheet_text = "@counter-style test { }";
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
TestCSSParserObserver test_css_parser_observer;
CSSParserImpl::ParseStyleSheetForInspector(sheet_text, context, style_sheet,
test_css_parser_observer);
EXPECT_EQ(style_sheet->ChildRules().size(), 0u);
}
TEST(CSSParserImplTest, AtContainerOffsets) {
ScopedCSSContainerQueriesForTest scoped_feature(true);
String sheet_text = "@container (max-width: 100px) { }";
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
TestCSSParserObserver test_css_parser_observer;
CSSParserImpl::ParseStyleSheetForInspector(sheet_text, context, style_sheet,
test_css_parser_observer);
EXPECT_EQ(style_sheet->ChildRules().size(), 1u);
EXPECT_EQ(test_css_parser_observer.rule_type_,
StyleRule::RuleType::kContainer);
EXPECT_EQ(test_css_parser_observer.rule_header_start_, 11u);
EXPECT_EQ(test_css_parser_observer.rule_header_end_, 30u);
EXPECT_EQ(test_css_parser_observer.rule_body_start_, 31u);
EXPECT_EQ(test_css_parser_observer.rule_body_end_, 32u);
}
TEST(CSSParserImplTest, AtContainerDisabled) {
String rule = "@container (max-width: 100px) { }";
{
ScopedCSSContainerQueriesForTest scoped_feature(true);
Document* document = Document::CreateForTest();
EXPECT_TRUE(css_test_helpers::ParseRule(*document, rule));
}
{
ScopedCSSContainerQueriesForTest scoped_feature(false);
Document* document = Document::CreateForTest();
EXPECT_FALSE(css_test_helpers::ParseRule(*document, rule));
}
}
TEST(CSSParserImplTest, RemoveImportantAnnotationIfPresent) {
struct TestCase {
String input;
String expected_text;
bool expected_is_important;
};
static const TestCase test_cases[] = {
{"", "", false},
{"!important", "", true},
{" !important", " ", true},
{"!", "!", false},
{"1px", "1px", false},
{"2px!important", "2px", true},
{"3px !important", "3px ", true},
{"4px ! important", "4px ", true},
{"5px !important ", "5px ", true},
{"6px !!important", "6px !", true},
{"7px !important !important", "7px !important ", true},
{"8px important", "8px important", false},
};
for (auto current_case : test_cases) {
CSSTokenizer tokenizer(current_case.input);
CSSParserTokenStream stream(tokenizer);
CSSTokenizedValue tokenized_value = CSSParserImpl::ConsumeValue(stream);
SCOPED_TRACE(current_case.input);
bool is_important =
CSSParserImpl::RemoveImportantAnnotationIfPresent(tokenized_value);
EXPECT_EQ(is_important, current_case.expected_is_important);
EXPECT_EQ(tokenized_value.text.ToString(), current_case.expected_text);
}
}
} // namespace blink