[ResourceTiming]: Migrate redirects(.sub).html WPT

This change updates the redirects.sub.html WPT to follow the new style
and structure we've been introducing in the linked bug.

Bug: 1171767
Change-Id: I28bb746d01872b7ba4585750d86fc4d5a8d9af9b
GithubIssue: https://github.com/w3c/resource-timing/issues/254
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2774478
Reviewed-by: Yoav Weiss <yoavweiss@chromium.org>
Commit-Queue: Tom McKee <tommckee@chromium.org>
Cr-Commit-Position: refs/heads/master@{#864435}
diff --git a/resource-timing/entry-attributes.html b/resource-timing/entry-attributes.html
index e667c63..1115b7f 100644
--- a/resource-timing/entry-attributes.html
+++ b/resource-timing/entry-attributes.html
@@ -9,26 +9,6 @@
 <script src="resources/resource-loaders.js"></script>
 <script src="resources/entry-invariants.js"></script>
 <script>
-
-// Given a resource-loader and a PerformanceResourceTiming validator, loads a
-// resource and validates the resulting entry.
-const attribute_test = (load_resource, validate, test_label) => {
-  promise_test(
-    async () => {
-      // Clear out everything that isn't the one ResourceTiming entry under test.
-      performance.clearResourceTimings();
-
-      await load_resource();
-
-      const entry_list = performance.getEntriesByType("resource");
-      if (entry_list.length != 1) {
-        throw new Error(`There should be one entry for one resource (found ${entry_list.length})`);
-      }
-
-      validate(entry_list[0]);
-  }, test_label);
-}
-
 attribute_test(
   () => load.image("resources/fake_responses.py#hash=1"),
   entry => {
diff --git a/resource-timing/redirects.html b/resource-timing/redirects.html
new file mode 100644
index 0000000..f0fe409
--- /dev/null
+++ b/resource-timing/redirects.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8" />
+<title>Resource Timing: resources fetched through same-origin redirects</title>
+<link rel="author" title="Google" href="http://www.google.com/" />
+<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/resource-loaders.js"></script>
+<script src="resources/entry-invariants.js"></script>
+<script>
+const {HTTPS_NOTSAMESITE_ORIGIN} = get_host_info();
+const redirect_url = `/common/redirect.py`;
+const url_prefix = `${redirect_url}?location=/resource-timing/resources/`;
+const https_url_prefix = `${redirect_url}?location=${HTTPS_NOTSAMESITE_ORIGIN}/resource-timing/resources/`;
+
+attribute_test(
+  () => load.stylesheet(url_prefix + "resource_timing_test0.css"),
+  invariants.assert_same_origin_redirected_resource,
+  "Verify attributes of a redirected stylesheet's PerformanceResourceTiming");
+
+attribute_test(
+  () => load.image(url_prefix + "blue.png"),
+  invariants.assert_same_origin_redirected_resource,
+  "Verify attributes of a redirected image's PerformanceResourceTiming");
+
+attribute_test(
+  () => load.iframe(url_prefix + "blank_page_green.html"),
+  invariants.assert_same_origin_redirected_resource,
+  "Verify attributes of a redirected iframe's PerformanceResourceTiming");
+
+attribute_test(
+  () => load.script(url_prefix + "empty_script.js"),
+  invariants.assert_same_origin_redirected_resource,
+  "Verify attributes of a redirected script's PerformanceResourceTiming");
+
+attribute_test(
+  () => load.xhr_sync(url_prefix + "blank_page_green.htm?id=xhr"),
+  invariants.assert_same_origin_redirected_resource,
+  "Verify attributes of a redirected synchronous XMLHttpRequest's " +
+  "PerformanceResourceTiming");
+
+attribute_test(
+  () => load.xhr_sync(https_url_prefix + "blank_page_green.htm?id=xhr"),
+  invariants.assert_cross_origin_redirected_resource,
+  "Verify attributes of a synchronous XMLHttpRequest's " +
+  "PerformanceResourceTiming where the initial HTTP request is redirected " +
+  "to a cross-origin HTTPS resource."
+);
+
+</script>
+</head>
+<body>
+<h1>Description</h1>
+<p>This test validates that, when a fetching resources that encounter
+same-origin redirects, attributes of the PerformanceResourceTiming entry
+conform to the specification.</p>
+</body>
+</html>
diff --git a/resource-timing/redirects.sub.html b/resource-timing/redirects.sub.html
deleted file mode 100644
index d3d4f75..0000000
--- a/resource-timing/redirects.sub.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8" />
-<title>Resource Timing redirect names</title>
-<link rel="author" title="Google" href="http://www.google.com/" />
-<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/webperftestharness.js"></script>
-<script src="resources/webperftestharnessextension.js"></script>
-<script>
-let iframe;
-const redirect_url = 'common/redirect.py';
-const url_prefix = redirect_url + '?location=/resource-timing/resources/';
-const https_url_prefix = redirect_url + '?location=https://{{hosts[alt][]}}:{{ports[https][0]}}/resource-timing/resources/';
-function setup_iframe() {
-    const iframe_content =
-        '<link rel="stylesheet" href="/' + url_prefix + 'resource_timing_test0.css"></link>' +
-        '<img src="/' + url_prefix + 'blue.png"></img>' +
-        '<iframe src="/' + url_prefix + 'blank_page_green.htm"></iframe>' +
-        '<script src="/' + url_prefix + 'empty_script.js"></scr' + 'ipt>' +
-        '<scr' + 'ipt>' +
-        'const xhr = new XMLHttpRequest;' +
-        'xhr.open("GET", "/' + url_prefix + 'blank_page_green.htm?id=xhr", false);' +
-        'xhr.send();' +
-        'const xhr2 = new XMLHttpRequest;' +
-        'xhr2.open("GET", "/' + https_url_prefix + 'blank_page_green.htm?id=xhr", false);' +
-        'xhr2.send();' +
-        '</scr' + 'ipt>';
-    iframe = document.getElementById('frameContext');
-    iframe.contentWindow.document.write(iframe_content);
-}
-function onload_test() {
-    const context = new PerformanceContext(iframe.contentWindow.performance);
-    const entries = context.getEntriesByType('resource');
-
-    const index = window.location.pathname.lastIndexOf('resource-timing');
-    const pathname = window.location.pathname.substring(0, index) + url_prefix;
-    const https_pathname = window.location.pathname.substring(0, index) + https_url_prefix;
-    let expected_entries = {};
-    expected_entries[pathname + 'resource_timing_test0.css'] = 'link';
-    expected_entries[pathname + 'blue.png'] = 'img';
-    expected_entries[pathname + 'blank_page_green.htm'] = 'iframe';
-    expected_entries[pathname + 'empty_script.js'] = 'script';
-    expected_entries[pathname + 'blank_page_green.htm?id=xhr'] = 'xmlhttprequest';
-    expected_entries[https_pathname + 'blank_page_green.htm?id=xhr'] = 'xmlhttprequest';
-
-    test_resource_entries(entries, expected_entries);
-}
-window.setup_iframe = setup_iframe;
-</script>
-</head>
-<body>
-<h1>Description</h1>
-<p>This test validates that redirects do not alter the URL.</p>
-<div id="log"></div>
-<iframe id="frameContext" onload="onload_test();" src="resources/inject_resource_test.html"></iframe>
-</body>
-</html>
diff --git a/resource-timing/resources/entry-invariants.js b/resource-timing/resources/entry-invariants.js
index 26701ee..51d94e3 100644
--- a/resource-timing/resources/entry-invariants.js
+++ b/resource-timing/resources/entry-invariants.js
@@ -88,5 +88,50 @@
       "domainLookupEnd",
       "connectStart",
     ]);
+  },
+
+  // Asserts that attributes of the given PerformanceResourceTiming entry match
+  // what the spec dictates for any resource fetched over HTTPS through a
+  // cross-origin redirect.
+  // (e.g. GET http://remote.com/foo => 304 Location: https://remote.com/foo)
+  assert_cross_origin_redirected_resource: entry => {
+    assert_zeroed_(entry, [
+      "redirectStart",
+      "redirectEnd",
+      "domainLookupStart",
+      "domainLookupEnd",
+      "connectStart",
+      "connectEnd",
+      "secureConnectionStart",
+      "requestStart",
+      "responseStart",
+    ]);
+
+    assert_positive_(entry, [
+      "fetchStart",
+      "responseEnd",
+    ]);
   }
 };
