[css-variables] Custom props with invalid var() should behave as 'unset'

We currently have a (WPT-enforced) bug where custom properties that
reference guaranteed-invalid values [1] behave themselves as guaranteed-
invalid. This is not correct per spec, which requires the custom
property to behave as 'unset' in this case. This was clarified in [2],
although the spec has described this behavior long before that.

Unfortunately Firefox has the same issue. Safari on the other hand, does
implement it correctly.

[1] https://drafts.csswg.org/css-variables/#guaranteed-invalid
[2] https://github.com/w3c/csswg-drafts/issues/4075

Bug: 980930
Change-Id: I84a0da3aad6b72b574009d560eb868632769098a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2108026
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: Xiaocheng Hu <xiaochengh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#751636}
diff --git a/css/css-env/env-in-custom-properties.tentative.html b/css/css-env/env-in-custom-properties.tentative.html
index ea471b0..6dadcc5 100644
--- a/css/css-env/env-in-custom-properties.tentative.html
+++ b/css/css-env/env-in-custom-properties.tentative.html
@@ -6,18 +6,30 @@
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <style>
-      body {
+      #parent {
+        --var1: inherited;
+      }
+      #child {
         --my-width: env(test, 100px);
         width: var(--my-width);
+        --var1: env(nonexistent);
       }
     </style>
   </head>
   <body>
+    <div id="parent">
+      <div id="child"></div>
+    </div>
     <script>
     test(() => {
-      const style = window.getComputedStyle(document.body);
+      const style = window.getComputedStyle(child);
       assert_equals(style.getPropertyValue("width"), "100px");
-    });
+    }, 'env() is substituted into a custom property');
+
+    test(() => {
+      const style = window.getComputedStyle(child);
+      assert_equals(style.getPropertyValue("--var1"), " inherited");
+    }, 'Substitution of unrecognized env() causes unset');
     </script>
   </body>
 </html>
diff --git a/css/css-variables/variable-substitution-variable-declaration.html b/css/css-variables/variable-substitution-variable-declaration.html
index 0b0ab1f..678d05a 100644
--- a/css/css-variables/variable-substitution-variable-declaration.html
+++ b/css/css-variables/variable-substitution-variable-declaration.html
@@ -140,7 +140,7 @@
 
             { element: "target10",      propertyName: "--varA",         expectedPropertyValue: "" },
             { element: "target10",      propertyName: "--varB",         expectedPropertyValue: "" },
-            { element: "target10",      propertyName: "--varC",         expectedPropertyValue: "" },
+            { element: "target10",      propertyName: "--varC",         expectedPropertyValue: " another good one" },
         ];
 
         testcases.forEach(function (testcase) {
diff --git a/css/css-variables/variables-substitute-guaranteed-invalid.html b/css/css-variables/variables-substitute-guaranteed-invalid.html
new file mode 100644
index 0000000..c30e874
--- /dev/null
+++ b/css/css-variables/variables-substitute-guaranteed-invalid.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>Testing substitution of guaranteed-invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-variables/#guaranteed-invalid">
+<link rel="help" href="https://drafts.csswg.org/css-variables/#cycles">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+    #target1 {
+        /* Cycle */
+        --var1: var(--var2);
+        --var2: var(--var1);
+
+        /* Reference to cycle */
+        --var3: var(--var1);
+
+        /* Reference to non-existent property */
+        --var4: var(--noexist);
+    }
+
+    #target1parent {
+        --var3: inherited;
+        --var4: inherited;
+    }
+</style>
+<div id="target1parent">
+    <div id="target1"></div>
+</div>
+<script>
+    test( function () {
+        let cs = getComputedStyle(target1);
+        assert_equals(cs.getPropertyValue('--var1'), '');
+        assert_equals(cs.getPropertyValue('--var2'), '');
+    }, 'Custom properties in a cycle are guaranteed-invalid');
+
+    test( function () {
+        let cs = getComputedStyle(target1);
+        assert_equals(cs.getPropertyValue('--var3'), ' inherited');
+    }, 'A custom property referencing a cycle is treated as unset');
+
+    test( function () {
+        let cs = getComputedStyle(target1);
+        assert_equals(cs.getPropertyValue('--var4'), ' inherited');
+    }, 'A custom property referencing a non-existent variable is treated as unset');
+</script>