| // Copyright 2015 The Cobalt Authors. All Rights Reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include <memory> | |
| #include "cobalt/cssom/selector_tree.h" | |
| #include "cobalt/base/version_compatibility.h" | |
| #include "cobalt/css_parser/parser.h" | |
| #include "cobalt/cssom/css_style_rule.h" | |
| #include "cobalt/cssom/specificity.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| namespace cobalt { | |
| namespace cssom { | |
| TEST(SelectorTreeTest, RootShouldHaveNoChildrenAfterInitialization) { | |
| SelectorTree selector_tree; | |
| EXPECT_TRUE( | |
| selector_tree.children(selector_tree.root_node(), kChildCombinator) | |
| .empty()); | |
| EXPECT_TRUE( | |
| selector_tree.children(selector_tree.root_node(), kDescendantCombinator) | |
| .empty()); | |
| EXPECT_TRUE( | |
| selector_tree.children(selector_tree.root_node(), kNextSiblingCombinator) | |
| .empty()); | |
| EXPECT_TRUE( | |
| selector_tree | |
| .children(selector_tree.root_node(), kFollowingSiblingCombinator) | |
| .empty()); | |
| } | |
| TEST(SelectorTreeTest, AppendRuleShouldTakeOneRule) { | |
| SelectorTree selector_tree; | |
| // Selector Tree: | |
| // root | |
| // kDescendantCombinator -> node_1("div") | |
| std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create(); | |
| scoped_refptr<CSSStyleRule> css_style_rule_1 = | |
| css_parser | |
| ->ParseRule("div {}", | |
| base::SourceLocation("[object SelectorTreeTest]", 1, 1)) | |
| ->AsCSSStyleRule(); | |
| selector_tree.AppendRule(css_style_rule_1); | |
| // Verify that ValidateVersionCompatibility does not report a usage error | |
| // when the minimum compatibility version is 1. | |
| base::VersionCompatibility::GetInstance()->SetMinimumVersion(1); | |
| EXPECT_TRUE(selector_tree.ValidateVersionCompatibility()); | |
| ASSERT_EQ(0, | |
| selector_tree.children(selector_tree.root_node(), kChildCombinator) | |
| .size()); | |
| ASSERT_EQ(1, selector_tree | |
| .children(selector_tree.root_node(), kDescendantCombinator) | |
| .size()); | |
| ASSERT_EQ(0, selector_tree | |
| .children(selector_tree.root_node(), kNextSiblingCombinator) | |
| .size()); | |
| ASSERT_EQ( | |
| 0, selector_tree | |
| .children(selector_tree.root_node(), kFollowingSiblingCombinator) | |
| .size()); | |
| const SelectorTree::Node* node_1 = | |
| selector_tree.children(selector_tree.root_node(), kDescendantCombinator) | |
| .begin() | |
| ->second; | |
| ASSERT_EQ(1, node_1->rules().size()); | |
| EXPECT_EQ(css_style_rule_1, node_1->rules()[0]); | |
| EXPECT_EQ(Specificity(0, 0, 1), node_1->cumulative_specificity()); | |
| } | |
| TEST(SelectorTreeTest, AppendRuleShouldNormalizeCompoundSelector) { | |
| SelectorTree selector_tree; | |
| // Selector Tree: | |
| // root | |
| // kDescendantCombinator -> node_1(".class#id") | |
| std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create(); | |
| scoped_refptr<CSSStyleRule> css_style_rule_1 = | |
| css_parser | |
| ->ParseRule(".class#id {}", | |
| base::SourceLocation("[object SelectorTreeTest]", 1, 1)) | |
| ->AsCSSStyleRule(); | |
| scoped_refptr<CSSStyleRule> css_style_rule_2 = | |
| css_parser | |
| ->ParseRule("#id.class {}", | |
| base::SourceLocation("[object SelectorTreeTest]", 1, 1)) | |
| ->AsCSSStyleRule(); | |
| selector_tree.AppendRule(css_style_rule_1); | |
| selector_tree.AppendRule(css_style_rule_2); | |
| // Verify that ValidateVersionCompatibility does not report a usage error | |
| // when the minimum compatibility version is 1. | |
| base::VersionCompatibility::GetInstance()->SetMinimumVersion(1); | |
| EXPECT_TRUE(selector_tree.ValidateVersionCompatibility()); | |
| ASSERT_EQ(0, | |
| selector_tree.children(selector_tree.root_node(), kChildCombinator) | |
| .size()); | |
| ASSERT_EQ(1, selector_tree | |
| .children(selector_tree.root_node(), kDescendantCombinator) | |
| .size()); | |
| ASSERT_EQ(0, selector_tree | |
| .children(selector_tree.root_node(), kNextSiblingCombinator) | |
| .size()); | |
| ASSERT_EQ( | |
| 0, selector_tree | |
| .children(selector_tree.root_node(), kFollowingSiblingCombinator) | |
| .size()); | |
| } | |
| TEST(SelectorTreeTest, AppendRuleSimpleShouldTakeTwoIdenticalRules) { | |
| SelectorTree selector_tree; | |
| // Selector Tree: | |
| // root | |
| // kDescendantCombinator -> node_1("div") | |
| std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create(); | |
| scoped_refptr<CSSStyleRule> css_style_rule_1 = | |
| css_parser | |
| ->ParseRule("div {}", | |
| base::SourceLocation("[object SelectorTreeTest]", 1, 1)) | |
| ->AsCSSStyleRule(); | |
| scoped_refptr<CSSStyleRule> css_style_rule_2 = | |
| css_parser | |
| ->ParseRule("div {}", | |
| base::SourceLocation("[object SelectorTreeTest]", 1, 1)) | |
| ->AsCSSStyleRule(); | |
| selector_tree.AppendRule(css_style_rule_1); | |
| selector_tree.AppendRule(css_style_rule_2); | |
| // Verify that ValidateVersionCompatibility does not report a usage error | |
| // when the minimum compatibility version is 1. | |
| base::VersionCompatibility::GetInstance()->SetMinimumVersion(1); | |
| EXPECT_TRUE(selector_tree.ValidateVersionCompatibility()); | |
| ASSERT_EQ(0, | |
| selector_tree.children(selector_tree.root_node(), kChildCombinator) | |
| .size()); | |
| ASSERT_EQ(1, selector_tree | |
| .children(selector_tree.root_node(), kDescendantCombinator) | |
| .size()); | |
| ASSERT_EQ(0, selector_tree | |
| .children(selector_tree.root_node(), kNextSiblingCombinator) | |
| .size()); | |
| ASSERT_EQ( | |
| 0, selector_tree | |
| .children(selector_tree.root_node(), kFollowingSiblingCombinator) | |
| .size()); | |
| const SelectorTree::Node* node_1 = | |
| selector_tree.children(selector_tree.root_node(), kDescendantCombinator) | |
| .begin() | |
| ->second; | |
| ASSERT_EQ(2, node_1->rules().size()); | |
| EXPECT_EQ(css_style_rule_1, node_1->rules()[0]); | |
| EXPECT_EQ(css_style_rule_2, node_1->rules()[1]); | |
| EXPECT_EQ(Specificity(0, 0, 1), node_1->cumulative_specificity()); | |
| } | |
| TEST(SelectorTreeTest, AppendRuleSimpleShouldTakeTwoDescendantSelectors) { | |
| SelectorTree selector_tree; | |
| // Selector Tree: | |
| // root | |
| // kNone -> node_1("div") | |
| // kChildCombinator -> node_2("span") | |
| std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create(); | |
| scoped_refptr<CSSStyleRule> css_style_rule_1 = | |
| css_parser | |
| ->ParseRule("div {}", | |
| base::SourceLocation("[object SelectorTreeTest]", 1, 1)) | |
| ->AsCSSStyleRule(); | |
| scoped_refptr<CSSStyleRule> css_style_rule_2 = | |
| css_parser | |
| ->ParseRule("div span {}", | |
| base::SourceLocation("[object SelectorTreeTest]", 1, 1)) | |
| ->AsCSSStyleRule(); | |
| selector_tree.AppendRule(css_style_rule_1); | |
| selector_tree.AppendRule(css_style_rule_2); | |
| // Verify that ValidateVersionCompatibility does not report a usage error | |
| // when the minimum compatibility version is 1. | |
| base::VersionCompatibility::GetInstance()->SetMinimumVersion(1); | |
| EXPECT_TRUE(selector_tree.ValidateVersionCompatibility()); | |
| ASSERT_EQ(0, | |
| selector_tree.children(selector_tree.root_node(), kChildCombinator) | |
| .size()); | |
| ASSERT_EQ(1, selector_tree | |
| .children(selector_tree.root_node(), kDescendantCombinator) | |
| .size()); | |
| ASSERT_EQ(0, selector_tree | |
| .children(selector_tree.root_node(), kNextSiblingCombinator) | |
| .size()); | |
| ASSERT_EQ( | |
| 0, selector_tree | |
| .children(selector_tree.root_node(), kFollowingSiblingCombinator) | |
| .size()); | |
| const SelectorTree::Node* node_1 = | |
| selector_tree.children(selector_tree.root_node(), kDescendantCombinator) | |
| .begin() | |
| ->second; | |
| ASSERT_EQ(1, node_1->rules().size()); | |
| EXPECT_EQ(css_style_rule_1, node_1->rules()[0]); | |
| EXPECT_EQ(Specificity(0, 0, 1), node_1->cumulative_specificity()); | |
| ASSERT_EQ(0, selector_tree.children(node_1, kChildCombinator).size()); | |
| ASSERT_EQ(1, selector_tree.children(node_1, kDescendantCombinator).size()); | |
| ASSERT_EQ(0, selector_tree.children(node_1, kNextSiblingCombinator).size()); | |
| ASSERT_EQ(0, | |
| selector_tree.children(node_1, kFollowingSiblingCombinator).size()); | |
| const SelectorTree::Node* node_2 = | |
| selector_tree.children(node_1, kDescendantCombinator).begin()->second; | |
| ASSERT_EQ(1, node_2->rules().size()); | |
| EXPECT_EQ(css_style_rule_2, node_2->rules()[0]); | |
| EXPECT_EQ(Specificity(0, 0, 2), node_2->cumulative_specificity()); | |
| } | |
| TEST(SelectorTreeTest, AppendRuleTwoDifferentNotSelectorsForSameElement) { | |
| SelectorTree selector_tree; | |
| // Selector Tree: | |
| // root | |
| // kDescendantCombinator -> node_1("body:not(.class-1)") | |
| // kDescendantCombinator -> node_2("body:not(.class-2)") | |
| std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create(); | |
| scoped_refptr<CSSStyleRule> css_style_rule_1 = | |
| css_parser | |
| ->ParseRule("body:not(.class-1) {}", | |
| base::SourceLocation("[object SelectorTreeTest]", 1, 1)) | |
| ->AsCSSStyleRule(); | |
| scoped_refptr<CSSStyleRule> css_style_rule_2 = | |
| css_parser | |
| ->ParseRule("body:not(.class-2) {}", | |
| base::SourceLocation("[object SelectorTreeTest]", 1, 1)) | |
| ->AsCSSStyleRule(); | |
| selector_tree.AppendRule(css_style_rule_1); | |
| selector_tree.AppendRule(css_style_rule_2); | |
| // Verify that ValidateVersionCompatibility does not report a usage error | |
| // when the minimum compatibility version is 16. | |
| base::VersionCompatibility::GetInstance()->SetMinimumVersion(16); | |
| EXPECT_TRUE(selector_tree.ValidateVersionCompatibility()); | |
| // Verify that ValidateVersionCompatibility reports a usage error when the | |
| // minimum compatibility version is 1. | |
| base::VersionCompatibility::GetInstance()->SetMinimumVersion(1); | |
| EXPECT_FALSE(selector_tree.ValidateVersionCompatibility()); | |
| ASSERT_EQ(0, | |
| selector_tree.children(selector_tree.root_node(), kChildCombinator) | |
| .size()); | |
| ASSERT_EQ(2, selector_tree | |
| .children(selector_tree.root_node(), kDescendantCombinator) | |
| .size()); | |
| ASSERT_EQ(0, selector_tree | |
| .children(selector_tree.root_node(), kNextSiblingCombinator) | |
| .size()); | |
| ASSERT_EQ( | |
| 0, selector_tree | |
| .children(selector_tree.root_node(), kFollowingSiblingCombinator) | |
| .size()); | |
| auto node_iter = | |
| selector_tree.children(selector_tree.root_node(), kDescendantCombinator) | |
| .begin(); | |
| const SelectorTree::Node* node_1 = node_iter->second; | |
| ASSERT_EQ(1, node_1->rules().size()); | |
| EXPECT_EQ(css_style_rule_1, node_1->rules()[0]); | |
| EXPECT_EQ(Specificity(0, 1, 1), node_1->cumulative_specificity()); | |
| const SelectorTree::Node* node_2 = (++node_iter)->second; | |
| ASSERT_EQ(1, node_2->rules().size()); | |
| EXPECT_EQ(css_style_rule_2, node_2->rules()[0]); | |
| EXPECT_EQ(Specificity(0, 1, 1), node_1->cumulative_specificity()); | |
| } | |
| TEST(SelectorTreeTest, AppendRuleTwoNotSelectorsForDifferentElements) { | |
| SelectorTree selector_tree; | |
| // Selector Tree: | |
| // root | |
| // kDescendantCombinator -> node_1("body:not(.class-1)") | |
| // kDescendantCombinator -> node_2("div:not(.class-1)") | |
| std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create(); | |
| scoped_refptr<CSSStyleRule> css_style_rule_1 = | |
| css_parser | |
| ->ParseRule("body:not(.class-1) {}", | |
| base::SourceLocation("[object SelectorTreeTest]", 1, 1)) | |
| ->AsCSSStyleRule(); | |
| scoped_refptr<CSSStyleRule> css_style_rule_2 = | |
| css_parser | |
| ->ParseRule("div:not(.class-1) {}", | |
| base::SourceLocation("[object SelectorTreeTest]", 1, 1)) | |
| ->AsCSSStyleRule(); | |
| selector_tree.AppendRule(css_style_rule_1); | |
| selector_tree.AppendRule(css_style_rule_2); | |
| // Verify that ValidateVersionCompatibility does not report a usage error | |
| // when the minimum compatibility version is 1. | |
| base::VersionCompatibility::GetInstance()->SetMinimumVersion(1); | |
| EXPECT_TRUE(selector_tree.ValidateVersionCompatibility()); | |
| ASSERT_EQ(0, | |
| selector_tree.children(selector_tree.root_node(), kChildCombinator) | |
| .size()); | |
| ASSERT_EQ(2, selector_tree | |
| .children(selector_tree.root_node(), kDescendantCombinator) | |
| .size()); | |
| ASSERT_EQ(0, selector_tree | |
| .children(selector_tree.root_node(), kNextSiblingCombinator) | |
| .size()); | |
| ASSERT_EQ( | |
| 0, selector_tree | |
| .children(selector_tree.root_node(), kFollowingSiblingCombinator) | |
| .size()); | |
| auto node_iter = | |
| selector_tree.children(selector_tree.root_node(), kDescendantCombinator) | |
| .begin(); | |
| const SelectorTree::Node* node_1 = node_iter->second; | |
| ASSERT_EQ(1, node_1->rules().size()); | |
| EXPECT_EQ(css_style_rule_1, node_1->rules()[0]); | |
| EXPECT_EQ(Specificity(0, 1, 1), node_1->cumulative_specificity()); | |
| const SelectorTree::Node* node_2 = (++node_iter)->second; | |
| ASSERT_EQ(1, node_2->rules().size()); | |
| EXPECT_EQ(css_style_rule_2, node_2->rules()[0]); | |
| EXPECT_EQ(Specificity(0, 1, 1), node_1->cumulative_specificity()); | |
| } | |
| TEST(SelectorTreeTest, AppendRuleTwoIdenticalNotSelectors) { | |
| SelectorTree selector_tree; | |
| // Selector Tree: | |
| // root | |
| // kDescendantCombinator -> node_1("body:not(.class-1") | |
| std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create(); | |
| scoped_refptr<CSSStyleRule> css_style_rule_1 = | |
| css_parser | |
| ->ParseRule("body:not(.class-1) {}", | |
| base::SourceLocation("[object SelectorTreeTest]", 1, 1)) | |
| ->AsCSSStyleRule(); | |
| scoped_refptr<CSSStyleRule> css_style_rule_2 = | |
| css_parser | |
| ->ParseRule("body:not(.class-1) {}", | |
| base::SourceLocation("[object SelectorTreeTest]", 1, 1)) | |
| ->AsCSSStyleRule(); | |
| selector_tree.AppendRule(css_style_rule_1); | |
| selector_tree.AppendRule(css_style_rule_2); | |
| // Verify that ValidateVersionCompatibility does not report a usage error | |
| // when the minimum compatibility version is 1. | |
| base::VersionCompatibility::GetInstance()->SetMinimumVersion(1); | |
| EXPECT_TRUE(selector_tree.ValidateVersionCompatibility()); | |
| ASSERT_EQ(0, | |
| selector_tree.children(selector_tree.root_node(), kChildCombinator) | |
| .size()); | |
| ASSERT_EQ(1, selector_tree | |
| .children(selector_tree.root_node(), kDescendantCombinator) | |
| .size()); | |
| ASSERT_EQ(0, selector_tree | |
| .children(selector_tree.root_node(), kNextSiblingCombinator) | |
| .size()); | |
| ASSERT_EQ( | |
| 0, selector_tree | |
| .children(selector_tree.root_node(), kFollowingSiblingCombinator) | |
| .size()); | |
| const SelectorTree::Node* node_1 = | |
| selector_tree.children(selector_tree.root_node(), kDescendantCombinator) | |
| .begin() | |
| ->second; | |
| ASSERT_EQ(2, node_1->rules().size()); | |
| EXPECT_EQ(css_style_rule_1, node_1->rules()[0]); | |
| EXPECT_EQ(css_style_rule_2, node_1->rules()[1]); | |
| EXPECT_EQ(Specificity(0, 1, 1), node_1->cumulative_specificity()); | |
| } | |
| } // namespace cssom | |
| } // namespace cobalt |