+
+// Given a resource-loader and a PerformanceResourceTiming validator, loads a
+// resource and validates the resulting entry.
+const attribute_test = (load_resource, validate, test_label) => {
+  promise_test(
+    async () => {
+      // Clear out everything that isn't the one ResourceTiming entry under test.
+      performance.clearResourceTimings();
+
+      await load_resource();
+
+      const entry_list = performance.getEntriesByType("resource");
+      if (entry_list.length != 1) {
+        const names = entry_list
+          .map(e => e.name)
+          .join(", ");
+        throw new Error(`There should be one entry for one resource (found ${entry_list.length}: ${names})`);
+      }
+
+      validate(entry_list[0]);
+  }, test_label);
+}
diff --git a/resource-timing/resources/resource-loaders.js b/resource-timing/resources/resource-loaders.js
index 1d20d71..c5754d1 100644
--- a/resource-timing/resources/resource-loaders.js
+++ b/resource-timing/resources/resource-loaders.js
@@ -26,5 +26,56 @@
     return document.fonts.ready.then(() => {
       document.body.removeChild(div);
     });
+  },
+
+  // Returns a promise that settles once the given path has been fetched as a
+  // stylesheet resource.
+  stylesheet: async path => {
+    const link = document.createElement("link");
+    link.rel = "stylesheet";
+    link.type = "text/css";
+    link.href = path;
+
+    const loaded = new Promise(resolve => {
+      link.onload = link.onerror = resolve;
+    });
+
+    document.head.appendChild(link);
+    await loaded;
+    document.head.removeChild(link);
+  },
+
+  // Returns a promise that settles once the given path has been fetched as an
+  // iframe.
+  iframe: async path => {
+    const frame = document.createElement("iframe");
+    const loaded = new Promise(resolve => {
+      frame.onload = frame.onerror = resolve;
+    });
+    frame.src = path;
+    document.body.appendChild(frame);
+    await loaded;
+    document.body.removeChild(frame);
+  },
+
+  // Returns a promise that settles once the given path has been fetched as a
+  // script.
+  script: async path => {
+    const script = document.createElement("script");
+    const loaded = new Promise(resolve => {
+      script.onload = script.onerror = resolve;
+    });
+    script.src = path;
+    document.body.appendChild(script);
+    await loaded;
+    document.body.removeChild(script);
+  },
+
+  // Returns a promise that settles once the given path has been fetched
+  // through a synchronous XMLHttpRequest.
+  xhr_sync: async path => {
+    const xhr = new XMLHttpRequest;
+    xhr.open("GET", path, /* async = */ false);
+    xhr.send();
   }
 };