blob: 65902459c7a78fc22b4c2e097bfd333d0fd5f8ac [file] [log] [blame]
// Copyright 2018 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/resolver/css_variable_resolver.h"
#include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
#include "third_party/blink/renderer/core/css/css_variable_reference_value.h"
#include "third_party/blink/renderer/core/css/document_style_environment_variables.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
#include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
#include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
#include "third_party/blink/renderer/core/css/properties/css_property.h"
#include "third_party/blink/renderer/core/css/properties/longhand.h"
#include "third_party/blink/renderer/core/css/property_registration.h"
#include "third_party/blink/renderer/core/css/property_registry.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
namespace blink {
namespace {
// blue
static const Color kFallbackTestColor = Color(0, 0, 255);
// black
static const Color kMainBgTestColor = Color(0, 0, 0);
// red
static const Color kTestColor = Color(255, 0, 0);
} // namespace
class CSSVariableResolverTest : public PageTestBase {
public:
void SetUp() override {
PageTestBase::SetUp();
RuntimeEnabledFeatures::SetCSSEnvironmentVariablesEnabled(true);
GetStyleEngine().EnsureEnvironmentVariables().SetVariable("test", "red");
}
void SetTestHTML(const String& value) {
GetDocument().body()->SetInnerHTMLFromString(
"<style>"
" #target {"
" --main-bg-color: black;"
" --test: red;"
" background-color: " +
value +
" }"
"</style>"
"<div>"
" <div id=target></div>"
"</div>");
GetDocument().View()->UpdateAllLifecyclePhases();
}
const CSSCustomPropertyDeclaration* CreateCustomProperty(
const String& value) {
return CreateCustomProperty("--unused", value);
}
const CSSCustomPropertyDeclaration* CreateCustomProperty(
const AtomicString& name,
const String& value) {
const auto tokens = CSSTokenizer(value).TokenizeToEOF();
return CSSVariableParser::ParseDeclarationValue(
name, tokens, false, *CSSParserContext::Create(GetDocument()));
}
};
TEST_F(CSSVariableResolverTest, ParseEnvVariable_Missing_NestedVar) {
SetTestHTML("env(missing, var(--main-bg-color))");
// Check that the element has the background color provided by the
// nested variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kMainBgTestColor, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(CSSVariableResolverTest, ParseEnvVariable_Missing_NestedVar_Fallback) {
SetTestHTML("env(missing, var(--missing, blue))");
// Check that the element has the fallback background color.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kFallbackTestColor,
target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(CSSVariableResolverTest, ParseEnvVariable_Missing_WithFallback) {
SetTestHTML("env(missing, blue)");
// Check that the element has the fallback background color.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kFallbackTestColor,
target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(CSSVariableResolverTest, ParseEnvVariable_Valid) {
SetTestHTML("env(test)");
// Check that the element has the background color provided by the variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kTestColor, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(CSSVariableResolverTest, ParseEnvVariable_Valid_WithFallback) {
SetTestHTML("env(test, blue)");
// Check that the element has the background color provided by the variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kTestColor, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(CSSVariableResolverTest, ParseEnvVariable_WhenNested) {
SetTestHTML("var(--main-bg-color, env(missing))");
// Check that the element has the background color provided by var().
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kMainBgTestColor, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(CSSVariableResolverTest, ParseEnvVariable_WhenNested_WillFallback) {
SetTestHTML("var(--missing, env(test))");
// Check that the element has the background color provided by the variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kTestColor, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(CSSVariableResolverTest, NoResolutionWithoutVar) {
scoped_refptr<StyleInheritedVariables> inherited_variables =
StyleInheritedVariables::Create();
std::unique_ptr<StyleNonInheritedVariables> non_inherited_variables =
StyleNonInheritedVariables::Create();
EXPECT_FALSE(inherited_variables->NeedsResolution());
EXPECT_FALSE(non_inherited_variables->NeedsResolution());
const auto* prop = CreateCustomProperty("#fefefe");
inherited_variables->SetVariable("--prop", prop->Value());
non_inherited_variables->SetVariable("--prop", prop->Value());
EXPECT_FALSE(inherited_variables->NeedsResolution());
EXPECT_FALSE(non_inherited_variables->NeedsResolution());
}
TEST_F(CSSVariableResolverTest, VarNeedsResolution) {
scoped_refptr<StyleInheritedVariables> inherited_variables =
StyleInheritedVariables::Create();
std::unique_ptr<StyleNonInheritedVariables> non_inherited_variables =
StyleNonInheritedVariables::Create();
EXPECT_FALSE(inherited_variables->NeedsResolution());
EXPECT_FALSE(non_inherited_variables->NeedsResolution());
const auto* prop1 = CreateCustomProperty("var(--prop2)");
const auto* prop2 = CreateCustomProperty("#fefefe");
inherited_variables->SetVariable("--prop1", prop1->Value());
non_inherited_variables->SetVariable("--prop1", prop1->Value());
EXPECT_TRUE(inherited_variables->NeedsResolution());
EXPECT_TRUE(non_inherited_variables->NeedsResolution());
// While NeedsResolution() == true, add some properties without
// var()-references.
inherited_variables->SetVariable("--prop2", prop2->Value());
non_inherited_variables->SetVariable("--prop2", prop2->Value());
// We should still need resolution even after adding properties that don't
// have var-references.
EXPECT_TRUE(inherited_variables->NeedsResolution());
EXPECT_TRUE(non_inherited_variables->NeedsResolution());
inherited_variables->ClearNeedsResolution();
non_inherited_variables->ClearNeedsResolution();
EXPECT_FALSE(inherited_variables->NeedsResolution());
EXPECT_FALSE(non_inherited_variables->NeedsResolution());
}
TEST_F(CSSVariableResolverTest, UrlNeedsResolution) {
scoped_refptr<StyleInheritedVariables> inherited_variables =
StyleInheritedVariables::Create();
std::unique_ptr<StyleNonInheritedVariables> non_inherited_variables =
StyleNonInheritedVariables::Create();
EXPECT_FALSE(inherited_variables->NeedsResolution());
EXPECT_FALSE(non_inherited_variables->NeedsResolution());
const auto* prop = CreateCustomProperty("url(a)");
inherited_variables->SetVariable("--prop", prop->Value());
non_inherited_variables->SetVariable("--prop", prop->Value());
EXPECT_TRUE(inherited_variables->NeedsResolution());
EXPECT_TRUE(non_inherited_variables->NeedsResolution());
}
TEST_F(CSSVariableResolverTest, CopiedVariablesRetainNeedsResolution) {
scoped_refptr<StyleInheritedVariables> inherited_variables =
StyleInheritedVariables::Create();
std::unique_ptr<StyleNonInheritedVariables> non_inherited_variables =
StyleNonInheritedVariables::Create();
const auto* prop = CreateCustomProperty("var(--x)");
inherited_variables->SetVariable("--prop", prop->Value());
non_inherited_variables->SetVariable("--prop", prop->Value());
EXPECT_TRUE(inherited_variables->NeedsResolution());
EXPECT_TRUE(non_inherited_variables->NeedsResolution());
EXPECT_TRUE(inherited_variables->Copy()->NeedsResolution());
EXPECT_TRUE(non_inherited_variables->Clone()->NeedsResolution());
inherited_variables->ClearNeedsResolution();
non_inherited_variables->ClearNeedsResolution();
EXPECT_FALSE(inherited_variables->NeedsResolution());
EXPECT_FALSE(non_inherited_variables->NeedsResolution());
EXPECT_FALSE(inherited_variables->Copy()->NeedsResolution());
EXPECT_FALSE(non_inherited_variables->Clone()->NeedsResolution());
}
TEST_F(CSSVariableResolverTest, NeedsResolutionClearedByResolver) {
const ComputedStyle* initial = &ComputedStyle::InitialStyle();
StyleResolverState state(GetDocument(), nullptr, initial, initial);
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
style->InheritFrom(*initial);
state.SetStyle(std::move(style));
const auto* prop1 = CreateCustomProperty("--prop1", "var(--prop2)");
const auto* prop2 = CreateCustomProperty("--prop2", "#fefefe");
const auto* prop3 = CreateCustomProperty("--prop3", "var(--prop2)");
// Register prop3 to make it non-inherited.
CSSSyntaxDescriptor token_syntax("*");
String initial_value_str("foo");
const auto tokens = CSSTokenizer(initial_value_str).TokenizeToEOF();
const CSSParserContext* context = CSSParserContext::Create(GetDocument());
const CSSValue* initial_value =
token_syntax.Parse(CSSParserTokenRange(tokens), context, false);
ASSERT_TRUE(initial_value);
ASSERT_TRUE(initial_value->IsVariableReferenceValue());
PropertyRegistration* registration = new PropertyRegistration(
"--prop3", token_syntax, false, initial_value,
ToCSSVariableReferenceValue(*initial_value).VariableDataValue());
ASSERT_TRUE(GetDocument().GetPropertyRegistry());
GetDocument().GetPropertyRegistry()->RegisterProperty("--prop3",
*registration);
ToLonghand(GetCSSPropertyVariable()).ApplyValue(state, *prop1);
ToLonghand(GetCSSPropertyVariable()).ApplyValue(state, *prop2);
ToLonghand(GetCSSPropertyVariable()).ApplyValue(state, *prop3);
EXPECT_TRUE(state.Style()->InheritedVariables()->NeedsResolution());
EXPECT_TRUE(state.Style()->NonInheritedVariables()->NeedsResolution());
CSSVariableResolver(state).ResolveVariableDefinitions();
EXPECT_FALSE(state.Style()->InheritedVariables()->NeedsResolution());
EXPECT_FALSE(state.Style()->NonInheritedVariables()->NeedsResolution());
}
} // namespace blink