blob: 777fabc120b689a8cb60eb58e60e564173ded2a6 [file] [log] [blame]
// Copyright 2014 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/css_test_helpers.h"
#include "third_party/blink/renderer/core/css/parser/css_parser.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
#include "third_party/blink/renderer/core/css/rule_set.h"
#include "testing/gtest/include/gtest/gtest.h"
#include <iostream>
namespace blink {
namespace {
unsigned Specificity(const String& selector_text) {
CSSSelectorList selector_list =
css_test_helpers::ParseSelectorList(selector_text);
return selector_list.First()->Specificity();
}
bool HasLinkOrVisited(const String& selector_text) {
CSSSelectorList selector_list =
css_test_helpers::ParseSelectorList(selector_text);
return selector_list.First()->HasLinkOrVisited();
}
} // namespace
TEST(CSSSelector, Representations) {
css_test_helpers::TestStyleSheet sheet;
const char* css_rules =
"summary::-webkit-details-marker { }"
"* {}"
"div {}"
"#id {}"
".class {}"
"[attr] {}"
"div:hover {}"
"div:nth-child(2){}"
".class#id { }"
"#id.class { }"
"[attr]#id { }"
"div[attr]#id { }"
"div::first-line { }"
".a.b.c { }"
"div:not(.a) { }" // without class a
"div:not(:visited) { }" // without the visited pseudo class
"[attr=\"value\"] { }" // Exact equality
"[attr~=\"value\"] { }" // One of a space-separated list
"[attr^=\"value\"] { }" // Begins with
"[attr$=\"value\"] { }" // Ends with
"[attr*=\"value\"] { }" // Substring equal to
"[attr|=\"value\"] { }" // One of a hyphen-separated list
".a .b { }" // .b is a descendant of .a
".a > .b { }" // .b is a direct descendant of .a
".a ~ .b { }" // .a precedes .b in sibling order
".a + .b { }" // .a element immediately precedes .b in sibling order
".a, .b { }" // matches .a or .b
".a.b .c {}";
sheet.AddCSSRules(css_rules);
EXPECT_EQ(29u,
sheet.GetRuleSet().RuleCount()); // .a, .b counts as two rules.
#ifndef NDEBUG
sheet.GetRuleSet().Show();
#endif
}
TEST(CSSSelector, OverflowRareDataMatchNth) {
int max_int = std::numeric_limits<int>::max();
int min_int = std::numeric_limits<int>::min();
CSSSelector selector;
// Overflow count - b (max_int - -1 = max_int + 1)
selector.SetNth(1, -1);
EXPECT_FALSE(selector.MatchNth(max_int));
// 0 - (min_int) = max_int + 1
selector.SetNth(1, min_int);
EXPECT_FALSE(selector.MatchNth(0));
// min_int - 1
selector.SetNth(-1, min_int);
EXPECT_FALSE(selector.MatchNth(1));
// a shouldn't negate to itself (and min_int negates to itself).
// Note: This test can only fail when using ubsan.
selector.SetNth(min_int, 10);
EXPECT_FALSE(selector.MatchNth(2));
}
TEST(CSSSelector, Specificity_Is) {
EXPECT_EQ(Specificity(".a :is(.b, div.c)"), Specificity(".a div.c"));
EXPECT_EQ(Specificity(".a :is(.c#d, .e)"), Specificity(".a .c#d"));
EXPECT_EQ(Specificity(":is(.e+.f, .g>.b, .h)"), Specificity(".e+.f"));
EXPECT_EQ(Specificity(".a :is(.e+.f, .g>.b, .h#i)"), Specificity(".a .h#i"));
EXPECT_EQ(Specificity(".a+:is(.b+span.f, :is(.c>.e, .g))"),
Specificity(".a+.b+span.f"));
EXPECT_EQ(Specificity("div > :is(div:where(span:where(.b ~ .c)))"),
Specificity("div > div"));
EXPECT_EQ(Specificity(":is(.c + .c + .c, .b + .c:not(span), .b + .c + .e)"),
Specificity(".c + .c + .c"));
}
TEST(CSSSelector, Specificity_Where) {
EXPECT_EQ(Specificity(".a :where(.b, div.c)"), Specificity(".a"));
EXPECT_EQ(Specificity(".a :where(.c#d, .e)"), Specificity(".a"));
EXPECT_EQ(Specificity(":where(.e+.f, .g>.b, .h)"), Specificity("*"));
EXPECT_EQ(Specificity(".a :where(.e+.f, .g>.b, .h#i)"), Specificity(".a"));
EXPECT_EQ(Specificity("div > :where(.b+span.f, :where(.c>.e, .g))"),
Specificity("div"));
EXPECT_EQ(Specificity("div > :where(div:is(span:is(.b ~ .c)))"),
Specificity("div"));
EXPECT_EQ(
Specificity(":where(.c + .c + .c, .b + .c:not(span), .b + .c + .e)"),
Specificity("*"));
}
TEST(CSSSelector, Specificity_Slotted) {
EXPECT_EQ(Specificity("::slotted(.a)"), Specificity(".a::first-line"));
EXPECT_EQ(Specificity("::slotted(*)"), Specificity("::first-line"));
}
TEST(CSSSelector, Specificity_Not) {
EXPECT_EQ(Specificity(":not(div)"), Specificity(":is(div)"));
EXPECT_EQ(Specificity(":not(.a)"), Specificity(":is(.a)"));
EXPECT_EQ(Specificity(":not(div.a)"), Specificity(":is(div.a)"));
EXPECT_EQ(Specificity(".a :not(.b, div.c)"),
Specificity(".a :is(.b, div.c)"));
EXPECT_EQ(Specificity(".a :not(.c#d, .e)"), Specificity(".a :is(.c#d, .e)"));
EXPECT_EQ(Specificity(".a :not(.e+.f, .g>.b, .h#i)"),
Specificity(".a :is(.e+.f, .g>.b, .h#i)"));
EXPECT_EQ(Specificity(":not(.c + .c + .c, .b + .c:not(span), .b + .c + .e)"),
Specificity(":is(.c + .c + .c, .b + .c:not(span), .b + .c + .e)"));
}
TEST(CSSSelector, HasLinkOrVisited) {
EXPECT_FALSE(HasLinkOrVisited("tag"));
EXPECT_FALSE(HasLinkOrVisited("visited"));
EXPECT_FALSE(HasLinkOrVisited("link"));
EXPECT_FALSE(HasLinkOrVisited(".a"));
EXPECT_FALSE(HasLinkOrVisited("#a:is(visited)"));
EXPECT_FALSE(HasLinkOrVisited(":not(link):hover"));
EXPECT_FALSE(HasLinkOrVisited(":hover"));
EXPECT_FALSE(HasLinkOrVisited(":is(:hover)"));
EXPECT_FALSE(HasLinkOrVisited(":not(:is(:hover))"));
EXPECT_TRUE(HasLinkOrVisited(":visited"));
EXPECT_TRUE(HasLinkOrVisited(":link"));
EXPECT_TRUE(HasLinkOrVisited(":visited:link"));
EXPECT_TRUE(HasLinkOrVisited(":not(:visited)"));
EXPECT_TRUE(HasLinkOrVisited(":not(:link)"));
EXPECT_TRUE(HasLinkOrVisited(":not(:is(:link))"));
EXPECT_TRUE(HasLinkOrVisited(":is(:link)"));
EXPECT_TRUE(HasLinkOrVisited(":is(.a, .b, :is(:visited))"));
EXPECT_TRUE(HasLinkOrVisited("::cue(:visited)"));
EXPECT_TRUE(HasLinkOrVisited("::cue(:link)"));
EXPECT_TRUE(HasLinkOrVisited(":host(:link)"));
EXPECT_TRUE(HasLinkOrVisited(":host-context(:link)"));
}
TEST(CSSSelector, CueDefaultNamespace) {
css_test_helpers::TestStyleSheet sheet;
sheet.AddCSSRules(R"HTML(
@namespace "http://www.w3.org/1999/xhtml";
video::cue(b) {}
)HTML");
const CSSSelector& cue_selector =
(*sheet.GetRuleSet().CuePseudoRules())[0]->Selector();
EXPECT_EQ(cue_selector.GetPseudoType(), CSSSelector::kPseudoCue);
const CSSSelectorList* cue_arguments = cue_selector.SelectorList();
ASSERT_TRUE(cue_arguments);
const CSSSelector* vtt_type_selector = cue_arguments->First();
ASSERT_TRUE(vtt_type_selector);
EXPECT_EQ(vtt_type_selector->TagQName().LocalName(), "b");
// Default namespace should not affect VTT node type selector.
EXPECT_EQ(vtt_type_selector->TagQName().NamespaceURI(), g_star_atom);
}
} // namespace blink