[css-variables] Clone style when there are no variable references
When resolving the CSS Custom Properties inheritance, we recalculate all
the styles of nodes in the hierarchy. Following a similar approach to
the one used for 'independent properties', we can just clone the node's
parent style and propagate just the InheritedVariables data structure.
We'll only recalc the styles if a node has any variable reference,
defined as :var(--*), to a custom property.
Bug: 1056209
Change-Id: I81081e11cd69f06e69a6030b97fabce3a50630b3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2111172
Commit-Queue: Javier Fernandez <jfernandez@igalia.com>
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/master@{#765278}
diff --git a/third_party/blink/renderer/build/scripts/core/style/computed_style_fields.py b/third_party/blink/renderer/build/scripts/core/style/computed_style_fields.py
index 6de3f6a3..3f2199b 100644
--- a/third_party/blink/renderer/build/scripts/core/style/computed_style_fields.py
+++ b/third_party/blink/renderer/build/scripts/core/style/computed_style_fields.py
@@ -180,6 +180,8 @@
if not self.is_inherited_flag:
self.is_inherited = kwargs.pop('inherited')
self.is_independent = kwargs.pop('independent')
+ self.is_semi_independent_variable = kwargs.pop(
+ 'semi_independent_variable')
assert self.is_inherited or not self.is_independent, \
'Only inherited fields can be independent'
diff --git a/third_party/blink/renderer/build/scripts/core/style/make_computed_style_base.py b/third_party/blink/renderer/build/scripts/core/style/make_computed_style_base.py
index 0c50e29..f929043 100755
--- a/third_party/blink/renderer/build/scripts/core/style/make_computed_style_base.py
+++ b/third_party/blink/renderer/build/scripts/core/style/make_computed_style_base.py
@@ -276,6 +276,7 @@
property_name=property_['name'].original,
inherited=property_['inherited'],
independent=property_['independent'],
+ semi_independent_variable=property_['semi_independent_variable'],
type_name=property_['type_name'],
wrapper_pointer_name=property_['wrapper_pointer_name'],
field_template=property_['field_template'],
diff --git a/third_party/blink/renderer/build/scripts/core/style/templates/computed_style_base.h.tmpl b/third_party/blink/renderer/build/scripts/core/style/templates/computed_style_base.h.tmpl
index eeaaa05..1b01069 100644
--- a/third_party/blink/renderer/build/scripts/core/style/templates/computed_style_base.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/style/templates/computed_style_base.h.tmpl
@@ -86,6 +86,7 @@
|selectattr("is_property")
|selectattr("is_inherited")
|selectattr("is_independent")
+ |rejectattr("is_semi_independent_variable")
|list
)|indent(8)}}
);
@@ -97,6 +98,19 @@
|selectattr("is_property")
|selectattr("is_inherited")
|rejectattr("is_independent")
+ |rejectattr("is_semi_independent_variable")
+ |list
+ )|indent(8)}}
+ );
+ }
+
+ inline bool InheritedVariablesEqual(const ComputedStyleBase& o) const {
+ return (
+ {{fieldwise_compare(computed_style, computed_style.all_fields
+ |selectattr("is_property")
+ |selectattr("is_inherited")
+ |rejectattr("is_independent")
+ |selectattr("is_semi_independent_variable")
|list
)|indent(8)}}
);
@@ -129,6 +143,9 @@
void InheritFrom(const ComputedStyleBase& other,
IsAtShadowBoundary isAtShadowBoundary);
+ void InheritCustomPropertiesFrom(const ComputedStyleBase& other) {
+ inherited_variables_data_ = other.inherited_variables_data_;
+ }
void CopyNonInheritedFromCached(
const ComputedStyleBase& other);
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 2cb7fec7..cfce5ef 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -110,6 +110,16 @@
valid_type: "bool",
},
+ // - semi_independent_variable
+ // This property affects to the {Inherited, NonInherited}Variable data fields so that we
+ // can assume that the custom properties might not depend on any other property. We can
+ // handle these properties so that they are excluded from the shared Inherited/NohInherited
+ // logic, like the Equal and inheritance functions.
+ semi_independent_variable: {
+ default: false,
+ valid_type: "bool",
+ },
+
// - affected_by_all
// The affected_by_all flag indicates whether a change to the CSS property
// "all" affects this property.
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade.cc b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
index 62120d0..906e723 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
@@ -583,6 +583,8 @@
if (HasFontSizeDependency(To<CustomProperty>(property), data.get()))
resolver.DetectCycle(GetCSSPropertyFontSize());
+ state_.Style()->SetHasVariableDeclaration();
+
if (resolver.InCycle())
return CSSInvalidVariableValue::Create();
@@ -885,6 +887,7 @@
void StyleCascade::MarkHasVariableReference(const CSSProperty& property) {
if (!property.IsInherited())
state_.Style()->SetHasVariableReferenceFromNonInheritedProperty();
+ state_.Style()->SetHasVariableReference();
}
const Document& StyleCascade::GetDocument() const {
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 103e61d..3dcea60 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -3166,7 +3166,8 @@
const ComputedStyle* parent_style = ParentComputedStyle();
DCHECK(parent_style);
const ComputedStyle* style = GetComputedStyle();
- if (!style || style->Animations() || style->Transitions())
+ if (!style || style->Animations() || style->Transitions() ||
+ style->HasVariableReference() || style->HasVariableDeclaration())
return nullptr;
scoped_refptr<ComputedStyle> new_style = ComputedStyle::Clone(*style);
new_style->PropagateIndependentInheritedProperties(*parent_style);
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index c8dbf5a..431fcbd3 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -102,7 +102,7 @@
}
private:
- void* data_refs[7];
+ void* data_refs[8];
unsigned bitfields[5];
};
@@ -291,8 +291,16 @@
if (!non_inherited_equal && old_style.HasExplicitlyInheritedProperties()) {
return Difference::kInherited;
}
- if (!old_style.IndependentInheritedEqual(new_style))
+ bool variables_independent = RuntimeEnabledFeatures::CSSCascadeEnabled() &&
+ !old_style.HasVariableReference() &&
+ !old_style.HasVariableDeclaration();
+ bool inherited_variables_equal = old_style.InheritedVariablesEqual(new_style);
+ if (!inherited_variables_equal && !variables_independent)
+ return Difference::kInherited;
+ if (!old_style.IndependentInheritedEqual(new_style) ||
+ !inherited_variables_equal) {
return Difference::kIndependentInherited;
+ }
if (non_inherited_equal) {
DCHECK(old_style == new_style);
if (PseudoElementStylesEqual(old_style, new_style))
@@ -312,6 +320,8 @@
void ComputedStyle::PropagateIndependentInheritedProperties(
const ComputedStyle& parent_style) {
ComputedStyleBase::PropagateIndependentInheritedProperties(parent_style);
+ if (!HasVariableReference() && !HasVariableDeclaration())
+ InheritCustomPropertiesFrom(parent_style);
}
StyleSelfAlignmentData ResolvedSelfAlignment(
diff --git a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5 b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
index 8bbe5782..14e9626 100644
--- a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
+++ b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
@@ -176,6 +176,20 @@
custom_copy: true,
custom_compare: true,
},
+ // A property references a variable that needs to be resolved
+ {
+ name: "HasVariableReference",
+ field_template: "monotonic_flag",
+ default_value: "false",
+ custom_compare: true,
+ },
+ // A property which value consists of a custom property declaration.
+ {
+ name: "HasVariableDeclaration",
+ field_template: "monotonic_flag",
+ default_value: "false",
+ custom_compare: true,
+ },
// Explicitly inherits a non-inherited property
{
name: "HasExplicitlyInheritedProperties",
@@ -561,12 +575,13 @@
{
name: "InheritedVariables",
inherited: true,
+ semi_independent_variable: true,
field_template: "external",
type_name: "StyleInheritedVariables",
include_paths: ["third_party/blink/renderer/core/style/style_inherited_variables.h"],
default_value: "nullptr",
wrapper_pointer_name: "scoped_refptr",
- field_group: "*",
+ field_group: "InheritedVariables",
computed_style_custom_functions: ["getter", "setter"],
},
{
diff --git a/third_party/blink/renderer/core/style/computed_style_test.cc b/third_party/blink/renderer/core/style/computed_style_test.cc
index 074d1e4..17f5b22 100644
--- a/third_party/blink/renderer/core/style/computed_style_test.cc
+++ b/third_party/blink/renderer/core/style/computed_style_test.cc
@@ -527,6 +527,134 @@
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) {
ScopedCSSColorSchemeForTest scoped_property_enabled(true);
ScopedCSSColorSchemeUARenderingForTest scoped_ua_enabled(true);
diff --git a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/registered-property-change-style-001.html b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/registered-property-change-style-001.html
new file mode 100644
index 0000000..320b44d8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/registered-property-change-style-001.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Variables Test: Style changes on registered properties using variables</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="http://www.w3.org/TR/css-variables-1/#using-variables">
+<meta name="assert" content="A change in the custom property declaration must be propagated to all the descendants">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="outer">
+ <div id="inbetween">
+ <div id="inner"></div>
+ </div>
+</div>
+<script>
+ "use strict";
+ test( function () {
+ outer.style.cssText = '';
+ inbetween.style.cssText = '';
+ inner.style.cssText = 'color: var(--color1)';
+ let initialValue = getComputedStyle(inner).getPropertyValue('color');
+ assert_equals(initialValue, "rgb(0, 0, 0)", "Initial value");
+
+ inbetween.style.cssText = 'color: green';
+ let inheritedValue = getComputedStyle(inner).getPropertyValue('color');
+ assert_equals(inheritedValue, "rgb(0, 128, 0)", "Inherited value");
+
+ CSS.registerProperty({name: '--color1', syntax: '<color>', initialValue: 'red', inherits: true});
+ let actualValue = getComputedStyle(inner).getPropertyValue('color');
+ assert_equals(actualValue, "rgb(255, 0, 0)", "Resolved value");
+ }, "New registered property declaration");
+
+ test( function () {
+ outer.style.cssText = '';
+ inbetween.style.cssText = '';
+ inner.style.cssText = 'color: var(--color2)';
+ let initialValue = getComputedStyle(inner).getPropertyValue('color');
+ assert_equals(initialValue, "rgb(0, 0, 0)", "Initial value");
+
+ outer.style.cssText = '--color2: blue';
+ inbetween.style.cssText = 'color: green';
+ let resolvedValue = getComputedStyle(inner).getPropertyValue('color');
+ assert_equals(resolvedValue, "rgb(0, 0, 255)", "Resolved value");
+
+ outer.style.cssText = '';
+ CSS.registerProperty({name: '--color2', syntax: '<color>', initialValue: 'red', inherits: true});
+ let actualValue = getComputedStyle(inner).getPropertyValue('color');
+ assert_equals(actualValue, "rgb(255, 0, 0)", "Resolved value");
+ }, "Registered property overrides a previous declaration ");
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-variables/css-variable-change-style-001.html b/third_party/blink/web_tests/external/wpt/css/css-variables/css-variable-change-style-001.html
new file mode 100644
index 0000000..798c772
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-variables/css-variable-change-style-001.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Variables Test: Style changes on properties using variables</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="http://www.w3.org/TR/css-variables-1/#using-variables">
+<meta name="assert" content="A change in the custom property declaration must be propagated to all the descendants">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ .inner {
+ color: var(--x);
+ background-color: var(--x);
+ white-space: var(--x);
+ }
+</style>
+<div id="outer">
+ <div id="inbetween">
+ <div id="inner"></div>
+ </div>
+</div>
+<script>
+ "use strict";
+ let colorValues = [
+ { Id: "case1", outer: "red", inbetween: "", expected: "rgb(255, 0, 0)" },
+ { Id: "case2", outer: "red", inbetween: "blue", expected: "rgb(0, 0, 255)" },
+ { Id: "case3", outer: "green", inbetween: "blue", expected: "rgb(0, 0, 255)" },
+ { Id: "case4", outer: "green", inbetween: "", expected: "rgb(0, 128, 0)" },
+ { Id: "case5", outer: "green", inbetween: "red", expected: "rgb(255, 0, 0)" },
+ { Id: "case6", outer: "" , inbetween: "red", expected: "rgb(255, 0, 0)" },
+ { Id: "case7", outer: "blue" , inbetween: "" , expected: "rgb(0, 0, 255)" },
+ ];
+
+ let whiteSpaceValues = [
+ { Id: "case1", outer: "pre", inbetween: "", expected: "pre" },
+ { Id: "case2", outer: "pre-wrap", inbetween: "", expected: "pre-wrap" },
+ { Id: "case3", outer: "pre-wrap", inbetween: "nowrap", expected: "nowrap" },
+ { Id: "case3", outer: "pre-wrap", inbetween: "", expected: "pre-wrap" },
+ { Id: "case4", outer: "pre-line", inbetween: "normal", expected: "normal" },
+ { Id: "case5", outer: "pre-line", inbetween: "", expected: "pre-line" },
+ { Id: "case6", outer: "", inbetween: "pre-wrap", expected: "pre-wrap" },
+ { Id: "case7", outer: "", inbetween: "", expected: "normal" },
+ ];
+
+ let testcases = [
+ { property: "color", values: colorValues, },
+ { property: "background-color", values: colorValues, },
+ { property: "white-space", values: whiteSpaceValues },
+ ];
+
+ function initializeStyles() {
+ outer.style.cssText = "";
+ inbetween.style.cssText = "";
+ inner.style.cssText = "";
+ }
+
+ testcases.forEach(function (testcase) {
+ test( function () {
+ initializeStyles();
+ inner.style.cssText = testcase.property + ': var(--x)';
+ testcase.values.forEach(function (value) {
+ outer.style.cssText = "--x:" + value.outer;
+ inbetween.style.cssText = "--x:" + value.inbetween;
+ let computedStyle = getComputedStyle(inner);
+ let actualValue = computedStyle.getPropertyValue(testcase.property);
+ assert_equals(actualValue, value.expected, value.Id);
+ });
+ }, "Test declaration changes on '" + testcase.property + "' as variable");
+
+ test( function () {
+ initializeStyles();
+ inbetween.style.cssText = testcase.property + ': inherit';
+ inner.style.cssText = testcase.property + ': inherit';
+ testcase.values.forEach(function (value) {
+ outer.style.cssText = "--x:" + value.outer + "; " + testcase.property + ": " + value.outer;
+ let actualValue = getComputedStyle(inner).getPropertyValue(testcase.property);
+ let expectedValue = getComputedStyle(outer).getPropertyValue(testcase.property);
+ assert_equals(actualValue, expectedValue, value.Id);
+ });
+ }, "Avoid masking differences on '" + testcase.property + "' due to declaration changes");
+
+ test( function () {
+ initializeStyles();
+ inbetween.style.cssText = testcase.property + ': inherit';
+ inner.style.cssText = testcase.property + ': inherit';
+ let value1 = testcase.values[0];
+ let value2 = testcase.values[3];
+ outer.style.cssText = "--x:" + value2.outer + "; " + testcase.property + ": " + value1.outer;
+ let actualValue = getComputedStyle(inner).getPropertyValue(testcase.property);
+ assert_equals(actualValue, value1.expected, value1.Id);
+
+ inner.style.cssText = testcase.property + ': var(--x)';
+ actualValue = getComputedStyle(inner).getPropertyValue(testcase.property);
+ assert_equals(actualValue, value2.expected, value2.Id);
+ }, "Test changing '" + testcase.property + "' value to become a css variable");
+ });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-variables/css-variable-change-style-002.html b/third_party/blink/web_tests/external/wpt/css/css-variables/css-variable-change-style-002.html
new file mode 100644
index 0000000..9057136
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-variables/css-variable-change-style-002.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Variables Test: Style changes on properties using variables</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="http://www.w3.org/TR/css-variables-1/#using-variables">
+<meta name="assert" content="A change in the custom property declaration must be propagated to all the descendants">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ .test1 > div > div { color: var(--x); }
+ .test2 > div > div { background-color: var(--x); }
+ .test3 > div > div { white-space: var(--x); }
+</style>
+<div id="outer">
+ <div>
+ <div id="inner1"></div>
+ </div>
+ <div>
+ <div id="inner2"></div>
+ </div>
+ <div>
+ <div id="inner3"></div>
+ </div>
+</div>
+<script>
+ "use strict";
+
+ let colorValues = [
+ { Id: "case1", value: "red", expected: "rgb(255, 0, 0)" },
+ { Id: "case2", value: "green", expected: "rgb(0, 128, 0)" },
+ ];
+ let whiteSpaceValues = [
+ { Id: "case1", value: "pre-wrap", expected: "pre-wrap" },
+ { Id: "case2", value: "nowrap", expected: "nowrap" },
+ ];
+ let testcases = [
+ { property: "color", className: "test1", values: colorValues, },
+ { property: "background-color", className: "test2", values: colorValues, },
+ { property: "white-space", className: "test3", values: whiteSpaceValues},
+ ];
+
+ testcases.forEach(function (testcase) {
+ test( function () {
+ outer.className = testcase.className;
+ testcase.values.forEach(function (entry) {
+ document.body.style.cssText = "--x: " + entry.value;
+ let actualValue = getComputedStyle(inner1).getPropertyValue(testcase.property);
+ assert_equals(actualValue, entry.expected, entry.Id + "-1");
+ actualValue = getComputedStyle(inner2).getPropertyValue(testcase.property);
+ assert_equals(actualValue, entry.expected, entry.Id + "-2");
+ actualValue = getComputedStyle(inner3).getPropertyValue(testcase.property);
+ assert_equals(actualValue, entry.expected, entry.Id + "-3");
+ });
+ }, "Declaration changes on '" + testcase.property + "' propagate to all variable references");
+ });
+</script>