[css-typed-om] Rejig CSSResourceValue hierarchy.

We implement the new CSSImageValue, which is an opaque object with no
constructor. We also had to change the testsuite.js a bit to test it.

Bug: 545318
Change-Id: I0ae719c5380e01ff9423e0ff0684afe547de1bb0
Reviewed-on: https://chromium-review.googlesource.com/934001
Commit-Queue: Darren Shen <shend@chromium.org>
Reviewed-by: nainar <nainar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#538730}
diff --git a/css/css-typed-om/resources/testhelper.js b/css/css-typed-om/resources/testhelper.js
index 91af64c..cae0e27 100644
--- a/css/css-typed-om/resources/testhelper.js
+++ b/css/css-typed-om/resources/testhelper.js
@@ -66,12 +66,6 @@
     case 'CSSMatrixComponent':
       assert_matrix_approx_equals(a.matrix, b.matrix, 1e-6);
       break;
-    case 'CSSURLImageValue':
-      assert_equals(a.instrinsicWidth, b.instrinsicWidth);
-      assert_equals(a.instrinsicHeight, b.instrinsicHeight);
-      assert_equals(a.instrinsicRatio, b.instrinsicRatio);
-      assert_equals(a.url, b.url);
-      break;
     default:
       assert_equals(a, b);
       break;
