blob: ec5b1bed86b12bc88c83334ec7e07850d10384f5 [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/properties/longhands/custom_property.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
#include "third_party/blink/renderer/core/css/css_primitive_value.h"
#include "third_party/blink/renderer/core/css/css_test_helpers.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_local_context.h"
#include "third_party/blink/renderer/core/css/parser/css_tokenizer.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/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
namespace blink {
using css_test_helpers::RegisterProperty;
using VariableMode = CSSParserLocalContext::VariableMode;
namespace {
class CustomPropertyTest : public PageTestBase {
public:
void SetElementWithStyle(const String& value) {
GetDocument().body()->setInnerHTML("<div id='target' style='" + value +
"'></div>");
UpdateAllLifecyclePhasesForTest();
}
const ComputedStyle& GetComputedStyle() {
Element* node = GetDocument().getElementById("target");
return node->ComputedStyleRef();
}
const CSSValue* GetComputedValue(const CustomProperty& property) {
return property.CSSValueFromComputedStyle(GetComputedStyle(),
nullptr /* layout_object */,
false /* allow_visited_style */);
}
const CSSValue* ParseValue(const Longhand& property,
const String& value,
const CSSParserLocalContext& local_context) {
CSSTokenizer tokenizer(value);
const auto tokens = tokenizer.TokenizeToEOF();
CSSParserTokenRange range(tokens);
auto* context = MakeGarbageCollected<CSSParserContext>(GetDocument());
return property.ParseSingleValue(range, *context, local_context);
}
};
} // namespace
TEST_F(CustomPropertyTest, UnregisteredPropertyIsInherited) {
CustomProperty property("--x", GetDocument());
EXPECT_TRUE(property.IsInherited());
}
TEST_F(CustomPropertyTest, RegisteredNonInheritedPropertyIsNotInherited) {
RegisterProperty(GetDocument(), "--x", "<length>", "42px", false);
CustomProperty property("--x", GetDocument());
EXPECT_FALSE(property.IsInherited());
}
TEST_F(CustomPropertyTest, RegisteredInheritedPropertyIsInherited) {
RegisterProperty(GetDocument(), "--x", "<length>", "42px", true);
CustomProperty property("--x", GetDocument());
EXPECT_TRUE(property.IsInherited());
}
TEST_F(CustomPropertyTest, StaticVariableInstance) {
CustomProperty property("--x", GetDocument());
EXPECT_FALSE(Variable::IsStaticInstance(property));
EXPECT_TRUE(Variable::IsStaticInstance(GetCSSPropertyVariable()));
}
TEST_F(CustomPropertyTest, PropertyID) {
CustomProperty property("--x", GetDocument());
EXPECT_EQ(CSSPropertyID::kVariable, property.PropertyID());
}
TEST_F(CustomPropertyTest, GetPropertyNameAtomicString) {
CustomProperty property("--x", GetDocument());
EXPECT_EQ(AtomicString("--x"), property.GetPropertyNameAtomicString());
}
TEST_F(CustomPropertyTest, ComputedCSSValueUnregistered) {
CustomProperty property("--x", GetDocument());
SetElementWithStyle("--x:foo");
const CSSValue* value = GetComputedValue(property);
EXPECT_TRUE(value->IsCustomPropertyDeclaration());
EXPECT_EQ("foo", value->CssText());
}
TEST_F(CustomPropertyTest, ComputedCSSValueInherited) {
RegisterProperty(GetDocument(), "--x", "<length>", "0px", true);
CustomProperty property("--x", GetDocument());
SetElementWithStyle("--x:100px");
const CSSValue* value = GetComputedValue(property);
ASSERT_TRUE(value->IsPrimitiveValue());
const auto* primitive_value = To<CSSPrimitiveValue>(value);
EXPECT_EQ(100, primitive_value->GetIntValue());
}
TEST_F(CustomPropertyTest, ComputedCSSValueNonInherited) {
RegisterProperty(GetDocument(), "--x", "<length>", "0px", false);
CustomProperty property("--x", GetDocument());
SetElementWithStyle("--x:100px");
const CSSValue* value = GetComputedValue(property);
ASSERT_TRUE(value->IsPrimitiveValue());
const auto* primitive_value = To<CSSPrimitiveValue>(value);
EXPECT_EQ(100, primitive_value->GetIntValue());
}
TEST_F(CustomPropertyTest, ComputedCSSValueInitial) {
RegisterProperty(GetDocument(), "--x", "<length>", "100px", false);
CustomProperty property("--x", GetDocument());
SetElementWithStyle(""); // Do not apply --x.
const CSSValue* value = GetComputedValue(property);
ASSERT_TRUE(value->IsPrimitiveValue());
const auto* primitive_value = To<CSSPrimitiveValue>(value);
EXPECT_EQ(100, primitive_value->GetIntValue());
}
TEST_F(CustomPropertyTest, ComputedCSSValueEmptyInitial) {
CustomProperty property("--x", GetDocument());
SetElementWithStyle(""); // Do not apply --x.
const CSSValue* value = GetComputedValue(property);
EXPECT_FALSE(value);
}
TEST_F(CustomPropertyTest, ComputedCSSValueLateRegistration) {
CustomProperty property("--x", GetDocument());
SetElementWithStyle("--x:100px");
RegisterProperty(GetDocument(), "--x", "<length>", "100px", false);
// The property was not registered when the style was computed, hence the
// computed value should be what we expect for an unregistered property.
const CSSValue* value = GetComputedValue(property);
EXPECT_TRUE(value->IsCustomPropertyDeclaration());
EXPECT_EQ("100px", value->CssText());
}
TEST_F(CustomPropertyTest, ParseSingleValueUnregistered) {
CustomProperty property("--x", GetDocument());
const CSSValue* value =
ParseValue(property, "100px", CSSParserLocalContext());
ASSERT_TRUE(value->IsCustomPropertyDeclaration());
EXPECT_EQ("100px", value->CssText());
}
TEST_F(CustomPropertyTest, ParseSingleValueAnimationTainted) {
CustomProperty property("--x", GetDocument());
const CSSValue* value1 = ParseValue(
property, "100px", CSSParserLocalContext().WithAnimationTainted(true));
const CSSValue* value2 = ParseValue(
property, "100px", CSSParserLocalContext().WithAnimationTainted(false));
EXPECT_TRUE(
To<CSSCustomPropertyDeclaration>(value1)->Value()->IsAnimationTainted());
EXPECT_FALSE(
To<CSSCustomPropertyDeclaration>(value2)->Value()->IsAnimationTainted());
}
TEST_F(CustomPropertyTest, ParseSingleValueTyped) {
RegisterProperty(GetDocument(), "--x", "<length>", "0px", false);
CustomProperty property("--x", GetDocument());
const CSSValue* value1 =
ParseValue(property, "100px", CSSParserLocalContext());
EXPECT_TRUE(value1->IsPrimitiveValue());
EXPECT_EQ(100, To<CSSPrimitiveValue>(value1)->GetIntValue());
const CSSValue* value2 =
ParseValue(property, "maroon", CSSParserLocalContext());
EXPECT_FALSE(value2);
}
TEST_F(CustomPropertyTest, ParseSingleValueUntyped) {
RegisterProperty(GetDocument(), "--x", "<length>", "0px", false);
CustomProperty property("--x", GetDocument());
const CSSValue* value = ParseValue(
property, "maroon",
CSSParserLocalContext().WithVariableMode(VariableMode::kUntyped));
ASSERT_TRUE(value->IsCustomPropertyDeclaration());
EXPECT_EQ("maroon", value->CssText());
}
TEST_F(CustomPropertyTest, ParseSingleValueValidatedUntyped) {
RegisterProperty(GetDocument(), "--x", "<length>", "0px", false);
CustomProperty property("--x", GetDocument());
auto local_context =
CSSParserLocalContext().WithVariableMode(VariableMode::kValidatedUntyped);
const CSSValue* value1 = ParseValue(property, "100px", local_context);
ASSERT_TRUE(value1->IsCustomPropertyDeclaration());
EXPECT_EQ("100px", value1->CssText());
const CSSValue* value2 = ParseValue(property, "maroon", local_context);
EXPECT_FALSE(value2);
}
TEST_F(CustomPropertyTest, GetCSSPropertyName) {
CustomProperty property("--x", GetDocument());
EXPECT_EQ(CSSPropertyName("--x"), property.GetCSSPropertyName());
}
TEST_F(CustomPropertyTest, SupportsGuaranteedInvalid) {
RegisterProperty(GetDocument(), "--universal", "*", "foo", true);
RegisterProperty(GetDocument(), "--no-initial", "*", base::nullopt, true);
RegisterProperty(GetDocument(), "--length", "<length>", "0px", true);
CustomProperty unregistered("--unregistered", GetDocument());
CustomProperty universal("--universal", GetDocument());
CustomProperty no_initial_value("--no-initial", GetDocument());
CustomProperty length("--length", GetDocument());
EXPECT_TRUE(unregistered.SupportsGuaranteedInvalid());
EXPECT_TRUE(universal.SupportsGuaranteedInvalid());
EXPECT_TRUE(no_initial_value.SupportsGuaranteedInvalid());
EXPECT_FALSE(length.SupportsGuaranteedInvalid());
}
TEST_F(CustomPropertyTest, HasInitialValue) {
RegisterProperty(GetDocument(), "--universal", "*", "foo", true);
RegisterProperty(GetDocument(), "--no-initial", "*", base::nullopt, true);
RegisterProperty(GetDocument(), "--length", "<length>", "0px", true);
CustomProperty unregistered("--unregistered", GetDocument());
CustomProperty universal("--universal", GetDocument());
CustomProperty no_initial_value("--no-initial", GetDocument());
CustomProperty length("--length", GetDocument());
EXPECT_FALSE(unregistered.HasInitialValue());
EXPECT_TRUE(universal.HasInitialValue());
EXPECT_FALSE(no_initial_value.HasInitialValue());
EXPECT_TRUE(length.HasInitialValue());
}
} // namespace blink