blob: c066f28990efdb21c64c677ed713eb08823c0a34 [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/style/computed_style.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/css/css_font_selector.h"
#include "third_party/blink/renderer/core/css/css_gradient_value.h"
#include "third_party/blink/renderer/core/css/css_identifier_value.h"
#include "third_party/blink/renderer/core/css/css_math_expression_node.h"
#include "third_party/blink/renderer/core/css/css_math_function_value.h"
#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
#include "third_party/blink/renderer/core/css/css_test_helpers.h"
#include "third_party/blink/renderer/core/css/css_value_list.h"
#include "third_party/blink/renderer/core/css/parser/css_parser.h"
#include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
#include "third_party/blink/renderer/core/css/property_registry.h"
#include "third_party/blink/renderer/core/css/resolver/style_adjuster.h"
#include "third_party/blink/renderer/core/css/resolver/style_cascade.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/document.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/style/clip_path_operation.h"
#include "third_party/blink/renderer/core/style/shape_clip_path_operation.h"
#include "third_party/blink/renderer/core/style/shape_value.h"
#include "third_party/blink/renderer/core/style/style_difference.h"
#include "third_party/blink/renderer/core/style/style_generated_image.h"
#include "third_party/blink/renderer/core/style/style_initial_data.h"
#include "third_party/blink/renderer/core/testing/color_scheme_helper.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/transforms/scale_transform_operation.h"
#include "ui/base/ui_base_features.h"
namespace blink {
TEST(ComputedStyleTest, ShapeOutsideBoxEqual) {
auto* shape1 = MakeGarbageCollected<ShapeValue>(CSSBoxType::kContent);
auto* shape2 = MakeGarbageCollected<ShapeValue>(CSSBoxType::kContent);
scoped_refptr<ComputedStyle> style1 = ComputedStyle::Create();
scoped_refptr<ComputedStyle> style2 = ComputedStyle::Create();
style1->SetShapeOutside(shape1);
style2->SetShapeOutside(shape2);
EXPECT_EQ(*style1, *style2);
}
TEST(ComputedStyleTest, ShapeOutsideCircleEqual) {
scoped_refptr<BasicShapeCircle> circle1 = BasicShapeCircle::Create();
scoped_refptr<BasicShapeCircle> circle2 = BasicShapeCircle::Create();
auto* shape1 = MakeGarbageCollected<ShapeValue>(std::move(circle1),
CSSBoxType::kContent);
auto* shape2 = MakeGarbageCollected<ShapeValue>(std::move(circle2),
CSSBoxType::kContent);
scoped_refptr<ComputedStyle> style1 = ComputedStyle::Create();
scoped_refptr<ComputedStyle> style2 = ComputedStyle::Create();
style1->SetShapeOutside(shape1);
style2->SetShapeOutside(shape2);
EXPECT_EQ(*style1, *style2);
}
TEST(ComputedStyleTest, ClipPathEqual) {
scoped_refptr<BasicShapeCircle> shape = BasicShapeCircle::Create();
scoped_refptr<ShapeClipPathOperation> path1 =
ShapeClipPathOperation::Create(shape);
scoped_refptr<ShapeClipPathOperation> path2 =
ShapeClipPathOperation::Create(shape);
scoped_refptr<ComputedStyle> style1 = ComputedStyle::Create();
scoped_refptr<ComputedStyle> style2 = ComputedStyle::Create();
style1->SetClipPath(path1);
style2->SetClipPath(path2);
EXPECT_EQ(*style1, *style2);
}
TEST(ComputedStyleTest, FocusRingWidth) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
if (::features::IsFormControlsRefreshEnabled()) {
style->SetOutlineStyleIsAuto(static_cast<bool>(OutlineIsAuto::kOn));
EXPECT_EQ(3, style->GetOutlineStrokeWidthForFocusRing());
style->SetEffectiveZoom(3.5);
style->SetOutlineWidth(4);
EXPECT_EQ(3.5, style->GetOutlineStrokeWidthForFocusRing());
} else {
style->SetEffectiveZoom(3.5);
style->SetOutlineStyle(EBorderStyle::kSolid);
#if defined(OS_MAC)
EXPECT_EQ(3, style->GetOutlineStrokeWidthForFocusRing());
#else
style->SetOutlineStyleIsAuto(static_cast<bool>(OutlineIsAuto::kOn));
static uint16_t outline_width = 4;
style->SetOutlineWidth(outline_width);
double expected_width = 3.5;
EXPECT_EQ(expected_width, style->GetOutlineStrokeWidthForFocusRing());
expected_width = 1.0;
style->SetEffectiveZoom(0.5);
EXPECT_EQ(expected_width, style->GetOutlineStrokeWidthForFocusRing());
#endif
}
}
TEST(ComputedStyleTest, FocusRingOutset) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
style->SetOutlineStyle(EBorderStyle::kSolid);
style->SetOutlineStyleIsAuto(static_cast<bool>(OutlineIsAuto::kOn));
style->SetEffectiveZoom(4.75);
if (::features::IsFormControlsRefreshEnabled()) {
EXPECT_EQ(4, style->OutlineOutsetExtent());
} else {
#if defined(OS_MAC)
EXPECT_EQ(4, style->OutlineOutsetExtent());
#else
EXPECT_EQ(3, style->OutlineOutsetExtent());
#endif
}
}
TEST(ComputedStyleTest, SVGStackingContext) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
style->UpdateIsStackingContextWithoutContainment(false, false, true);
EXPECT_TRUE(style->IsStackingContextWithoutContainment());
}
TEST(ComputedStyleTest, Preserve3dForceStackingContext) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
style->SetTransformStyle3D(ETransformStyle3D::kPreserve3d);
style->SetOverflowX(EOverflow::kHidden);
style->SetOverflowY(EOverflow::kHidden);
style->UpdateIsStackingContextWithoutContainment(false, false, false);
EXPECT_EQ(ETransformStyle3D::kFlat, style->UsedTransformStyle3D());
EXPECT_TRUE(style->IsStackingContextWithoutContainment());
}
TEST(ComputedStyleTest, LayoutContainmentStackingContext) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
EXPECT_FALSE(style->IsStackingContextWithoutContainment());
style->SetContain(kContainsLayout);
style->UpdateIsStackingContextWithoutContainment(false, false, false);
// Containment doesn't change IsStackingContextWithoutContainment
EXPECT_FALSE(style->IsStackingContextWithoutContainment());
}
TEST(ComputedStyleTest, FirstPublicPseudoStyle) {
static_assert(kFirstPublicPseudoId == kPseudoIdFirstLine,
"Make sure we are testing the first public pseudo id");
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
style->SetHasPseudoElementStyle(kPseudoIdFirstLine);
EXPECT_TRUE(style->HasPseudoElementStyle(kPseudoIdFirstLine));
EXPECT_TRUE(style->HasAnyPseudoElementStyles());
}
TEST(ComputedStyleTest, LastPublicPseudoElementStyle) {
static_assert(kFirstInternalPseudoId - 1 == kPseudoIdGrammarError,
"Make sure we are testing the last public pseudo id");
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
style->SetHasPseudoElementStyle(kPseudoIdGrammarError);
EXPECT_TRUE(style->HasPseudoElementStyle(kPseudoIdGrammarError));
EXPECT_TRUE(style->HasAnyPseudoElementStyles());
}
TEST(ComputedStyleTest,
UpdatePropertySpecificDifferencesRespectsTransformAnimation) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
other->SetHasCurrentTransformAnimation(true);
StyleDifference diff;
style->UpdatePropertySpecificDifferences(*other, diff);
EXPECT_TRUE(diff.TransformChanged());
}
TEST(ComputedStyleTest,
UpdatePropertySpecificDifferencesCompositingReasonsTransforom) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
TransformOperations operations;
// An operation is necessary since having either a non-empty transform list
// or a transform animation will set HasTransform();
operations.Operations().push_back(
ScaleTransformOperation::Create(1, 1, TransformOperation::kScale));
style->SetTransform(operations);
other->SetTransform(operations);
other->SetHasCurrentTransformAnimation(true);
StyleDifference diff;
style->UpdatePropertySpecificDifferences(*other, diff);
EXPECT_FALSE(diff.TransformChanged());
EXPECT_TRUE(diff.CompositingReasonsChanged());
}
TEST(ComputedStyleTest,
UpdatePropertySpecificDifferencesCompositingReasonsOpacity) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
other->SetHasCurrentOpacityAnimation(true);
StyleDifference diff;
style->UpdatePropertySpecificDifferences(*other, diff);
EXPECT_TRUE(diff.CompositingReasonsChanged());
}
TEST(ComputedStyleTest,
UpdatePropertySpecificDifferencesCompositingReasonsFilter) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
other->SetHasCurrentFilterAnimation(true);
StyleDifference diff;
style->UpdatePropertySpecificDifferences(*other, diff);
EXPECT_TRUE(diff.CompositingReasonsChanged());
}
TEST(ComputedStyleTest,
UpdatePropertySpecificDifferencesCompositingReasonsBackdropFilter) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
other->SetHasCurrentBackdropFilterAnimation(true);
StyleDifference diff;
style->UpdatePropertySpecificDifferences(*other, diff);
EXPECT_TRUE(diff.CompositingReasonsChanged());
}
TEST(ComputedStyleTest,
UpdatePropertySpecificDifferencesCompositingReasonsBackfaceVisibility) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
other->SetBackfaceVisibility(EBackfaceVisibility::kHidden);
StyleDifference diff;
style->UpdatePropertySpecificDifferences(*other, diff);
EXPECT_TRUE(diff.CompositingReasonsChanged());
}
TEST(ComputedStyleTest,
UpdatePropertySpecificDifferencesCompositingReasonsWillChange) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
other->SetBackfaceVisibility(EBackfaceVisibility::kHidden);
StyleDifference diff;
style->UpdatePropertySpecificDifferences(*other, diff);
EXPECT_TRUE(diff.CompositingReasonsChanged());
}
TEST(ComputedStyleTest,
UpdatePropertySpecificDifferencesCompositingReasonsUsedStylePreserve3D) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
style->SetTransformStyle3D(ETransformStyle3D::kPreserve3d);
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
// This induces a flat used transform style.
other->SetOpacity(0.5);
StyleDifference diff;
style->UpdatePropertySpecificDifferences(*other, diff);
EXPECT_TRUE(diff.CompositingReasonsChanged());
}
TEST(ComputedStyleTest,
UpdatePropertySpecificDifferencesCompositingReasonsOverflow) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
other->SetOverflowX(EOverflow::kHidden);
StyleDifference diff;
style->UpdatePropertySpecificDifferences(*other, diff);
EXPECT_TRUE(diff.CompositingReasonsChanged());
}
TEST(ComputedStyleTest,
UpdatePropertySpecificDifferencesCompositingReasonsContainsPaint) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
// This induces a flat used transform style.
other->SetContain(kContainsPaint);
StyleDifference diff;
style->UpdatePropertySpecificDifferences(*other, diff);
EXPECT_TRUE(diff.CompositingReasonsChanged());
}
TEST(ComputedStyleTest, UpdateBackgroundColorDifferencesHasAlpha) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
StyleDifference diff;
style->AdjustDiffForBackgroundVisuallyEqual(*other, diff);
EXPECT_FALSE(diff.HasAlphaChanged());
style->SetBackgroundColor(StyleColor(Color(255, 255, 255, 255)));
other->SetBackgroundColor(StyleColor(Color(255, 255, 255, 128)));
EXPECT_FALSE(
style->VisitedDependentColor(GetCSSPropertyBackgroundColor()).HasAlpha());
EXPECT_TRUE(
other->VisitedDependentColor(GetCSSPropertyBackgroundColor()).HasAlpha());
style->AdjustDiffForBackgroundVisuallyEqual(*other, diff);
EXPECT_TRUE(diff.HasAlphaChanged());
}
TEST(ComputedStyleTest, UpdateBackgroundLayerDifferencesHasAlpha) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
StyleDifference diff;
style->AdjustDiffForBackgroundVisuallyEqual(*other, diff);
EXPECT_FALSE(diff.HasAlphaChanged());
other->AccessBackgroundLayers().EnsureNext();
style->AdjustDiffForBackgroundVisuallyEqual(*other, diff);
EXPECT_TRUE(diff.HasAlphaChanged());
}
TEST(ComputedStyleTest, HasOutlineWithCurrentColor) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
EXPECT_FALSE(style->HasOutline());
EXPECT_FALSE(style->HasOutlineWithCurrentColor());
style->SetOutlineColor(StyleColor::CurrentColor());
EXPECT_FALSE(style->HasOutlineWithCurrentColor());
style->SetOutlineWidth(5);
EXPECT_FALSE(style->HasOutlineWithCurrentColor());
style->SetOutlineStyle(EBorderStyle::kSolid);
EXPECT_TRUE(style->HasOutlineWithCurrentColor());
}
TEST(ComputedStyleTest, HasBorderColorReferencingCurrentColor) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
EXPECT_FALSE(style->HasBorderColorReferencingCurrentColor());
style->SetBorderBottomColor(StyleColor::CurrentColor());
EXPECT_FALSE(style->HasBorderColorReferencingCurrentColor());
style->SetBorderBottomWidth(5);
EXPECT_FALSE(style->HasBorderColorReferencingCurrentColor());
style->SetBorderBottomStyle(EBorderStyle::kSolid);
EXPECT_TRUE(style->HasBorderColorReferencingCurrentColor());
}
TEST(ComputedStyleTest, BorderWidth) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
style->SetBorderBottomWidth(5);
EXPECT_EQ(style->BorderBottomWidth(), 0);
EXPECT_EQ(style->BorderBottom().Width(), 5);
style->SetBorderBottomStyle(EBorderStyle::kSolid);
EXPECT_EQ(style->BorderBottomWidth(), 5);
EXPECT_EQ(style->BorderBottom().Width(), 5);
}
TEST(ComputedStyleTest, CursorList) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> other = ComputedStyle::Create();
auto* gradient = MakeGarbageCollected<cssvalue::CSSLinearGradientValue>(
nullptr, nullptr, nullptr, nullptr, nullptr, cssvalue::kRepeating);
auto* image_value = MakeGarbageCollected<StyleGeneratedImage>(*gradient);
auto* other_image_value =
MakeGarbageCollected<StyleGeneratedImage>(*gradient);
EXPECT_TRUE(DataEquivalent(image_value, other_image_value));
style->AddCursor(image_value, false);
other->AddCursor(other_image_value, false);
EXPECT_EQ(*style, *other);
}
TEST(ComputedStyleTest, BorderStyle) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> other = ComputedStyle::Create();
style->SetBorderLeftStyle(EBorderStyle::kSolid);
style->SetBorderTopStyle(EBorderStyle::kSolid);
style->SetBorderRightStyle(EBorderStyle::kSolid);
style->SetBorderBottomStyle(EBorderStyle::kSolid);
other->SetBorderLeftStyle(EBorderStyle::kSolid);
other->SetBorderTopStyle(EBorderStyle::kSolid);
other->SetBorderRightStyle(EBorderStyle::kSolid);
other->SetBorderBottomStyle(EBorderStyle::kSolid);
EXPECT_TRUE(style->BorderSizeEquals(*other));
style->SetBorderLeftWidth(1.0);
EXPECT_FALSE(style->BorderSizeEquals(*other));
other->SetBorderLeftWidth(1.0);
EXPECT_TRUE(style->BorderSizeEquals(*other));
EXPECT_TRUE(style->BorderSizeEquals(*other));
style->SetBorderTopWidth(1.0);
EXPECT_FALSE(style->BorderSizeEquals(*other));
other->SetBorderTopWidth(1.0);
EXPECT_TRUE(style->BorderSizeEquals(*other));
EXPECT_TRUE(style->BorderSizeEquals(*other));
style->SetBorderRightWidth(1.0);
EXPECT_FALSE(style->BorderSizeEquals(*other));
other->SetBorderRightWidth(1.0);
EXPECT_TRUE(style->BorderSizeEquals(*other));
EXPECT_TRUE(style->BorderSizeEquals(*other));
style->SetBorderBottomWidth(1.0);
EXPECT_FALSE(style->BorderSizeEquals(*other));
other->SetBorderBottomWidth(1.0);
EXPECT_TRUE(style->BorderSizeEquals(*other));
style->SetBorderLeftStyle(EBorderStyle::kHidden);
EXPECT_FALSE(style->BorderSizeEquals(*other));
style->SetBorderLeftStyle(EBorderStyle::kNone);
EXPECT_FALSE(style->BorderSizeEquals(*other));
style->SetBorderLeftStyle(EBorderStyle::kSolid);
EXPECT_TRUE(style->BorderSizeEquals(*other));
style->SetBorderTopStyle(EBorderStyle::kHidden);
EXPECT_FALSE(style->BorderSizeEquals(*other));
style->SetBorderTopStyle(EBorderStyle::kNone);
EXPECT_FALSE(style->BorderSizeEquals(*other));
style->SetBorderTopStyle(EBorderStyle::kSolid);
EXPECT_TRUE(style->BorderSizeEquals(*other));
style->SetBorderRightStyle(EBorderStyle::kHidden);
EXPECT_FALSE(style->BorderSizeEquals(*other));
style->SetBorderRightStyle(EBorderStyle::kNone);
EXPECT_FALSE(style->BorderSizeEquals(*other));
style->SetBorderRightStyle(EBorderStyle::kSolid);
EXPECT_TRUE(style->BorderSizeEquals(*other));
style->SetBorderBottomStyle(EBorderStyle::kHidden);
EXPECT_FALSE(style->BorderSizeEquals(*other));
style->SetBorderBottomStyle(EBorderStyle::kNone);
EXPECT_FALSE(style->BorderSizeEquals(*other));
style->SetBorderBottomStyle(EBorderStyle::kSolid);
EXPECT_TRUE(style->BorderSizeEquals(*other));
}
#define TEST_ANIMATION_FLAG(flag, inherited) \
do { \
auto style = ComputedStyle::Create(); \
auto other = ComputedStyle::Create(); \
EXPECT_FALSE(style->flag()); \
EXPECT_FALSE(other->flag()); \
style->Set##flag(true); \
EXPECT_TRUE(style->flag()); \
EXPECT_EQ(ComputedStyle::Difference::inherited, \
ComputedStyle::ComputeDifference(style.get(), other.get())); \
auto diff = style->VisualInvalidationDiff(*document, *other); \
EXPECT_TRUE(diff.HasDifference()); \
EXPECT_TRUE(diff.CompositingReasonsChanged()); \
} while (false)
#define TEST_ANIMATION_FLAG_NO_DIFF(flag) \
do { \
auto style = ComputedStyle::Create(); \
auto other = ComputedStyle::Create(); \
EXPECT_FALSE(style->flag()); \
EXPECT_FALSE(other->flag()); \
style->Set##flag(true); \
EXPECT_TRUE(style->flag()); \
EXPECT_EQ(ComputedStyle::Difference::kEqual, \
ComputedStyle::ComputeDifference(style.get(), other.get())); \
auto diff = style->VisualInvalidationDiff(*document, *other); \
EXPECT_FALSE(diff.HasDifference()); \
EXPECT_FALSE(diff.CompositingReasonsChanged()); \
} while (false)
TEST(ComputedStyleTest, AnimationFlags) {
Persistent<Document> document = Document::CreateForTest();
TEST_ANIMATION_FLAG(HasCurrentTransformAnimation, kNonInherited);
TEST_ANIMATION_FLAG(HasCurrentOpacityAnimation, kNonInherited);
TEST_ANIMATION_FLAG(HasCurrentFilterAnimation, kNonInherited);
TEST_ANIMATION_FLAG(HasCurrentBackdropFilterAnimation, kNonInherited);
TEST_ANIMATION_FLAG(SubtreeWillChangeContents, kInherited);
TEST_ANIMATION_FLAG_NO_DIFF(IsRunningTransformAnimationOnCompositor);
TEST_ANIMATION_FLAG_NO_DIFF(IsRunningOpacityAnimationOnCompositor);
TEST_ANIMATION_FLAG_NO_DIFF(IsRunningFilterAnimationOnCompositor);
TEST_ANIMATION_FLAG_NO_DIFF(IsRunningBackdropFilterAnimationOnCompositor);
}
TEST(ComputedStyleTest, CustomPropertiesEqual_Values) {
auto dummy = std::make_unique<DummyPageHolder>(IntSize(0, 0));
css_test_helpers::RegisterProperty(dummy->GetDocument(), "--x", "<length>",
"0px", false);
scoped_refptr<ComputedStyle> style1 = ComputedStyle::Create();
scoped_refptr<ComputedStyle> style2 = ComputedStyle::Create();
using UnitType = CSSPrimitiveValue::UnitType;
const auto* value1 = CSSNumericLiteralValue::Create(1.0, UnitType::kPixels);
const auto* value2 = CSSNumericLiteralValue::Create(2.0, UnitType::kPixels);
const auto* value3 = CSSNumericLiteralValue::Create(1.0, UnitType::kPixels);
Vector<AtomicString> properties;
properties.push_back("--x");
style1->SetVariableValue("--x", value1, false);
style2->SetVariableValue("--x", value1, false);
EXPECT_TRUE(style1->CustomPropertiesEqual(properties, *style2));
style1->SetVariableValue("--x", value1, false);
style2->SetVariableValue("--x", value3, false);
EXPECT_TRUE(style1->CustomPropertiesEqual(properties, *style2));
style1->SetVariableValue("--x", value1, false);
style2->SetVariableValue("--x", value2, false);
EXPECT_FALSE(style1->CustomPropertiesEqual(properties, *style2));
}
TEST(ComputedStyleTest, CustomPropertiesEqual_Data) {
auto dummy = std::make_unique<DummyPageHolder>(IntSize(0, 0));
css_test_helpers::RegisterProperty(dummy->GetDocument(), "--x", "<length>",
"0px", false);
scoped_refptr<ComputedStyle> style1 = ComputedStyle::Create();
scoped_refptr<ComputedStyle> style2 = ComputedStyle::Create();
auto value1 = css_test_helpers::CreateVariableData("foo");
auto value2 = css_test_helpers::CreateVariableData("bar");
auto value3 = css_test_helpers::CreateVariableData("foo");
Vector<AtomicString> properties;
properties.push_back("--x");
style1->SetVariableData("--x", value1, false);
style2->SetVariableData("--x", value1, false);
EXPECT_TRUE(style1->CustomPropertiesEqual(properties, *style2));
style1->SetVariableData("--x", value1, false);
style2->SetVariableData("--x", value3, false);
EXPECT_TRUE(style1->CustomPropertiesEqual(properties, *style2));
style1->SetVariableData("--x", value1, false);
style2->SetVariableData("--x", value2, false);
EXPECT_FALSE(style1->CustomPropertiesEqual(properties, *style2));
}
TEST(ComputedStyleTest, CustomPropertiesInheritance_FastPath) {
auto dummy = std::make_unique<DummyPageHolder>(IntSize(0, 0));
css_test_helpers::RegisterProperty(dummy->GetDocument(), "--x", "<length>",
"0px", true);
scoped_refptr<ComputedStyle> old_style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> new_style = ComputedStyle::Create();
using UnitType = CSSPrimitiveValue::UnitType;
const auto* value1 = CSSNumericLiteralValue::Create(1.0, UnitType::kPixels);
const auto* value2 = CSSNumericLiteralValue::Create(2.0, UnitType::kPixels);
EXPECT_FALSE(old_style->HasVariableDeclaration());
EXPECT_FALSE(old_style->HasVariableReference());
EXPECT_FALSE(new_style->HasVariableReference());
EXPECT_FALSE(new_style->HasVariableDeclaration());
// Removed variable
old_style->SetVariableValue("--x", value1, true);
EXPECT_EQ(ComputedStyle::Difference::kIndependentInherited,
ComputedStyle::ComputeDifference(old_style.get(), new_style.get()));
old_style = ComputedStyle::Create();
new_style = ComputedStyle::Create();
// Added a new variable
new_style->SetVariableValue("--x", value2, true);
EXPECT_EQ(ComputedStyle::Difference::kIndependentInherited,
ComputedStyle::ComputeDifference(old_style.get(), new_style.get()));
// Change value of variable
old_style->SetVariableValue("--x", value1, true);
new_style->SetVariableValue("--x", value2, true);
new_style->SetHasVariableReference();
EXPECT_FALSE(new_style->HasVariableDeclaration());
EXPECT_TRUE(new_style->HasVariableReference());
EXPECT_EQ(ComputedStyle::Difference::kIndependentInherited,
ComputedStyle::ComputeDifference(old_style.get(), new_style.get()));
old_style = ComputedStyle::Create();
new_style = ComputedStyle::Create();
// New styles with variable declaration don't force style recalc
old_style->SetVariableValue("--x", value1, true);
new_style->SetVariableValue("--x", value2, true);
new_style->SetHasVariableDeclaration();
EXPECT_TRUE(new_style->HasVariableDeclaration());
EXPECT_FALSE(new_style->HasVariableReference());
EXPECT_EQ(ComputedStyle::Difference::kIndependentInherited,
ComputedStyle::ComputeDifference(old_style.get(), new_style.get()));
old_style = ComputedStyle::Create();
new_style = ComputedStyle::Create();
// New styles with variable reference don't force style recalc
old_style->SetVariableValue("--x", value1, true);
new_style->SetVariableValue("--x", value2, true);
new_style->SetHasVariableDeclaration();
new_style->SetHasVariableReference();
EXPECT_TRUE(new_style->HasVariableDeclaration());
EXPECT_TRUE(new_style->HasVariableReference());
EXPECT_EQ(ComputedStyle::Difference::kIndependentInherited,
ComputedStyle::ComputeDifference(old_style.get(), new_style.get()));
}
TEST(ComputedStyleTest, CustomPropertiesInheritance_StyleRecalc) {
auto dummy = std::make_unique<DummyPageHolder>(IntSize(0, 0));
css_test_helpers::RegisterProperty(dummy->GetDocument(), "--x", "<length>",
"0px", true);
scoped_refptr<ComputedStyle> old_style = ComputedStyle::Create();
scoped_refptr<ComputedStyle> new_style = ComputedStyle::Create();
using UnitType = CSSPrimitiveValue::UnitType;
const auto* value1 = CSSNumericLiteralValue::Create(1.0, UnitType::kPixels);
const auto* value2 = CSSNumericLiteralValue::Create(2.0, UnitType::kPixels);
EXPECT_FALSE(old_style->HasVariableDeclaration());
EXPECT_FALSE(old_style->HasVariableReference());
EXPECT_FALSE(new_style->HasVariableReference());
EXPECT_FALSE(new_style->HasVariableDeclaration());
// Removed variable value
// Old styles with variable reference force style recalc
old_style->SetHasVariableReference();
old_style->SetVariableValue("--x", value2, true);
EXPECT_TRUE(old_style->HasVariableReference());
EXPECT_EQ(ComputedStyle::Difference::kInherited,
ComputedStyle::ComputeDifference(old_style.get(), new_style.get()));
old_style = ComputedStyle::Create();
new_style = ComputedStyle::Create();
// New variable value
// Old styles with variable declaration force style recalc
old_style->SetHasVariableDeclaration();
new_style->SetVariableValue("--x", value2, true);
EXPECT_TRUE(old_style->HasVariableDeclaration());
EXPECT_EQ(ComputedStyle::Difference::kInherited,
ComputedStyle::ComputeDifference(old_style.get(), new_style.get()));
old_style = ComputedStyle::Create();
new_style = ComputedStyle::Create();
// Change variable value
// Old styles with variable declaration force style recalc
old_style->SetVariableValue("--x", value1, true);
new_style->SetVariableValue("--x", value2, true);
old_style->SetHasVariableDeclaration();
EXPECT_TRUE(old_style->HasVariableDeclaration());
EXPECT_EQ(ComputedStyle::Difference::kInherited,
ComputedStyle::ComputeDifference(old_style.get(), new_style.get()));
old_style = ComputedStyle::Create();
new_style = ComputedStyle::Create();
// Change variable value
// Old styles with variable reference force style recalc
old_style->SetVariableValue("--x", value1, true);
new_style->SetVariableValue("--x", value2, true);
old_style->SetHasVariableReference();
EXPECT_TRUE(old_style->HasVariableReference());
EXPECT_EQ(ComputedStyle::Difference::kInherited,
ComputedStyle::ComputeDifference(old_style.get(), new_style.get()));
}
TEST(ComputedStyleTest, ApplyColorSchemeLightOnDark) {
ScopedCSSColorSchemeUARenderingForTest scoped_ua_enabled(true);
std::unique_ptr<DummyPageHolder> dummy_page_holder_ =
std::make_unique<DummyPageHolder>(IntSize(0, 0), nullptr);
const ComputedStyle* initial = &ComputedStyle::InitialStyle();
ColorSchemeHelper color_scheme_helper(dummy_page_holder_->GetDocument());
color_scheme_helper.SetPreferredColorScheme(
mojom::blink::PreferredColorScheme::kDark);
StyleResolverState state(dummy_page_holder_->GetDocument(),
*dummy_page_holder_->GetDocument().documentElement(),
initial, initial);
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
state.SetStyle(style);
CSSPropertyRef ref("color-scheme", state.GetDocument());
CSSValueList* dark_value = CSSValueList::CreateSpaceSeparated();
dark_value->Append(*CSSIdentifierValue::Create(CSSValueID::kDark));
CSSValueList* light_value = CSSValueList::CreateSpaceSeparated();
light_value->Append(*CSSIdentifierValue::Create(CSSValueID::kLight));
To<Longhand>(ref.GetProperty()).ApplyValue(state, *dark_value);
EXPECT_EQ(mojom::blink::ColorScheme::kDark, style->UsedColorScheme());
To<Longhand>(ref.GetProperty()).ApplyValue(state, *light_value);
EXPECT_EQ(mojom::blink::ColorScheme::kLight, style->UsedColorScheme());
}
TEST(ComputedStyleTest, ApplyInternalLightDarkColor) {
using css_test_helpers::ParseDeclarationBlock;
ScopedCSSColorSchemeUARenderingForTest scoped_ua_enabled(true);
std::unique_ptr<DummyPageHolder> dummy_page_holder_ =
std::make_unique<DummyPageHolder>(IntSize(0, 0), nullptr);
const ComputedStyle* initial = &ComputedStyle::InitialStyle();
ColorSchemeHelper color_scheme_helper(dummy_page_holder_->GetDocument());
color_scheme_helper.SetPreferredColorScheme(
mojom::blink::PreferredColorScheme::kDark);
StyleResolverState state(dummy_page_holder_->GetDocument(),
*dummy_page_holder_->GetDocument().documentElement(),
initial, initial);
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
state.SetStyle(style);
CSSValueList* dark_value = CSSValueList::CreateSpaceSeparated();
dark_value->Append(*CSSIdentifierValue::Create(CSSValueID::kDark));
CSSValueList* light_value = CSSValueList::CreateSpaceSeparated();
light_value->Append(*CSSIdentifierValue::Create(CSSValueID::kLight));
auto* color_declaration = ParseDeclarationBlock(
"color:-internal-light-dark(black, white)", CSSParserMode::kUASheetMode);
auto* dark_declaration = ParseDeclarationBlock("color-scheme:dark");
auto* light_declaration = ParseDeclarationBlock("color-scheme:light");
StyleCascade cascade1(state);
cascade1.MutableMatchResult().AddMatchedProperties(color_declaration);
cascade1.MutableMatchResult().AddMatchedProperties(dark_declaration);
cascade1.Apply();
EXPECT_EQ(Color::kWhite, style->VisitedDependentColor(GetCSSPropertyColor()));
StyleCascade cascade2(state);
cascade2.MutableMatchResult().AddMatchedProperties(color_declaration);
cascade2.MutableMatchResult().AddMatchedProperties(light_declaration);
cascade2.Apply();
EXPECT_EQ(Color::kBlack, style->VisitedDependentColor(GetCSSPropertyColor()));
}
TEST(ComputedStyleTest, ApplyInternalLightDarkBackgroundImage) {
using css_test_helpers::ParseDeclarationBlock;
ScopedCSSColorSchemeUARenderingForTest scoped_ua_enabled(true);
std::unique_ptr<DummyPageHolder> dummy_page_holder_ =
std::make_unique<DummyPageHolder>(IntSize(0, 0), nullptr);
const ComputedStyle* initial = &ComputedStyle::InitialStyle();
ColorSchemeHelper color_scheme_helper(dummy_page_holder_->GetDocument());
color_scheme_helper.SetPreferredColorScheme(
mojom::blink::PreferredColorScheme::kDark);
StyleResolverState state(dummy_page_holder_->GetDocument(),
*dummy_page_holder_->GetDocument().documentElement(),
initial, initial);
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
state.SetStyle(style);
auto* bgimage_declaration = ParseDeclarationBlock(
"background-image:-internal-light-dark(none, url(dummy.png))",
kUASheetMode);
auto* dark_declaration = ParseDeclarationBlock("color-scheme:dark");
auto* light_declaration = ParseDeclarationBlock("color-scheme:light");
EXPECT_FALSE(style->HasNonInheritedLightDarkValue());
StyleCascade cascade1(state);
cascade1.MutableMatchResult().AddMatchedProperties(bgimage_declaration);
cascade1.MutableMatchResult().AddMatchedProperties(dark_declaration);
cascade1.Apply();
EXPECT_TRUE(style->HasBackgroundImage());
EXPECT_TRUE(style->HasNonInheritedLightDarkValue());
style = ComputedStyle::Create();
state.SetStyle(style);
StyleCascade cascade2(state);
cascade2.MutableMatchResult().AddMatchedProperties(bgimage_declaration);
cascade2.MutableMatchResult().AddMatchedProperties(light_declaration);
cascade2.Apply();
EXPECT_FALSE(style->HasBackgroundImage());
EXPECT_TRUE(style->HasNonInheritedLightDarkValue());
}
TEST(ComputedStyleTest, StrokeWidthZoomAndCalc) {
std::unique_ptr<DummyPageHolder> dummy_page_holder_ =
std::make_unique<DummyPageHolder>(IntSize(0, 0), nullptr);
const ComputedStyle* initial = &ComputedStyle::InitialStyle();
StyleResolverState state(dummy_page_holder_->GetDocument(),
*dummy_page_holder_->GetDocument().documentElement(),
initial, initial);
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
style->SetEffectiveZoom(1.5);
state.SetStyle(style);
auto* calc_value =
CSSMathFunctionValue::Create(CSSMathExpressionNumericLiteral::Create(
CSSNumericLiteralValue::Create(10,
CSSPrimitiveValue::UnitType::kNumber),
true));
To<Longhand>(GetCSSPropertyStrokeWidth()).ApplyValue(state, *calc_value);
auto* computed_value = To<Longhand>(GetCSSPropertyStrokeWidth())
.CSSValueFromComputedStyleInternal(
*style, nullptr /* layout_object */,
false /* allow_visited_style */);
ASSERT_TRUE(computed_value);
auto* numeric_value = DynamicTo<CSSNumericLiteralValue>(computed_value);
ASSERT_TRUE(numeric_value);
EXPECT_TRUE(numeric_value->IsPx());
EXPECT_EQ(10, numeric_value->DoubleValue());
}
TEST(ComputedStyleTest, InitialVariableNamesEmpty) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
EXPECT_TRUE(style->GetVariableNames().IsEmpty());
}
TEST(ComputedStyleTest, InitialVariableNames) {
using css_test_helpers::CreateLengthRegistration;
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
PropertyRegistry* registry = MakeGarbageCollected<PropertyRegistry>();
registry->RegisterProperty("--x", *CreateLengthRegistration("--x", 1));
registry->RegisterProperty("--y", *CreateLengthRegistration("--y", 2));
style->SetInitialData(StyleInitialData::Create(*registry));
EXPECT_EQ(2u, style->GetVariableNames().size());
EXPECT_TRUE(style->GetVariableNames().Contains("--x"));
EXPECT_TRUE(style->GetVariableNames().Contains("--y"));
}
TEST(ComputedStyleTest, InheritedVariableNames) {
using css_test_helpers::CreateVariableData;
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
const bool inherited = true;
style->SetVariableData("--a", CreateVariableData("foo"), inherited);
style->SetVariableData("--b", CreateVariableData("bar"), inherited);
EXPECT_EQ(2u, style->GetVariableNames().size());
EXPECT_TRUE(style->GetVariableNames().Contains("--a"));
EXPECT_TRUE(style->GetVariableNames().Contains("--b"));
}
TEST(ComputedStyleTest, NonInheritedVariableNames) {
using css_test_helpers::CreateVariableData;
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
const bool inherited = true;
style->SetVariableData("--a", CreateVariableData("foo"), !inherited);
style->SetVariableData("--b", CreateVariableData("bar"), !inherited);
EXPECT_EQ(2u, style->GetVariableNames().size());
EXPECT_TRUE(style->GetVariableNames().Contains("--a"));
EXPECT_TRUE(style->GetVariableNames().Contains("--b"));
}
TEST(ComputedStyleTest, InheritedAndNonInheritedVariableNames) {
using css_test_helpers::CreateVariableData;
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
const bool inherited = true;
style->SetVariableData("--a", CreateVariableData("foo"), inherited);
style->SetVariableData("--b", CreateVariableData("bar"), inherited);
style->SetVariableData("--d", CreateVariableData("foz"), !inherited);
style->SetVariableData("--c", CreateVariableData("baz"), !inherited);
EXPECT_EQ(4u, style->GetVariableNames().size());
EXPECT_TRUE(style->GetVariableNames().Contains("--a"));
EXPECT_TRUE(style->GetVariableNames().Contains("--b"));
EXPECT_TRUE(style->GetVariableNames().Contains("--c"));
EXPECT_TRUE(style->GetVariableNames().Contains("--d"));
}
TEST(ComputedStyleTest, InitialAndInheritedAndNonInheritedVariableNames) {
using css_test_helpers::CreateLengthRegistration;
using css_test_helpers::CreateVariableData;
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
PropertyRegistry* registry = MakeGarbageCollected<PropertyRegistry>();
registry->RegisterProperty("--b", *CreateLengthRegistration("--b", 1));
registry->RegisterProperty("--e", *CreateLengthRegistration("--e", 2));
style->SetInitialData(StyleInitialData::Create(*registry));
const bool inherited = true;
style->SetVariableData("--a", CreateVariableData("foo"), inherited);
style->SetVariableData("--b", CreateVariableData("bar"), inherited);
style->SetVariableData("--d", CreateVariableData("foz"), !inherited);
style->SetVariableData("--c", CreateVariableData("baz"), !inherited);
EXPECT_EQ(5u, style->GetVariableNames().size());
EXPECT_TRUE(style->GetVariableNames().Contains("--a"));
EXPECT_TRUE(style->GetVariableNames().Contains("--b"));
EXPECT_TRUE(style->GetVariableNames().Contains("--c"));
EXPECT_TRUE(style->GetVariableNames().Contains("--d"));
EXPECT_TRUE(style->GetVariableNames().Contains("--e"));
}
TEST(ComputedStyleTest, BorderWidthZoom) {
std::unique_ptr<DummyPageHolder> dummy_page_holder_ =
std::make_unique<DummyPageHolder>(IntSize(0, 0), nullptr);
const ComputedStyle* initial = &ComputedStyle::InitialStyle();
StyleResolverState state(dummy_page_holder_->GetDocument(),
*dummy_page_holder_->GetDocument().documentElement(),
initial, initial);
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
style->SetEffectiveZoom(2);
style->SetBorderLeftStyle(EBorderStyle::kSolid);
style->SetOutlineStyle(EBorderStyle::kSolid);
style->SetColumnRuleStyle(EBorderStyle::kSolid);
state.SetStyle(style);
const struct {
CSSIdentifierValue* css_value;
double expected_px;
STACK_ALLOCATED();
} tests[] = {
{CSSIdentifierValue::Create(CSSValueID::kThin), 1.0},
{CSSIdentifierValue::Create(CSSValueID::kMedium), 3.0},
{CSSIdentifierValue::Create(CSSValueID::kThick), 5.0},
};
for (const auto& test : tests) {
for (const auto* property :
{&GetCSSPropertyBorderLeftWidth(), &GetCSSPropertyOutlineWidth(),
&GetCSSPropertyColumnRuleWidth()}) {
const Longhand& longhand = To<Longhand>(*property);
longhand.ApplyValue(state, *test.css_value);
auto* computed_value = longhand.CSSValueFromComputedStyleInternal(
*style, nullptr /* layout_object */, false /* allow_visited_style */);
AtomicString prop_name = longhand.GetCSSPropertyName().ToAtomicString();
ASSERT_TRUE(computed_value) << prop_name;
auto* numeric_value = DynamicTo<CSSNumericLiteralValue>(computed_value);
ASSERT_TRUE(numeric_value) << prop_name;
EXPECT_TRUE(numeric_value->IsPx()) << prop_name;
EXPECT_EQ(test.expected_px, numeric_value->DoubleValue()) << prop_name;
}
}
}
TEST(ComputedStyleTest, TextDecorationEqualDoesNotRequireRecomputeInkOverflow) {
using css_test_helpers::ParseDeclarationBlock;
std::unique_ptr<DummyPageHolder> dummy_page_holder_ =
std::make_unique<DummyPageHolder>(IntSize(0, 0), nullptr);
const ComputedStyle* initial = &ComputedStyle::InitialStyle();
StyleResolverState state(dummy_page_holder_->GetDocument(),
*dummy_page_holder_->GetDocument().documentElement(),
initial, initial);
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
// Set up the initial text decoration properties
style->SetTextDecorationStyle(ETextDecorationStyle::kSolid);
style->SetTextDecorationColor(StyleColor(CSSValueID::kGreen));
style->SetTextDecoration(TextDecoration::kUnderline);
style->SetTextDecorationThickness(
TextDecorationThickness(Length(5, Length::Type::kFixed)));
style->SetTextUnderlineOffset(Length(2, Length::Type::kFixed));
style->SetTextUnderlinePosition(kTextUnderlinePositionUnder);
state.SetStyle(style);
StyleAdjuster::AdjustComputedStyle(state, nullptr /* element */);
EXPECT_EQ(TextDecoration::kUnderline, style->TextDecorationsInEffect());
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
StyleDifference diff1;
style->UpdatePropertySpecificDifferences(*other, diff1);
EXPECT_FALSE(diff1.NeedsRecomputeVisualOverflow());
// Change the color, and it should not invalidate
other->SetTextDecorationColor(StyleColor(CSSValueID::kBlue));
state.SetStyle(other);
StyleAdjuster::AdjustComputedStyle(state, nullptr /* element */);
StyleDifference diff2;
style->UpdatePropertySpecificDifferences(*other, diff2);
EXPECT_FALSE(diff2.NeedsRecomputeVisualOverflow());
}
TEST(ComputedStyleTest, TextDecorationNotEqualRequiresRecomputeInkOverflow) {
using css_test_helpers::ParseDeclarationBlock;
std::unique_ptr<DummyPageHolder> dummy_page_holder_ =
std::make_unique<DummyPageHolder>(IntSize(0, 0), nullptr);
const ComputedStyle* initial = &ComputedStyle::InitialStyle();
StyleResolverState state(dummy_page_holder_->GetDocument(),
*dummy_page_holder_->GetDocument().documentElement(),
initial, initial);
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
// Set up the initial text decoration properties
style->SetTextDecorationStyle(ETextDecorationStyle::kSolid);
style->SetTextDecorationColor(StyleColor(CSSValueID::kGreen));
style->SetTextDecoration(TextDecoration::kUnderline);
style->SetTextDecorationThickness(
TextDecorationThickness(Length(5, Length::Type::kFixed)));
style->SetTextUnderlineOffset(Length(2, Length::Type::kFixed));
style->SetTextUnderlinePosition(kTextUnderlinePositionUnder);
state.SetStyle(style);
StyleAdjuster::AdjustComputedStyle(state, nullptr /* element */);
// Change decoration style
scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
other->SetTextDecorationStyle(ETextDecorationStyle::kWavy);
state.SetStyle(other);
StyleAdjuster::AdjustComputedStyle(state, nullptr /* element */);
StyleDifference diff_decoration_style;
style->UpdatePropertySpecificDifferences(*other, diff_decoration_style);
EXPECT_TRUE(diff_decoration_style.NeedsRecomputeVisualOverflow());
// Change decoration line
other = ComputedStyle::Clone(*style);
other->SetTextDecoration(TextDecoration::kOverline);
state.SetStyle(other);
StyleAdjuster::AdjustComputedStyle(state, nullptr /* element */);
StyleDifference diff_decoration_line;
style->UpdatePropertySpecificDifferences(*other, diff_decoration_line);
EXPECT_TRUE(diff_decoration_line.NeedsRecomputeVisualOverflow());
// Change decoration thickness
other = ComputedStyle::Clone(*style);
other->SetTextDecorationThickness(
TextDecorationThickness(Length(3, Length::Type::kFixed)));
state.SetStyle(other);
StyleAdjuster::AdjustComputedStyle(state, nullptr /* element */);
StyleDifference diff_decoration_thickness;
style->UpdatePropertySpecificDifferences(*other, diff_decoration_thickness);
EXPECT_TRUE(diff_decoration_thickness.NeedsRecomputeVisualOverflow());
// Change underline offset
other = ComputedStyle::Clone(*style);
other->SetTextUnderlineOffset(Length(4, Length::Type::kFixed));
state.SetStyle(other);
StyleAdjuster::AdjustComputedStyle(state, nullptr /* element */);
StyleDifference diff_underline_offset;
style->UpdatePropertySpecificDifferences(*other, diff_underline_offset);
EXPECT_TRUE(diff_underline_offset.NeedsRecomputeVisualOverflow());
// Change underline position
other = ComputedStyle::Clone(*style);
other->SetTextUnderlinePosition(kTextUnderlinePositionLeft);
state.SetStyle(other);
StyleAdjuster::AdjustComputedStyle(state, nullptr /* element */);
StyleDifference diff_underline_position;
style->UpdatePropertySpecificDifferences(*other, diff_underline_position);
EXPECT_TRUE(diff_underline_position.NeedsRecomputeVisualOverflow());
}
// Verify that cloned ComputedStyle is independent from source, i.e.
// copy-on-write works as expected.
TEST(ComputedStyleTest, ClonedStyleAnimationsAreIndependent) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
auto& animations = style->AccessAnimations();
animations.DelayList().clear();
animations.DelayList().push_back(CSSAnimationData::InitialDelay());
EXPECT_EQ(1u, style->Animations()->DelayList().size());
scoped_refptr<ComputedStyle> cloned_style = ComputedStyle::Clone(*style);
auto& cloned_style_animations = cloned_style->AccessAnimations();
EXPECT_EQ(1u, cloned_style_animations.DelayList().size());
cloned_style_animations.DelayList().push_back(
CSSAnimationData::InitialDelay());
EXPECT_EQ(2u, cloned_style->Animations()->DelayList().size());
EXPECT_EQ(1u, style->Animations()->DelayList().size());
}
TEST(ComputedStyleTest, ClonedStyleTransitionsAreIndependent) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
auto& transitions = style->AccessTransitions();
transitions.PropertyList().clear();
transitions.PropertyList().push_back(CSSTransitionData::InitialProperty());
EXPECT_EQ(1u, style->Transitions()->PropertyList().size());
scoped_refptr<ComputedStyle> cloned_style = ComputedStyle::Clone(*style);
auto& cloned_style_transitions = cloned_style->AccessTransitions();
EXPECT_EQ(1u, cloned_style_transitions.PropertyList().size());
cloned_style_transitions.PropertyList().push_back(
CSSTransitionData::InitialProperty());
EXPECT_EQ(2u, cloned_style->Transitions()->PropertyList().size());
EXPECT_EQ(1u, style->Transitions()->PropertyList().size());
}
TEST(ComputedStyleTest, ApplyInitialAnimationNameAndTransitionProperty) {
std::unique_ptr<DummyPageHolder> dummy_page_holder_ =
std::make_unique<DummyPageHolder>(IntSize(0, 0), nullptr);
const ComputedStyle* initial = &ComputedStyle::InitialStyle();
StyleResolverState state(dummy_page_holder_->GetDocument(),
*dummy_page_holder_->GetDocument().documentElement(),
initial, initial);
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
state.SetStyle(style);
EXPECT_FALSE(style->Animations());
EXPECT_FALSE(style->Transitions());
To<Longhand>(GetCSSPropertyAnimationName()).ApplyInitial(state);
To<Longhand>(GetCSSPropertyTransitionProperty()).ApplyInitial(state);
EXPECT_FALSE(style->Animations());
EXPECT_FALSE(style->Transitions());
}
#define TEST_STYLE_VALUE_NO_DIFF(field_name) \
{ \
scoped_refptr<ComputedStyle> style1 = ComputedStyle::Create(); \
scoped_refptr<ComputedStyle> style2 = ComputedStyle::Create(); \
style1->Set##field_name( \
ComputedStyleInitialValues::Initial##field_name()); \
style2->Set##field_name( \
ComputedStyleInitialValues::Initial##field_name()); \
auto diff = style1->VisualInvalidationDiff(*document, *style2); \
EXPECT_FALSE(diff.HasDifference()); \
}
// Ensures ref-counted values are compared by their values, not by pointers.
#define TEST_STYLE_REFCOUNTED_VALUE_NO_DIFF(type, field_name) \
{ \
scoped_refptr<ComputedStyle> style1 = ComputedStyle::Create(); \
scoped_refptr<ComputedStyle> style2 = ComputedStyle::Create(); \
scoped_refptr<type> value1 = base::MakeRefCounted<type>(); \
scoped_refptr<type> value2 = base::MakeRefCounted<type>(value1->data); \
style1->Set##field_name(value1); \
style2->Set##field_name(value2); \
auto diff = style1->VisualInvalidationDiff(*document, *style2); \
EXPECT_FALSE(diff.HasDifference()); \
}
TEST(ComputedStyleTest, SvgStrokeStyleShouldCompareValue) {
Persistent<Document> document = Document::CreateForTest();
TEST_STYLE_VALUE_NO_DIFF(StrokeOpacity);
TEST_STYLE_VALUE_NO_DIFF(StrokeMiterLimit);
TEST_STYLE_VALUE_NO_DIFF(StrokeWidth);
TEST_STYLE_VALUE_NO_DIFF(StrokeDashOffset);
TEST_STYLE_REFCOUNTED_VALUE_NO_DIFF(SVGDashArray, StrokeDashArray);
TEST_STYLE_VALUE_NO_DIFF(StrokePaint);
TEST_STYLE_VALUE_NO_DIFF(InternalVisitedStrokePaint);
}
TEST(ComputedStyleTest, SvgMiscStyleShouldCompareValue) {
Persistent<Document> document = Document::CreateForTest();
TEST_STYLE_VALUE_NO_DIFF(FloodColor);
TEST_STYLE_VALUE_NO_DIFF(FloodOpacity);
TEST_STYLE_VALUE_NO_DIFF(LightingColor);
TEST_STYLE_VALUE_NO_DIFF(BaselineShift);
}
} // namespace blink