diff --git a/css/css-typed-om/stylevalue-normalization/normalize-image.html b/css/css-typed-om/stylevalue-normalization/normalize-image.html
new file mode 100644
index 0000000..0b60ec4
--- /dev/null
+++ b/css/css-typed-om/stylevalue-normalization/normalize-image.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSImageValue normalization tests</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#resourcevalue-normalization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testhelper.js"></script>
+<body>
+<script>
+'use strict';
+
+const gTestUrl = '/media/1x1-green.png';
+const gBadTestUrl = document.location.href;
+
+test(t => {
+  const result = CSSStyleValue.parse('background-image', 'url("' + gTestUrl + '")');
+  assert_class_string(result, 'CSSImageValue');
+}, 'Normalizing a valid <url> returns a CSSImageValue');
+
+test(t => {
+  const result = CSSStyleValue.parse('background-image', 'url("' + gBadTestUrl + '")');
+  assert_class_string(result, 'CSSImageValue');
+}, 'Normalizing a bad <url> returns a CSSImageValue');
+
+test(t => {
+  const result = CSSStyleValue.parse('background-image', 'linear-gradient(red, orange)');
+  assert_equals(result.constructor.name, 'CSSImageValue');
+}, 'Normalizing a <gradient> returns a CSSImageValue');
+
+</script>
diff --git a/css/css-typed-om/stylevalue-normalization/normalize-resource.tentative.html b/css/css-typed-om/stylevalue-normalization/normalize-resource.tentative.html
deleted file mode 100644
index a972277..0000000
--- a/css/css-typed-om/stylevalue-normalization/normalize-resource.tentative.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>CSSResourceValue normalization tests</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#resourcevalue-normalization">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="../resources/testhelper.js"></script>
-<body>
-<script>
-'use strict';
-
-const gTestUrl = '/media/1x1-green.png';
-const gBadTestUrl = document.location.href;
-
-async_test(t => {
-  const result = CSSStyleValue.parse('background-image', 'url("' + gTestUrl + '")');
-  assert_equals(result.constructor.name, 'CSSURLImageValue');
-  assert_equals(result.intrinsicWidth, null);
-  assert_equals(result.intrinsicHeight, null);
-  assert_equals(result.intrinsicRatio, null);
-  assert_equals(result.state, 'unloaded');
-
-  let image = loadImageResource(t, result);
-  image.addEventListener('load', t.step_func_done(() => {
-    assert_equals(result.url, gTestUrl);
-    assert_equals(result.state, 'loaded');
-    assert_equals(result.intrinsicWidth, 1);
-    assert_equals(result.intrinsicHeight, 1);
-    assert_equals(result.intrinsicRatio, 1);
-  }));
-}, 'Normalizing a valid <url> returns a CSSURLImageValue that eventually loads');
-
-async_test(t => {
-  const result = CSSStyleValue.parse('background-image', 'url("' + gBadTestUrl + '")');
-  assert_equals(result.constructor.name, 'CSSURLImageValue');
-
-  let image = loadImageResource(t, result);
-  image.addEventListener('error', t.step_func_done(() => {
-      assert_equals(result.url, gBadTestUrl);
-      assert_equals(result.state, 'error');
-      assert_equals(result.intrinsicWidth, null);
-      assert_equals(result.intrinsicHeight, null);
-      assert_equals(result.intrinsicRatio, null);
-  }));
-}, 'Normalizing a bad <url> returns a CSSURLImageValue in error state');
-
-test(t => {
-  const result = CSSStyleValue.parse('background-image', 'linear-gradient(red, orange)');
-  assert_equals(result.constructor.name, 'CSSImageValue');
-  assert_equals(result.state, 'loaded');
-  assert_equals(result.intrinsicWidth, null);
-  assert_equals(result.intrinsicHeight, null);
-  assert_equals(result.intrinsicRatio, null);
-}, 'Normalizing a <gradient> returns a CSSImageValue');
-
-</script>
diff --git a/css/css-typed-om/stylevalue-serialization/cssImageValue.html b/css/css-typed-om/stylevalue-serialization/cssImageValue.html
new file mode 100644
index 0000000..92256e5
--- /dev/null
+++ b/css/css-typed-om/stylevalue-serialization/cssImageValue.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSURLImageValue serialization tests</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#urlimagevalue-serialization">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testhelper.js"></script>
+<body>
+<div id="log"></div>
+<div id="testUrl" style="background-image: url('/media/1x1-green.png')"></div>
+<script>
+'use strict';
+
+test(() => {
+  const result = document.getElementById('testUrl').attributeStyleMap.get('background-image');
+  assert_equals(result.toString(), 'url("/media/1x1-green.png")');
+}, 'CSSUrlImageValue serializes correctly');
+
+</script>
diff --git a/css/css-typed-om/stylevalue-serialization/cssUrlImageValue.tentative.html b/css/css-typed-om/stylevalue-serialization/cssUrlImageValue.tentative.html
deleted file mode 100644
index c97ed0d..0000000
--- a/css/css-typed-om/stylevalue-serialization/cssUrlImageValue.tentative.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>IDL-constructed CSSURLImageValue serialization tests</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#urlimagevalue-serialization">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="../resources/testhelper.js"></script>
-<script>
-'use strict';
-
-test(() => {
-  assert_equals(new CSSURLImageValue('http://foo.bar').toString(), 'url("http://foo.bar")');
-}, 'CSSUrlImageValue constructed from IDL serializes correctly');
-
-</script>
diff --git a/css/css-typed-om/stylevalue-subclasses/cssUrlImageValue-interface.html b/css/css-typed-om/stylevalue-subclasses/cssUrlImageValue-interface.html
deleted file mode 100644
index ed86054..0000000
--- a/css/css-typed-om/stylevalue-subclasses/cssUrlImageValue-interface.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>CSSURLImageValue IDL</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#cssurlimagevalue">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/WebIDLParser.js"></script>
-<script src="/resources/idlharness.js"></script>
-<script type="text/plain" id="idl">
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(USVString url)]
-interface CSSURLImageValue : CSSImageValue {
-    readonly attribute USVString url;
-};
-</script>
-<script>
-'use strict';
-const idlArray = new IdlArray();
-idlArray.add_untested_idls('interface CSSImageValue { stringifier; };');
-idlArray.add_idls(document.getElementById('idl').textContent);
-idlArray.test();
-</script>
diff --git a/css/css-typed-om/stylevalue-subclasses/cssUrlImageValue-invalid.html b/css/css-typed-om/stylevalue-subclasses/cssUrlImageValue-invalid.html
deleted file mode 100644
index 8d6370a..0000000
--- a/css/css-typed-om/stylevalue-subclasses/cssUrlImageValue-invalid.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>CSSURLImageValue Error Handling</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#imagevalue-objects">
-<meta name="assert" content="Test CSSURLImageValue constructor error handling" />
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<body>
-<div id="log">
-<script>
-'use strict';
-
-test(() => {
-  assert_throws(new TypeError(), () => new CSSURLImageValue("file://:invalid url"));
-}, 'Constructing a CSSURLImageValue with an invalid URL throws a TypeError');
-
-</script>
diff --git a/css/css-typed-om/stylevalue-subclasses/cssUrlImageValue.html b/css/css-typed-om/stylevalue-subclasses/cssUrlImageValue.html
deleted file mode 100644
index abc944f..0000000
--- a/css/css-typed-om/stylevalue-subclasses/cssUrlImageValue.html
+++ /dev/null
@@ -1,81 +0,0 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>CSSURLImageValue</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#imagevalue-objects">
-<meta name="assert" content="Test CSSURLImageValue constructor and attributes" />
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="../resources/testhelper.js"></script>
-<body>
-<div id="log">
-<script>
-'use strict';
-
-const gTestUrl = '/media/1x1-green.png';
-const gBase64TestUrl = 'data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=';
-const gBadTestUrl = document.location.href;
-
-test(() => {
-  const result = new CSSURLImageValue(gTestUrl);
-
-  assert_not_equals(result, null,
-      'A CSSURLImageValue should be created');
-  assert_equals(result.url, gTestUrl,
-      'url member should be same as passed in the constructor');
-  assert_equals(result.intrinsicWidth, null, 'intrinsicWidth');
-  assert_equals(result.intrinsicHeight, null, 'intrinsicHeight');
-  assert_equals(result.intrinsicRatio, null, 'intrinsicRatio');
-  assert_equals(result.state, 'unloaded', 'state');
-}, 'Constructing a CSSURLImageValue with a valid URL puts it in an ' +
-   'unloaded state');
-
-async_test(t => {
-  const result = new CSSURLImageValue(gTestUrl);
-  let image = loadImageResource(t, result);
-
-  image.addEventListener('load', t.step_func_done(() => {
-    assert_equals(result.url, gTestUrl,
-        'url member should be same as passed in the constructor');
-    assert_equals(result.state, 'loaded', 'state');
-    assert_equals(result.intrinsicWidth, 1,
-        'intrinsicWidth member should be width of loaded image');
-    assert_equals(result.intrinsicHeight, 1,
-        'intrinsicHeight member should be height of loaded image');
-    assert_equals(result.intrinsicRatio, 1,
-        'intrinsicRatio member should be ratio of loaded image');
-  }));
-}, 'Constructing a CSSURLImageValue from a URL sets its state to loaded');
-
-async_test(t => {
-  const result = new CSSURLImageValue(gBase64TestUrl);
-  let image = loadImageResource(t, result);
-
-  image.addEventListener('load', t.step_func_done(() => {
-    assert_equals(result.url, gBase64TestUrl,
-        'url member should be same as passed in the constructor');
-    assert_equals(result.state, 'loaded', 'state');
-    assert_equals(result.intrinsicWidth, 1,
-        'intrinsicWidth member should be width of loaded image');
-    assert_equals(result.intrinsicHeight, 1,
-        'intrinsicHeight member should be height of loaded image');
-    assert_equals(result.intrinsicRatio, 1,
-        'intrinsicRatio member should be ratio of loaded image');
-  }));
-}, 'Constructing a CSSURLImageValue from a base64 URL sets its state to loaded');
-
-async_test(t => {
-  const result = new CSSURLImageValue(gBadTestUrl);
-  let image = loadImageResource(t, result);
-
-  image.addEventListener('error', t.step_func_done(() => {
-      assert_equals(result.url, gBadTestUrl,
-        'url member should be same as passed in the constructor');
-      assert_equals(result.state, 'error', 'state');
-      assert_equals(result.intrinsicWidth, null, 'intrinsicWidth');
-      assert_equals(result.intrinsicHeight, null, 'intrinsicHeight');
-      assert_equals(result.intrinsicRatio, null, 'intrinsicRatio');
-  }));
-}, 'Constructing a CSSURLImageValue from a URL to an invalid image sets ' +
-   'its state to error');
-
-</script>
diff --git a/css/css-typed-om/the-stylepropertymap/properties/resources/testsuite.js b/css/css-typed-om/the-stylepropertymap/properties/resources/testsuite.js
index a198c41..b4ed1e7 100644
--- a/css/css-typed-om/the-stylepropertymap/properties/resources/testsuite.js
+++ b/css/css-typed-om/the-stylepropertymap/properties/resources/testsuite.js
@@ -145,22 +145,6 @@
       }
     ],
   },
