| // 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, AppendRuleSimpleShouldTakeTwoDesendantSelectors) { |
| 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 |