-  '<image>': {
-    description: 'an image',
-    examples: [
-      {
-        description: "a PNG image",
-        input: new CSSURLImageValue('/media/1x1.png'),
-        defaultComputed: (_, result) => {
-          // URLs compute to absolute URLs
-          assert_true(result instanceof CSSURLImageValue,
-            'Computed value should be a CSSURLImageValue');
-          assert_true(result.url.endsWith('/media/1x1.png'),
-            'Computed value should be an absolute URL');
-        }
-      }
-    ],
-  },
   '<transform>': {
     description: 'a transform',
     examples: [
@@ -218,6 +202,24 @@
   }, `Can set '${propertyName}' to ${description}`);
 }
 
+// We have to special case CSSImageValue as they cannot be created with a
+// constructor and are completely opaque.
+function testIsImageValidForProperty(propertyName) {
+  test(t => {
+    let element1 = createDivWithStyle(t, `${propertyName}: url("/media/1x1-green.png")`);
+    let element2 = createDivWithStyle(t);
+
+    const result = element1.attributeStyleMap.get(propertyName);
+    assert_not_equals(result, null, 'Image value must not be null');
+    assert_class_string(result, 'CSSImageValue',
+      'Image value must be a CSSImageValue');
+
+    element2.attributeStyleMap.set(propertyName, result);
+    assert_equals(element2.style[propertyName], element1.style[propertyName],
+      'Image value can be set on different element');
+  }, `Can set '${propertyName}' to an image`);
+}
+
 // Test that styleMap.set throws for invalid values
 function testPropertyInvalid(propertyName, examples, description) {
   test(t => {
@@ -289,6 +291,12 @@
     'CSS-wide keywords');
 
   for (const testCase of testCases) {
+    // <image> is a special case
+    if (testCase.syntax === '<image>') {
+      testIsImageValidForProperty(propertyName);
+      continue;
+    }
+
     // Retrieve test examples for this test case's syntax. If the syntax
     // looks like a keyword, then create an example on the fly.
     const syntaxExamples = testCase.syntax.match(/^[a-z\-]+$/) ?
diff --git a/interfaces/css-typed-om.idl b/interfaces/css-typed-om.idl
index 2744076..6c33907 100644
--- a/interfaces/css-typed-om.idl
+++ b/interfaces/css-typed-om.idl
@@ -308,23 +308,6 @@
     attribute CSSNumericValue y;
 };
 
-enum CSSResourceState {"unloaded", "loading", "loaded", "error"};
-
 [Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
-interface CSSResourceValue : CSSStyleValue {
-    readonly attribute CSSResourceState state;
-};
-
-
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
-interface CSSImageValue : CSSResourceValue {
-    readonly attribute double? intrinsicWidth;
-    readonly attribute double? intrinsicHeight;
-    readonly attribute double? intrinsicRatio;
-};
-
-[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet),
- Constructor(USVString url)]
-interface CSSURLImageValue : CSSImageValue {
-    readonly attribute USVString url;
+interface CSSImageValue : CSSStyleValue {
 };