Bug 1526275 [wpt PR 15262] - [WPT] Merge wpt/{referrer-policy,mixed-content}/generic/common.js, a=testonly
authorHiroshige Hayashizaki <hiroshige@chromium.org>
Wed, 06 Mar 2019 12:33:20 +0000
changeset 522536 6d7a636b53c9cb55be8630ca1ae34c2421cbcec4
parent 522535 48aba45c55341d1a6e33d8f8c2d2f61911f5beae
child 522537 c5bd3dc78b58810100682dad5ccd46b2aed68641
push id10871
push usercbrindusan@mozilla.com
push dateMon, 18 Mar 2019 15:49:32 +0000
treeherdermozilla-beta@018abdd16060 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1526275, 15262, 906850, 1455745, 633917
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1526275 [wpt PR 15262] - [WPT] Merge wpt/{referrer-policy,mixed-content}/generic/common.js, a=testonly Automatic update from web-platform-tests [WPT] Merge wpt/{referrer-policy,mixed-content}/generic/common.js To merge wpt/{referrer-policy,mixed-content} test frameworks, this CL merges their common.js. The new common.js is based on mixed-content's common.js, with some aspects imported from referrer-policy's common.js: - Passes results from subresource payloads to resolved promises, converting if necessary using wrapResult(). This is for referrer-policy tests that rely on subresource payload to get referrer request headers, while mixed-content tests don't use the payload information at all. - Accepts `additionalAttributes` arguments (to be used to set referrer-policy-related attributes to elements). - Extends bindEvents() to clean up event listeners on completion (which is done for some request types in referrer-policy's common.js). - Imports queryImage() (with renaming to requestViaImageForReferrerPolicy) from referrer-policy's common.js (this should be merged with mixed-content version of image requests, but not now). On mixed-content side: - expect.py's response for script requests is modified because postMessage() is required by referrer-policy's common.js. On referrer-policy side: - Move referrer-policy-specific code from common.js to referrer-policy-test-case.js, including wrapResult(). - All tests (except for two [1][2]) are converted to promise-based, to handle errors correctly. - Bugs in the remaining two tests [1][2] are fixed. [1] referrer-policy/generic/iframe-inheritance.html [2] referrer-policy/generic/sandboxed-iframe-with-opaque-origin.html Then now wpt/{referrer-policy,mixed-content}/generic/common.js are the same. They are duplicated (i.e. not moved/merged to a single file) just to avoid mass modification of a large number of generated files for each step of refactoring, as these file names are hard-coded there. Bug: 906850 Change-Id: I39f19d08d658c1a898fc453b621d82a2faaaaf6b Reviewed-on: https://chromium-review.googlesource.com/c/1455745 Commit-Queue: Hiroshige Hayashizaki <hiroshige@chromium.org> Reviewed-by: Mike West <mkwst@chromium.org> Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org> Reviewed-by: Jochen Eisinger <jochen@chromium.org> Cr-Commit-Position: refs/heads/master@{#633917} -- wpt-commits: 767f361425c20ded79c35630f709fc7addf7f80a wpt-pr: 15262
testing/web-platform/tests/fetch/sec-metadata/img.tentative.https.sub.html
testing/web-platform/tests/mixed-content/generic/common.js
testing/web-platform/tests/mixed-content/generic/expect.py
testing/web-platform/tests/mixed-content/generic/mixed-content-test-case.js
testing/web-platform/tests/mixed-content/generic/script.js
testing/web-platform/tests/referrer-policy/css-integration/child-css/external-import-stylesheet.html
testing/web-platform/tests/referrer-policy/css-integration/child-css/internal-import-stylesheet.html
testing/web-platform/tests/referrer-policy/css-integration/child-css/processing-instruction.html
testing/web-platform/tests/referrer-policy/css-integration/css-test-helper.js
testing/web-platform/tests/referrer-policy/css-integration/font-face/external-import-stylesheet.html
testing/web-platform/tests/referrer-policy/css-integration/font-face/external-stylesheet.html
testing/web-platform/tests/referrer-policy/css-integration/font-face/internal-import-stylesheet.html
testing/web-platform/tests/referrer-policy/css-integration/font-face/internal-stylesheet.html
testing/web-platform/tests/referrer-policy/css-integration/font-face/processing-instruction.html
testing/web-platform/tests/referrer-policy/css-integration/image/external-import-stylesheet.html
testing/web-platform/tests/referrer-policy/css-integration/image/external-stylesheet.html
testing/web-platform/tests/referrer-policy/css-integration/image/inline-style.html
testing/web-platform/tests/referrer-policy/css-integration/image/internal-import-stylesheet.html
testing/web-platform/tests/referrer-policy/css-integration/image/internal-stylesheet.html
testing/web-platform/tests/referrer-policy/css-integration/image/presentation-attribute.html
testing/web-platform/tests/referrer-policy/css-integration/image/processing-instruction.html
testing/web-platform/tests/referrer-policy/css-integration/svg/external-stylesheet.html
testing/web-platform/tests/referrer-policy/css-integration/svg/inline-style.html
testing/web-platform/tests/referrer-policy/css-integration/svg/internal-stylesheet.html
testing/web-platform/tests/referrer-policy/css-integration/svg/presentation-attribute.html
testing/web-platform/tests/referrer-policy/css-integration/svg/processing-instruction.html
testing/web-platform/tests/referrer-policy/generic/common.js
testing/web-platform/tests/referrer-policy/generic/iframe-inheritance.html
testing/web-platform/tests/referrer-policy/generic/link-rel-prefetch.html
testing/web-platform/tests/referrer-policy/generic/multiple-headers-and-values.html
testing/web-platform/tests/referrer-policy/generic/multiple-headers-combined.html
testing/web-platform/tests/referrer-policy/generic/multiple-headers-one-invalid.html
testing/web-platform/tests/referrer-policy/generic/multiple-headers-one-unknown-token.html
testing/web-platform/tests/referrer-policy/generic/multiple-headers.html
testing/web-platform/tests/referrer-policy/generic/referrer-policy-test-case.js
testing/web-platform/tests/referrer-policy/generic/sandboxed-iframe-with-opaque-origin.html
testing/web-platform/tests/referrer-policy/generic/subresource-test/area-navigate.html
testing/web-platform/tests/referrer-policy/generic/subresource-test/fetch-messaging.html
testing/web-platform/tests/referrer-policy/generic/subresource-test/iframe-messaging.html
testing/web-platform/tests/referrer-policy/generic/subresource-test/image-decoding.html
testing/web-platform/tests/referrer-policy/generic/subresource-test/link-navigate.html
testing/web-platform/tests/referrer-policy/generic/subresource-test/script-messaging.html
testing/web-platform/tests/referrer-policy/generic/subresource-test/worker-messaging.html
testing/web-platform/tests/referrer-policy/generic/subresource-test/xhr-messaging.html
testing/web-platform/tests/referrer-policy/generic/unsupported-csp-referrer-directive.html
testing/web-platform/tests/upgrade-insecure-requests/support/testharness-helper.sub.js
--- a/testing/web-platform/tests/fetch/sec-metadata/img.tentative.https.sub.html
+++ b/testing/web-platform/tests/fetch/sec-metadata/img.tentative.https.sub.html
@@ -2,74 +2,74 @@
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
 <script src=/referrer-policy/generic/common.js></script>
 <script src=/fetch/sec-metadata/resources/helper.js></script>
 <body>
 <script>
   // These tests reuse the `referrer-policy` infrastructure to load images that
   // encode their request headers in their pixels. Fun stuff!
-  async_test(t => {
+  promise_test(() =>
     loadImageInWindow(
       "https://{{host}}:{{ports[https][0]}}/referrer-policy/generic/subresource/image.py",
-      t.step_func_done(img => {
+      [],
+      window)
+    .then(img => {
         headers = decodeImageData(extractImageData(img)).headers;
         got = {
-        "dest": headers["sec-fetch-dest"],
-        "mode": headers["sec-fetch-mode"],
-        "site": headers["sec-fetch-site"],
-        "user": headers["sec-fetch-user"]
+          "dest": headers["sec-fetch-dest"],
+          "mode": headers["sec-fetch-mode"],
+          "site": headers["sec-fetch-site"],
+          "user": headers["sec-fetch-user"]
         };
         assert_header_equals(got, {
           "dest": "image",
           "site": "same-origin",
           "user": "?F",
           "mode": "cors", // Because `loadImageInWindow` tacks on `crossorigin`
         });
       }),
-      [],
-      window);
-  }, "Same-origin image");
+    "Same-origin image");
 
-  async_test(t => {
+  promise_test(() =>
     loadImageInWindow(
       "https://{{hosts[][www]}}:{{ports[https][0]}}/referrer-policy/generic/subresource/image.py",
-      t.step_func_done(img => {
+      [],
+      window)
+    .then(img => {
         headers = decodeImageData(extractImageData(img)).headers;
         got = {
-        "dest": headers["sec-fetch-dest"],
-        "mode": headers["sec-fetch-mode"],
-        "site": headers["sec-fetch-site"],
-        "user": headers["sec-fetch-user"]
+          "dest": headers["sec-fetch-dest"],
+          "mode": headers["sec-fetch-mode"],
+          "site": headers["sec-fetch-site"],
+          "user": headers["sec-fetch-user"]
         };
         assert_header_equals(got, {
           "dest": "image",
           "site": "same-site",
           "user": "?F",
           "mode": "cors", // Because `loadImageInWindow` tacks on `crossorigin`
         });
       }),
-      [],
-      window);
-  }, "Same-site image");
+    "Same-site image");
 
-  async_test(t => {
+  promise_test(() =>
     loadImageInWindow(
       "https://{{hosts[alt][www]}}:{{ports[https][0]}}/referrer-policy/generic/subresource/image.py",
-      t.step_func_done(img => {
+      [],
+      window)
+    .then(img => {
         headers = decodeImageData(extractImageData(img)).headers;
         got = {
-        "dest": headers["sec-fetch-dest"],
-        "mode": headers["sec-fetch-mode"],
-        "site": headers["sec-fetch-site"],
-        "user": headers["sec-fetch-user"]
+          "dest": headers["sec-fetch-dest"],
+          "mode": headers["sec-fetch-mode"],
+          "site": headers["sec-fetch-site"],
+          "user": headers["sec-fetch-user"]
         };
         assert_header_equals(got, {
           "dest": "image",
           "site": "cross-site",
           "user": "?F",
           "mode": "cors", // Because `loadImageInWindow` tacks on `crossorigin`
         });
       }),
-      [],
-      window);
-  }, "Cross-site image");
+    "Cross-site image");
 </script>
--- a/testing/web-platform/tests/mixed-content/generic/common.js
+++ b/testing/web-platform/tests/mixed-content/generic/common.js
@@ -1,15 +1,29 @@
 /**
  * @fileoverview Utilities for mixed-content in Web Platform Tests.
  * @author burnik@google.com (Kristijan Burnik)
  * Disclaimer: Some methods of other authors are annotated in the corresponding
  *     method's JSDoc.
  */
 
+// The same content is placed as
+// - wpt/referrer-policy/generic/common.js and
+// - wpt/mixed-content/generic/common.js.
+// If you modify either one, please also update the other one.
+//
+// TODO(https://crbug.com/906850): These two files are going to be merged.
+// Currently they are duplicated only to avoid frequent mass modification
+// for each step of refactoring, as these file names are hard-coded in
+// a large number of generated test files.
+
+function timeoutPromise(t, ms) {
+  return new Promise(resolve => { t.step_timeout(resolve, ms); });
+}
+
 /**
  * Normalizes the target port for use in a URL. For default ports, this is the
  *     empty string (omitted port), otherwise it's a colon followed by the port
  *     number. Ports 80, 443 and an empty string are regarded as default ports.
  * @param {number} targetPort The port to use
  * @return {string} The port portion for using as part of a URL.
  */
 function getNormalizedPort(targetPort) {
@@ -65,37 +79,76 @@ function xhrRequest(url, responseType) {
  * @param {object} An object with keys (serving as attribute names) and values.
  */
 function setAttributes(el, attrs) {
   attrs = attrs || {}
   for (var attr in attrs)
     el.setAttribute(attr, attrs[attr]);
 }
 
-
 /**
  * Binds to success and error events of an object wrapping them into a promise
  *     available through {@code element.eventPromise}. The success event
  *     resolves and error event rejects.
+ * This method adds event listeners, and then removes all the added listeners
+ * when one of listened event is fired.
  * @param {object} element An object supporting events on which to bind the
  *     promise.
  * @param {string} resolveEventName [="load"] The event name to bind resolve to.
  * @param {string} rejectEventName [="error"] The event name to bind reject to.
  */
 function bindEvents(element, resolveEventName, rejectEventName) {
-  element.eventPromise = new Promise(function(resolve, reject) {
-    element.addEventListener(resolveEventName  || "load", function (e) {
-      resolve(e);
-    });
-    element.addEventListener(rejectEventName || "error", function(e) {
+  element.eventPromise =
+      bindEvents2(element, resolveEventName, element, rejectEventName);
+}
+
+// Returns a promise wrapping success and error events of objects.
+// This is a variant of bindEvents that can accept separate objects for each
+// events and two events to reject, and doesn't set `eventPromise`.
+//
+// When `resolveObject`'s `resolveEventName` event (default: "load") is
+// fired, the promise is resolved with the event.
+//
+// When `rejectObject`'s `rejectEventName` event (default: "error") or
+// `rejectObject2`'s `rejectEventName2` event (default: "error") is
+// fired, the promise is rejected.
+//
+// `rejectObject2` is optional.
+function bindEvents2(resolveObject, resolveEventName, rejectObject, rejectEventName, rejectObject2, rejectEventName2) {
+  return new Promise(function(resolve, reject) {
+    const actualResolveEventName = resolveEventName || "load";
+    const actualRejectEventName = rejectEventName || "error";
+    const actualRejectEventName2 = rejectEventName2 || "error";
+
+    const resolveHandler = function(event) {
+      cleanup();
+      resolve(event);
+    };
+
+    const rejectHandler = function(event) {
       // Chromium starts propagating errors from worker.onerror to
       // window.onerror. This handles the uncaught exceptions in tests.
-      e.preventDefault();
-      reject(e);
-    });
+      event.preventDefault();
+      cleanup();
+      reject(event);
+    };
+
+    const cleanup = function() {
+      resolveObject.removeEventListener(actualResolveEventName, resolveHandler);
+      rejectObject.removeEventListener(actualRejectEventName, rejectHandler);
+      if (rejectObject2) {
+        rejectObject2.removeEventListener(actualRejectEventName2, rejectHandler);
+      }
+    };
+
+    resolveObject.addEventListener(actualResolveEventName, resolveHandler);
+    rejectObject.addEventListener(actualRejectEventName, rejectHandler);
+    if (rejectObject2) {
+      rejectObject2.addEventListener(actualRejectEventName2, rejectHandler);
+    }
   });
 }
 
 /**
  * Creates a new DOM element.
  * @param {string} tagName The type of the DOM element.
  * @param {object} attrs A JSON with attributes to apply to the element.
  * @param {DOMElement} parent Optional - an existing DOM element to append to
@@ -145,51 +198,183 @@ function createRequestViaElement(tagName
 function createHelperIframe(name, doBindEvents) {
   return createElement("iframe",
                        {"name": name, "id": name},
                        document.body,
                        doBindEvents);
 }
 
 /**
+ * requestVia*() functions return promises that are resolved on successful
+ * requests with objects of the same "type", i.e. objects that contains
+ * the same sets of keys that are fixed within one category of tests (e.g.
+ * within wpt/referrer-policy tests).
+ * wrapResult() (that should be defined outside this file) is used to convert
+ * the response bodies of subresources into the expected result objects in some
+ * cases, and in other cases the result objects are constructed more directly.
+ * TODO(https://crbug.com/906850): Clean up the semantics around this, e.g.
+ * use (or not use) wrapResult() consistently, unify the arguments, etc.
+ */
+
+/**
  * Creates a new iframe, binds load and error events, sets the src attribute and
  *     appends it to {@code document.body} .
  * @param {string} url The src for the iframe.
  * @return {Promise} The promise for success/error events.
  */
-function requestViaIframe(url) {
-  return createRequestViaElement("iframe", {"src": url}, document.body);
+function requestViaIframe(url, additionalAttributes) {
+  const iframe = createElement(
+      "iframe",
+      Object.assign({"src": url}, additionalAttributes),
+      document.body,
+      false);
+  return bindEvents2(window, "message", iframe, "error", window, "error")
+      .then(event => {
+          assert_equals(event.source, iframe.contentWindow);
+          return event.data;
+        });
 }
 
 /**
  * Creates a new image, binds load and error events, sets the src attribute and
  *     appends it to {@code document.body} .
  * @param {string} url The src for the image.
  * @return {Promise} The promise for success/error events.
  */
 function requestViaImage(url) {
   return createRequestViaElement("img", {"src": url}, document.body);
 }
 
+// Helpers for requestViaImageForReferrerPolicy().
+function loadImageInWindow(src, attributes, w) {
+  return new Promise((resolve, reject) => {
+    var image = new w.Image();
+    image.crossOrigin = "Anonymous";
+    image.onload = function() {
+      resolve(image);
+    };
+
+    // Extend element with attributes. (E.g. "referrerPolicy" or "rel")
+    if (attributes) {
+      for (var attr in attributes) {
+        image[attr] = attributes[attr];
+      }
+    }
+
+    image.src = src;
+    w.document.body.appendChild(image)
+  });
+}
+
+function extractImageData(img) {
+    var canvas = document.createElement("canvas");
+    var context = canvas.getContext('2d');
+    context.drawImage(img, 0, 0);
+    var imgData = context.getImageData(0, 0, img.clientWidth, img.clientHeight);
+    return imgData.data;
+}
+
+function decodeImageData(rgba) {
+  var rgb = new Uint8ClampedArray(rgba.length);
+
+  // RGBA -> RGB.
+  var rgb_length = 0;
+  for (var i = 0; i < rgba.length; ++i) {
+    // Skip alpha component.
+    if (i % 4 == 3)
+      continue;
+
+    // Zero is the string terminator.
+    if (rgba[i] == 0)
+      break;
+
+    rgb[rgb_length++] = rgba[i];
+  }
+
+  // Remove trailing nulls from data.
+  rgb = rgb.subarray(0, rgb_length);
+  var string_data = (new TextDecoder("ascii")).decode(rgb);
+
+  return JSON.parse(string_data);
+}
+
+// A variant of requestViaImage for referrer policy tests.
+// This tests many patterns of <iframe>s to test referrer policy inheritance.
+// TODO(https://crbug.com/906850): Merge this into requestViaImage().
+// <iframe>-related code should be moved outside requestViaImage*().
+function requestViaImageForReferrerPolicy(url, attributes, referrerPolicy) {
+  // For images, we'll test:
+  // - images in a `srcdoc` frame to ensure that it uses the referrer
+  //   policy of its parent,
+  // - images in a top-level document,
+  // - and images in a `srcdoc` frame with its own referrer policy to
+  //   override its parent.
+
+  var iframeWithoutOwnPolicy = document.createElement('iframe');
+  var noSrcDocPolicy = new Promise((resolve, reject) => {
+        iframeWithoutOwnPolicy.srcdoc = "Hello, world.";
+        iframeWithoutOwnPolicy.onload = resolve;
+        document.body.appendChild(iframeWithoutOwnPolicy);
+      })
+    .then(() => {
+        var nextUrl = url + "&cache_destroyer2=" + (new Date()).getTime();
+        return loadImageInWindow(nextUrl, attributes,
+                                 iframeWithoutOwnPolicy.contentWindow);
+      })
+    .then(function (img) {
+        return decodeImageData(extractImageData(img));
+      });
+
+  // Give a srcdoc iframe a referrer policy different from the top-level page's policy.
+  var iframePolicy = (referrerPolicy === "no-referrer") ? "unsafe-url" : "no-referrer";
+  var iframeWithOwnPolicy = document.createElement('iframe');
+  var srcDocPolicy = new Promise((resolve, reject) => {
+        iframeWithOwnPolicy.srcdoc = "<meta name='referrer' content='" + iframePolicy + "'>Hello world.";
+        iframeWithOwnPolicy.onload = resolve;
+        document.body.appendChild(iframeWithOwnPolicy);
+      })
+    .then(() => {
+        var nextUrl = url + "&cache_destroyer3=" + (new Date()).getTime();
+        return loadImageInWindow(nextUrl, null,
+                                 iframeWithOwnPolicy.contentWindow);
+      })
+    .then(function (img) {
+        return decodeImageData(extractImageData(img));
+      });
+
+  var pagePolicy = loadImageInWindow(url, attributes, window)
+    .then(function (img) {
+        return decodeImageData(extractImageData(img));
+      });
+
+  return Promise.all([noSrcDocPolicy, srcDocPolicy, pagePolicy]).then(values => {
+    assert_equals(values[0].headers.referer, values[2].headers.referer, "Referrer inside 'srcdoc' without its own policy should be the same as embedder's referrer.");
+    assert_equals((iframePolicy === "no-referrer" ? undefined : document.location.href), values[1].headers.referer, "Referrer inside 'srcdoc' should use the iframe's policy if it has one");
+    return wrapResult(values[2]);
+  });
+}
+
 /**
  * Initiates a new XHR GET request to provided URL.
  * @param {string} url The endpoint URL for the XHR.
  * @return {Promise} The promise for success/error events.
  */
 function requestViaXhr(url) {
-  return xhrRequest(url);
+  return xhrRequest(url).then(result => wrapResult(result));
 }
 
 /**
  * Initiates a new GET request to provided URL via the Fetch API.
  * @param {string} url The endpoint URL for the Fetch.
  * @return {Promise} The promise for success/error events.
  */
 function requestViaFetch(url) {
-  return fetch(url);
+  return fetch(url)
+    .then(res => res.json())
+    .then(j => wrapResult(j));
 }
 
 function dedicatedWorkerUrlThatFetches(url) {
   return `data:text/javascript,
     fetch('${url}')
       .then(() => postMessage(''),
             () => postMessage(''));`;
 }
@@ -208,20 +393,32 @@ function workerUrlThatImports(url) {
  */
 function requestViaDedicatedWorker(url, options) {
   var worker;
   try {
     worker = new Worker(url, options);
   } catch (e) {
     return Promise.reject(e);
   }
-  bindEvents(worker, "message", "error");
   worker.postMessage('');
+  return bindEvents2(worker, "message", worker, "error")
+    .then(event => wrapResult(event.data));
+}
 
-  return worker.eventPromise;
+function requestViaSharedWorker(url) {
+  var worker;
+  try {
+    worker = new SharedWorker(url);
+  } catch(e) {
+    return Promise.reject(e);
+  }
+  const promise = bindEvents2(worker.port, "message", worker, "error")
+    .then(event => wrapResult(event.data));
+  worker.port.start();
+  return promise;
 }
 
 // Returns a reference to a worklet object corresponding to a given type.
 function get_worklet(type) {
   if (type == 'animation')
     return CSS.animationWorklet;
   if (type == 'layout')
     return CSS.layoutWorklet;
@@ -246,57 +443,77 @@ function requestViaWorklet(type, url) {
  * Sets the href attribute on a navigable DOM element and performs a navigation
  *     by clicking it. To avoid navigating away from the current execution
  *     context, a target attribute is set to point to a new helper iframe.
  * @param {DOMElement} navigableElement The navigable DOMElement
  * @param {string} url The href for the navigable element.
  * @return {Promise} The promise for success/error events.
  */
 function requestViaNavigable(navigableElement, url) {
-  var iframe = createHelperIframe(guid(), true);
+  var iframe = createHelperIframe(guid(), false);
   setAttributes(navigableElement,
                 {"href": url,
                  "target": iframe.name});
+
+  const promise =
+    bindEvents2(window, "message", iframe, "error", window, "error")
+      .then(event => {
+          assert_equals(event.source, iframe.contentWindow, "event.source");
+          return event.data;
+        });
   navigableElement.click();
-
-  return iframe.eventPromise;
+  return promise;
 }
 
 /**
  * Creates a new anchor element, appends it to {@code document.body} and
  *     performs the navigation.
  * @param {string} url The URL to navigate to.
  * @return {Promise} The promise for success/error events.
  */
-function requestViaAnchor(url) {
-  var a = createElement("a", {"innerHTML": "Link to resource"}, document.body);
+function requestViaAnchor(url, additionalAttributes) {
+  var a = createElement(
+      "a",
+      Object.assign({"innerHTML": "Link to resource"}, additionalAttributes),
+      document.body);
 
   return requestViaNavigable(a, url);
 }
 
 /**
  * Creates a new area element, appends it to {@code document.body} and performs
  *     the navigation.
  * @param {string} url The URL to navigate to.
  * @return {Promise} The promise for success/error events.
  */
-function requestViaArea(url) {
-  var area = createElement("area", {}, document.body);
+function requestViaArea(url, additionalAttributes) {
+  var area = createElement(
+      "area",
+      Object.assign({}, additionalAttributes),
+      document.body);
 
+  // TODO(kristijanburnik): Append to map and add image.
   return requestViaNavigable(area, url);
 }
 
 /**
  * Creates a new script element, sets the src to url, and appends it to
  *     {@code document.body}.
  * @param {string} url The src URL.
  * @return {Promise} The promise for success/error events.
  */
-function requestViaScript(url) {
-  return createRequestViaElement("script", {"src": url}, document.body);
+function requestViaScript(url, additionalAttributes) {
+  const script = createElement(
+      "script",
+      Object.assign({"src": url}, additionalAttributes),
+      document.body,
+      false);
+
+  return bindEvents2(window, "message", script, "error", window, "error")
+    .then(event => wrapResult(event.data));
 }
 
 /**
  * Creates a new form element, sets attributes, appends it to
  *     {@code document.body} and submits the form.
  * @param {string} url The URL to submit to.
  * @return {Promise} The promise for success/error events.
  */
@@ -453,26 +670,30 @@ function requestViaObject(url) {
  * @param {string} url The URL for WebSocket to connect to.
  * @return {Promise} The promise for success/error events.
  */
 function requestViaWebSocket(url) {
   return new Promise(function(resolve, reject) {
     var websocket = new WebSocket(url);
 
     websocket.addEventListener("message", function(e) {
-      resolve(JSON.parse(e.data));
+      resolve(e.data);
     });
 
     websocket.addEventListener("open", function(e) {
       websocket.send("echo");
     });
 
     websocket.addEventListener("error", function(e) {
       reject(e)
     });
-  });
+  })
+  .then(data => {
+      return JSON.parse(data);
+    });
 }
 
 // SanityChecker does nothing in release mode. See sanity-checker.js for debug
 // mode.
 function SanityChecker() {}
 SanityChecker.prototype.checkScenario = function() {};
 SanityChecker.prototype.setFailTimeout = function(test, timeout) {};
+SanityChecker.prototype.checkSubresourceResult = function() {};
--- a/testing/web-platform/tests/mixed-content/generic/expect.py
+++ b/testing/web-platform/tests/mixed-content/generic/expect.py
@@ -78,16 +78,21 @@ def main(request, response):
                 response_data = open(os.path.join(request.doc_root,
                                                   "media",
                                                   "movie_5.ogv"), "rb").read()
             elif content_type == "application/javascript":
                 response_data = open(os.path.join(request.doc_root,
                                                   "mixed-content",
                                                   "generic",
                                                   "worker.js"), "rb").read()
+            elif content_type == "text/javascript":
+                response_data = open(os.path.join(request.doc_root,
+                                                  "mixed-content",
+                                                  "generic",
+                                                  "script.js"), "rb").read()
             else:
                 response_data = "/* purged */"
         elif action == "take":
             value = stash.take(key=key, path=path)
             if value is None:
                 status = "allowed"
             else:
                 status = "blocked"
--- a/testing/web-platform/tests/mixed-content/generic/mixed-content-test-case.js
+++ b/testing/web-platform/tests/mixed-content/generic/mixed-content-test-case.js
@@ -1,13 +1,18 @@
 /**
  * @fileoverview Test case for mixed-content in Web Platform Tests.
  * @author burnik@google.com (Kristijan Burnik)
  */
 
+function wrapResult(server_data) {
+  // Currently the returned value is not used in mixed-content tests.
+  return null;
+}
+
 /**
  * MixedContentTestCase exercises all the tests for checking browser behavior
  * when resources regarded as mixed-content are requested. A single run covers
  * only a single scenario.
  * @param {object} scenario A JSON describing the test arrangement and
  *     expectation(s). Refer to /mixed-content/spec.src.json for details.
  * @param {string} description The test scenario verbose description.
  * @param {SanityChecker} sanityChecker Instance of an object used to check the
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/script.js
@@ -0,0 +1,1 @@
+postMessage("", "*");
--- a/testing/web-platform/tests/referrer-policy/css-integration/child-css/external-import-stylesheet.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/child-css/external-import-stylesheet.html
@@ -2,45 +2,46 @@
 <html>
   <head>
     <title>CSS integration - Child css from external stylesheet</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
     <p>Check that child css are loaded with the referrer and referrer policy
     from the external stylesheet.</p>
 
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let url_prefix = location.protocol + "//www1." + location.hostname + ":" + location.port;
         let css_url = url_prefix +
           "/referrer-policy/generic/subresource/stylesheet.py?id=" + id +
           "&import-rule" + "&referrer-policy=no-referrer";
         let check_url = url_prefix + "/referrer-policy/generic/subresource/stylesheet.py" +
                         "?id=" + id + "&report-headers";
 
-        let link = document.createElement("link");
-        link.href = css_url;
-        link.rel = "stylesheet";
-        link.onload = function() {
-          css_test.step_timeout(function() {
-              queryXhr(check_url, function(message) {
-                  assert_own_property(message, "headers");
-                  assert_equals(message.referrer, undefined);
-                  css_test.done();
-              }, null, null, css_test);
-          }, 1000);
-        };
-        document.head.appendChild(link);
+        return new Promise(resolve => {
+            let link = document.createElement("link");
+            link.href = css_url;
+            link.rel = "stylesheet";
+            link.onload = resolve;
+            document.head.appendChild(link);
+          })
+          .then(() => timeoutPromise(css_test, 1000))
+          .then(() => requestViaXhr(check_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_equals(message.referrer, undefined);
+            });
       }, "Child css from external stylesheet.");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/child-css/internal-import-stylesheet.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/child-css/internal-import-stylesheet.html
@@ -2,42 +2,42 @@
 <html>
   <head>
     <title>CSS integration - Child css from internal stylesheet</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
     <p>Check that child css are loaded with the referrer and referrer policy
     from the internal stylesheet.</p>
 
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let url_prefix = location.protocol + "//www1." + location.hostname + ":" + location.port;
         let css_url = url_prefix + "/referrer-policy/generic/subresource/stylesheet.py?id=" + id + "&import-rule";
         let check_url = url_prefix + "/referrer-policy/generic/subresource/stylesheet.py" +
                         "?id=" + id + "&report-headers";
 
         let style = document.createElement("style");
         style.type = 'text/css';
         style.appendChild(document.createTextNode("@import url('" + css_url + "');"));
         document.head.appendChild(style);
-        css_test.step_timeout(function() {
-          queryXhr(check_url, function(message) {
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(check_url))
+          .then(function(message) {
               assert_own_property(message, "headers");
               assert_own_property(message, "referrer");
               assert_equals(message.referrer, css_url);
-              css_test.done();
-          }, null, null, css_test);
-        }, 1000);
+            });
       }, "Child css from internal stylesheet.");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/child-css/processing-instruction.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/child-css/processing-instruction.html
@@ -2,44 +2,44 @@
 <html>
   <head>
     <title>CSS integration - child css via a ProcessingInstruction</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
     <p>Check that child css are loaded with the referrer and referrer policy the
     external stylesheet(referenced from a ProcessingInstruction).</p>
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let url_prefix = location.protocol + "//www1." + location.hostname + ":" +
                          location.port +
                          "/referrer-policy/generic/subresource/stylesheet.py?id=" +
                          id;
         let css_url = url_prefix + "&amp;import-rule";
         let expected = url_prefix + "&import-rule";
         let check_url = url_prefix + "&report-headers";
 
         let processingInstruction =
           document.createProcessingInstruction(
             "xml-stylesheet", "href=\"" +css_url + "\" type=\"text/css\"");
-        css_test.step_timeout(function() {
-            queryXhr(check_url, function(message) {
+        document.insertBefore(processingInstruction, document.firstChild);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(check_url))
+          .then(function(message) {
                 assert_own_property(message, "headers");
                 assert_own_property(message, "referrer");
                 assert_equals(message.referrer, expected);
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
-        document.insertBefore(processingInstruction, document.firstChild);
+            });
       }, "Child css via a ProcessingInstruction.");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/css-test-helper.js
+++ b/testing/web-platform/tests/referrer-policy/css-integration/css-test-helper.js
@@ -9,51 +9,44 @@ var svg_test_properties = [
   'clip-path',
   'marker-start',
   'marker-mid',
   'marker-end',
   'mask',
   'mask-image',
 ];
 
-// Schedules async_test's for each of the test properties
 // Parameters:
 //     testProperties: An array of test properties.
 //     testDescription: A test description
 //     testFunction: A function call which sets up the expect result and runs
 //                   the actual test
 function runSvgTests(testProperties, testDescription, testFunction) {
-  let runNextTest = function () {
-    let property = testProperties.shift();
-    if (property === undefined) {
-      return;
-    }
-
+  for (const property of testProperties) {
     let current = {
-      test: async_test(testDescription + " " + property),
       id: token(),
       property: property,
     };
 
-    current.test.step(function() { testFunction(current) });
-
-    let check_url = url_prefix + "svg.py" + "?id=" + current.id +
-                    "&report-headers";
-    current.test.step_timeout(function() {
-      queryXhr(check_url, function(message) {
-          assert_own_property(message, "headers");
-          assert_own_property(message, "referrer");
-          assert_equals(message.referrer, current.expected);
-          current.test.done();
-      }, null, null, current.test);
-    }, 800);
-  };
-
-  add_result_callback(runNextTest);
-  runNextTest();
+    promise_test(t => {
+      testFunction(current);
+      return timeoutPromise(t, 800)
+        .then(() => {
+            let check_url = url_prefix + "svg.py" + "?id=" + current.id +
+                            "&report-headers";
+            return requestViaFetch(check_url);
+          })
+        .then(message => {
+            assert_own_property(message, "headers");
+            assert_own_property(message, "referrer");
+            assert_equals(message.referrer, current.expected);
+          });
+      },
+      testDescription + " " + property);
+  }
 }
 
 function createSvg() {
   let svg = document.createElementNS(svg_ns, 'svg');
   svg.setAttribute('width', '400');
   svg.setAttribute('height', '400');
   let path = document.createElementNS(svg_ns, 'path');
   path.setAttribute('d', 'M 50,5 95,100 5,100 z');
--- a/testing/web-platform/tests/referrer-policy/css-integration/font-face/external-import-stylesheet.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/font-face/external-import-stylesheet.html
@@ -2,50 +2,51 @@
 <html>
   <head>
     <title>CSS integration - Font from imported stylesheet (external)</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
     <p>Check that resources from imported stylesheets (loaded from external
     stylesheets) are loaded with the referrer and referrer policy from the
     external stylesheet.</p>
 
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let css_url = location.protocol + "//www1." + location.hostname + ":" +
           location.port +
           "/referrer-policy/generic/subresource/stylesheet.py?id=" + id +
           "&import-rule" + "&type=font";
         let url_prefix = location.protocol + "//" + location.hostname + ":" + location.port;
         let css_referrer = url_prefix +
           "/referrer-policy/generic/subresource/stylesheet.py?id=" + id + "&type=font";
         let font_url = url_prefix + "/referrer-policy/generic/subresource/font.py" +
                        "?id=" + id + "&report-headers" + "&type=font";
 
-        let link = document.createElement("link");
-        link.href = css_url;
-        link.rel = "stylesheet";
-        link.onload = function() {
-          css_test.step_timeout(function() {
-              queryXhr(font_url, function(message) {
-                  assert_own_property(message, "headers");
-                  assert_own_property(message, "referrer");
-                  assert_equals(message.referrer, css_referrer);
-                  css_test.done();
-              }, null, null, css_test);
-          }, 1000);
-        };
-        document.head.appendChild(link);
+        return new Promise(resolve => {
+            let link = document.createElement("link");
+            link.href = css_url;
+            link.rel = "stylesheet";
+            link.onload = resolve;
+            document.head.appendChild(link);
+          })
+          .then(() => timeoutPromise(css_test, 1000))
+          .then(() => requestViaXhr(font_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_referrer);
+            });
       }, "Font from imported stylesheet (external).");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/font-face/external-stylesheet.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/font-face/external-stylesheet.html
@@ -2,44 +2,45 @@
 <html>
   <head>
     <title>CSS integration - Font from external stylesheet</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
     <p>Check that resources from external stylesheets are loaded with
     the referrer and referrer policy from the external stylesheet.</p>
 
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let url_prefix = location.protocol + "//www1." + location.hostname + ":" + location.port;
         let css_url = url_prefix + "/referrer-policy/generic/subresource/stylesheet.py?id=" + id + "&type=font";
         let font_url = url_prefix + "/referrer-policy/generic/subresource/font.py" +
                       "?id=" + id + "&report-headers";
 
-        let link = document.createElement("link");
-        link.href = css_url;
-        link.rel = "stylesheet";
-        link.onload = function() {
-          css_test.step_timeout(function() {
-              queryXhr(font_url, function(message) {
-                  assert_own_property(message, "headers");
-                  assert_own_property(message, "referrer");
-                  assert_equals(message.referrer, css_url);
-                  css_test.done();
-              }, null, null, css_test);
-          }, 1000);
-        };
-        document.head.appendChild(link);
+        return new Promise(resolve => {
+            let link = document.createElement("link");
+            link.href = css_url;
+            link.rel = "stylesheet";
+            link.onload = resolve;
+            document.head.appendChild(link);
+          })
+          .then(() => timeoutPromise(css_test, 1000))
+          .then(() => requestViaXhr(font_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_url);
+            });
       }, "Font from external stylesheet.");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/font-face/internal-import-stylesheet.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/font-face/internal-import-stylesheet.html
@@ -2,42 +2,42 @@
 <html>
   <head>
     <title>CSS integration - Font from imported stylesheet (internal)</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
     <p>Check that resources from stylesheets (imported from internal
     stylesheets) are loaded with the referrer and referrer policy from from the
     imported style sheet.</p>
 
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let url_prefix = location.protocol + "//www1." + location.hostname + ":" +
                          location.port + "/referrer-policy/generic/subresource/";
         let css_url = url_prefix + "stylesheet.py?id=" + id + "&type=font";
         let font_url = url_prefix + "font.py?report-headers&id=" + id;
 
         let style = document.createElement("style");
         style.textContent = "@import url('" + css_url + "');";
         document.head.appendChild(style);
-        css_test.step_timeout(function() {
-            queryXhr(font_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, css_url);
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(font_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_url);
+            });
       }, "Font from imported stylesheet (internal).");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/font-face/internal-stylesheet.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/font-face/internal-stylesheet.html
@@ -2,42 +2,42 @@
 <html>
   <head>
     <title>CSS integration - Font from internal stylesheet</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
     <p>Check that resources from internal stylesheets are loaded with
     the referrer and referrer policy from the document.</p>
 
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let css_url = location.protocol + "//www1." + location.hostname + ":" +
                       location.port +
                       "/referrer-policy/generic/subresource/font.py" + "?id=" +
                       id + "&type=font";
         let font_url = css_url + "&report-headers";
 
         let style = document.createElement("style");
         style.textContent = "@font-face { font-family: 'wpt'; font-style: normal; font-weight: normal; src: url(" + css_url + "); format('truetype'); } body { font-family: 'wpt';}";
         document.head.appendChild(style);
-        css_test.step_timeout(function() {
-            queryXhr(font_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, location.origin + "/");
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(font_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, location.origin + "/");
+            });
       }, "Font from internal stylesheet.");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/font-face/processing-instruction.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/font-face/processing-instruction.html
@@ -2,48 +2,48 @@
 <html>
   <head>
     <title>CSS integration - Font from external stylesheet inserted via a ProcessingInstruction</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
     <p>Check that resources from external stylesheets (referenced from a
     ProcessingInstruction) are loaded with the referrer and referrer policy
     from the external stylesheet.</p>
 
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         let id = token();
         let url_prefix = location.protocol + "//www1." + location.hostname + ":" + location.port;
         let css_url = url_prefix +
                       "/referrer-policy/generic/subresource/stylesheet.py?id=" +
                       id + "&amp;type=font";
         let expected = url_prefix +
                       "/referrer-policy/generic/subresource/stylesheet.py?id=" +
                       id + "&type=font";
         let font_url = url_prefix + "/referrer-policy/generic/subresource/font.py" +
                        "?id=" + id + "&report-headers";
 
         let processingInstruction =
           document.createProcessingInstruction(
             "xml-stylesheet", "href=\"" + css_url + "\" type=\"text/css\"");
-        css_test.step_timeout(function() {
-            queryXhr(font_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, expected);
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
         document.insertBefore(processingInstruction, document.firstChild);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(font_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, expected);
+            });
       }, "Font from external stylesheet (from ProcessingInstruction).");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/image/external-import-stylesheet.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/image/external-import-stylesheet.html
@@ -2,50 +2,51 @@
 <html>
   <head>
     <title>CSS integration - image from imported stylesheet (external)</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
     <p>Check that resources from imported stylesheets (loaded from external
     stylesheets) are loaded with the referrer and referrer policy from the
     external stylesheet.</p>
 
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var css_url = location.protocol + "//www1." + location.hostname + ":" + location.port +
           "/referrer-policy/generic/subresource/stylesheet.py?id=" + id +
           "&import-rule" + "&type=image";
         var url_prefix = location.protocol + "//" + location.hostname + ":" + location.port;
         var css_referrer = url_prefix +
           "/referrer-policy/generic/subresource/stylesheet.py?id=" + id +
           "&type=image";
         var img_url = url_prefix + "/referrer-policy/generic/subresource/image.py" +
                       "?id=" + id + "&report-headers";
 
-        var link = document.createElement("link");
-        link.href = css_url;
-        link.rel = "stylesheet";
-        link.onload = function() {
-          css_test.step_timeout(function() {
-              queryXhr(img_url, function(message) {
-                  assert_own_property(message, "headers");
-                  assert_own_property(message, "referrer");
-                  assert_equals(message.referrer, css_referrer);
-                  css_test.done();
-               }, null, null, css_test);
-          }, 1000);
-        };
-        document.head.appendChild(link);
+        return new Promise(resolve => {
+            var link = document.createElement("link");
+            link.href = css_url;
+            link.rel = "stylesheet";
+            link.onload = resolve;
+            document.head.appendChild(link);
+          })
+          .then(() => timeoutPromise(css_test, 1000))
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_referrer);
+            });
       }, "Image from imported stylesheet (external).");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/image/external-stylesheet.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/image/external-stylesheet.html
@@ -2,44 +2,45 @@
 <html>
   <head>
     <title>CSS integration - image from external stylesheet</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
     <p>Check that resources from external stylesheets are loaded with
     the referrer and referrer policy from the external stylesheet.</p>
 
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var url_prefix = location.protocol + "//www1." + location.hostname + ":" + location.port;
         var css_url = url_prefix + "/referrer-policy/generic/subresource/stylesheet.py?id=" + id;
         var img_url = url_prefix + "/referrer-policy/generic/subresource/image.py" +
                       "?id=" + id + "&report-headers";
 
-        var link = document.createElement("link");
-        link.href = css_url;
-        link.rel = "stylesheet";
-        link.onload = css_test.step_func(function() {
-          css_test.step_timeout(function() {
-              queryXhr(img_url, function(message) {
-                  assert_own_property(message, "headers");
-                  assert_own_property(message, "referrer");
-                  assert_equals(message.referrer, css_url);
-                  css_test.done();
-              }, null, null, css_test);
-          }, 1000);
-        });
-        document.head.appendChild(link);
+        return new Promise(resolve => {
+            var link = document.createElement("link");
+            link.href = css_url;
+            link.rel = "stylesheet";
+            link.onload = resolve;
+            document.head.appendChild(link);
+          })
+          .then(() => timeoutPromise(css_test, 1000))
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_url);
+            });
       }, "Image from external stylesheet.");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/image/inline-style.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/image/inline-style.html
@@ -2,38 +2,38 @@
 <html>
   <head>
     <title>CSS integration - image from inline style</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
     <p>Check that resources from inline styles are loaded with
     the referrer and referrer policy from the document.</p>
 
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var css_url = location.protocol + "//www1." + location.hostname + ":" + location.port + "/referrer-policy/generic/subresource/image.py" + "?id=" + id;
         var img_url = css_url + "&report-headers";
 
         var div = document.querySelector("div.styled");
         div.style = "content:url(" + css_url + ")";
-        css_test.step_timeout(function() {
-            queryXhr(img_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, location.origin + "/");
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, location.origin + "/");
+            });
       }, "Image from inline styles.");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/image/internal-import-stylesheet.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/image/internal-import-stylesheet.html
@@ -2,42 +2,42 @@
 <html>
   <head>
     <title>CSS integration - image from imported stylesheet (internal)</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
     <p>Check that resources from stylesheets (imported from internal
     stylesheets) are loaded with the referrer and referrer policy from the
     document.</p>
 
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var url_prefix =  location.protocol + "//www1." + location.hostname + ":" + location.port + "/referrer-policy/generic/subresource/";
         var css_url = url_prefix + "stylesheet.py?id=" + id;
         var img_url = url_prefix + "image.py?report-headers&id=" + id;
 
         var style = document.createElement("style");
         style.type = 'text/css';
         style.appendChild(document.createTextNode("@import url('" + css_url + "');"));
         document.head.appendChild(style);
-        css_test.step_timeout(function() {
-            queryXhr(img_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, css_url);
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_url);
+            });
       }, "Image from imported stylesheet (internal).");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/image/internal-stylesheet.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/image/internal-stylesheet.html
@@ -2,40 +2,40 @@
 <html>
   <head>
     <title>CSS integration - image from internal stylesheet</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
     <p>Check that resources from internal stylesheets are loaded with
     the referrer and referrer policy from the document.</p>
 
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var css_url = location.protocol + "//www1." + location.hostname + ":" + location.port + "/referrer-policy/generic/subresource/image.py" + "?id=" + id;
         var img_url = css_url + "&report-headers";
 
         var style = document.createElement("style");
         style.type = 'text/css';
         style.appendChild(document.createTextNode("div.styled::before { content:url(" + css_url + ")}"));
         document.head.appendChild(style);
-        css_test.step_timeout(function() {
-          queryXhr(img_url, function(message) {
-            assert_own_property(message, "headers");
-            assert_own_property(message, "referrer");
-            assert_equals(message.referrer, location.origin + "/");
-            css_test.done();
-          }, null, null, css_test);
-        }, 1000);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, location.origin + "/");
+            });
       }, "Image from internal stylesheet.");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/image/presentation-attribute.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/image/presentation-attribute.html
@@ -2,35 +2,35 @@
 <html>
   <head>
     <title>CSS integration - image from presentation attribute</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
     <p>Check that resources from presentation attributes are loaded with
     the referrer and referrer policy from the document.</p>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var css_url = location.protocol + "//www1." + location.hostname + ":" + location.port + "/referrer-policy/generic/subresource/image.py" + "?id=" + id;
         var img_url = css_url + "&report-headers";
 
         document.body.background = css_url;
-        css_test.step_timeout(function() {
-            queryXhr(img_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, location.origin + "/");
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, location.origin + "/");
+            });
       }, "Image from presentation attributes.");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/image/processing-instruction.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/image/processing-instruction.html
@@ -2,41 +2,41 @@
 <html>
   <head>
     <title>CSS integration - image from external stylesheet inserted via a ProcessingInstruction</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
     <p>Check that resources from external stylesheets (referenced from a
     ProcessingInstruction) are loaded with the referrer and referrer policy
     from the external stylesheet.</p>
 
     <div class="styled"></div>
 
     <script>
-      async_test(function(css_test) {
+      promise_test(function(css_test) {
         var id = token();
         var url_prefix = location.protocol + "//www1." + location.hostname + ":" + location.port;
         var css_url = url_prefix + "/referrer-policy/generic/subresource/stylesheet.py?id=" + id;
         var img_url = url_prefix + "/referrer-policy/generic/subresource/image.py" +
                       "?id=" + id + "&report-headers";
 
         var processingInstruction = document.createProcessingInstruction("xml-stylesheet", "href=\"" + css_url + "\" type=\"text/css\"");
-        css_test.step_timeout(function() {
-            queryXhr(img_url, function(message) {
-                assert_own_property(message, "headers");
-                assert_own_property(message, "referrer");
-                assert_equals(message.referrer, css_url);
-                css_test.done();
-            }, null, null, css_test);
-        }, 1000);
         document.insertBefore(processingInstruction, document.firstChild);
+        return timeoutPromise(css_test, 1000)
+          .then(() => requestViaXhr(img_url))
+          .then(function(message) {
+              assert_own_property(message, "headers");
+              assert_own_property(message, "referrer");
+              assert_equals(message.referrer, css_url);
+            });
       }, "Image from external stylesheet (from ProcessingInstruction).");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/css-integration/svg/external-stylesheet.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/svg/external-stylesheet.html
@@ -2,16 +2,17 @@
 <html>
   <head>
     <title>CSS integration - styling SVG from external stylesheet</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <!-- Helper functions for referrer-policy css tests. -->
     <script src="/referrer-policy/css-integration/css-test-helper.js"></script>
     <meta name="referrer" content="never">
   </head>
   <body>
     <p>Check that resources from external stylesheets are loaded with
     the referrer and referrer policy from the external stylesheet.</p>
 
--- a/testing/web-platform/tests/referrer-policy/css-integration/svg/inline-style.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/svg/inline-style.html
@@ -2,16 +2,17 @@
 <html>
   <head>
     <title>CSS integration - styling SVG from inline style</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <!-- Helper functions for referrer-policy css tests. -->
     <script src="/referrer-policy/css-integration/css-test-helper.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
     <p>Check that resources from inline styles are loaded with
     the referrer and referrer policy from the document.</p>
     <script>
--- a/testing/web-platform/tests/referrer-policy/css-integration/svg/internal-stylesheet.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/svg/internal-stylesheet.html
@@ -2,16 +2,17 @@
 <html>
   <head>
     <title>CSS integration - styling SVG from internal style</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <!-- Helper functions for referrer-policy css tests. -->
     <script src="/referrer-policy/css-integration/css-test-helper.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
     <p>Check that resources from internal styles are loaded with
     the referrer and referrer policy from the document.</p>
     <script>
--- a/testing/web-platform/tests/referrer-policy/css-integration/svg/presentation-attribute.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/svg/presentation-attribute.html
@@ -3,16 +3,17 @@
   <head>
     <title>CSS integration - styling SVG from external stylesheet from
            presentation attribute</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <!-- Helper functions for referrer-policy css tests. -->
     <script src="/referrer-policy/css-integration/css-test-helper.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
     <p>Check that resources from presentation attributes are loaded with
     the referrer and referrer policy from the document.</p>
    <script>
--- a/testing/web-platform/tests/referrer-policy/css-integration/svg/processing-instruction.html
+++ b/testing/web-platform/tests/referrer-policy/css-integration/svg/processing-instruction.html
@@ -3,16 +3,17 @@
   <head>
     <title>CSS integration - styling SVG from external stylesheet via
            ProcessingInstruction</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <script src="/common/utils.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <!-- Helper functions for referrer-policy css tests. -->
     <script src="/referrer-policy/css-integration/css-test-helper.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body>
     <p>Check that resources from external stylesheets (referenced from a
     ProcessingInstruction) are loaded with the referrer and referrer policy
     from the external stylesheet.</p>
--- a/testing/web-platform/tests/referrer-policy/generic/common.js
+++ b/testing/web-platform/tests/referrer-policy/generic/common.js
@@ -1,59 +1,272 @@
-// NOTE: This method only strips the fragment and is not in accordance to the
-// recommended draft specification:
-// https://w3c.github.io/webappsec/specs/referrer-policy/#null
-// TODO(kristijanburnik): Implement this helper as defined by spec once added
-// scenarios for URLs containing username/password/etc.
-function stripUrlForUseAsReferrer(url) {
-  return url.replace(/#.*$/, "");
+/**
+ * @fileoverview Utilities for mixed-content in Web Platform Tests.
+ * @author burnik@google.com (Kristijan Burnik)
+ * Disclaimer: Some methods of other authors are annotated in the corresponding
+ *     method's JSDoc.
+ */
+
+// The same content is placed as
+// - wpt/referrer-policy/generic/common.js and
+// - wpt/mixed-content/generic/common.js.
+// If you modify either one, please also update the other one.
+//
+// TODO(https://crbug.com/906850): These two files are going to be merged.
+// Currently they are duplicated only to avoid frequent mass modification
+// for each step of refactoring, as these file names are hard-coded in
+// a large number of generated test files.
+
+function timeoutPromise(t, ms) {
+  return new Promise(resolve => { t.step_timeout(resolve, ms); });
+}
+
+/**
+ * Normalizes the target port for use in a URL. For default ports, this is the
+ *     empty string (omitted port), otherwise it's a colon followed by the port
+ *     number. Ports 80, 443 and an empty string are regarded as default ports.
+ * @param {number} targetPort The port to use
+ * @return {string} The port portion for using as part of a URL.
+ */
+function getNormalizedPort(targetPort) {
+  return ([80, 443, ""].indexOf(targetPort) >= 0) ? "" : ":" + targetPort;
+}
+
+/**
+ * Creates a GUID.
+ *     See: https://en.wikipedia.org/wiki/Globally_unique_identifier
+ *     Original author: broofa (http://www.broofa.com/)
+ *     Sourced from: http://stackoverflow.com/a/2117523/4949715
+ * @return {string} A pseudo-random GUID.
+ */
+function guid() {
+  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+    var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
+    return v.toString(16);
+  });
+}
+
+/**
+ * Initiates a new XHR via GET.
+ * @param {string} url The endpoint URL for the XHR.
+ * @param {string} responseType Optional - how should the response be parsed.
+ *     Default is "json".
+ *     See: https://xhr.spec.whatwg.org/#dom-xmlhttprequest-responsetype
+ * @return {Promise} A promise wrapping the success and error events.
+ */
+function xhrRequest(url, responseType) {
+  return new Promise(function(resolve, reject) {
+    var xhr = new XMLHttpRequest();
+    xhr.open('GET', url, true);
+    xhr.responseType = responseType || "json";
+
+    xhr.addEventListener("error", function() {
+      reject(Error("Network Error"));
+    });
+
+    xhr.addEventListener("load", function() {
+      if (xhr.status != 200)
+        reject(Error(xhr.statusText));
+      else
+        resolve(xhr.response);
+    });
+
+    xhr.send();
+  });
+}
+
+/**
+ * Sets attributes on a given DOM element.
+ * @param {DOMElement} element The element on which to set the attributes.
+ * @param {object} An object with keys (serving as attribute names) and values.
+ */
+function setAttributes(el, attrs) {
+  attrs = attrs || {}
+  for (var attr in attrs)
+    el.setAttribute(attr, attrs[attr]);
+}
+
+/**
+ * Binds to success and error events of an object wrapping them into a promise
+ *     available through {@code element.eventPromise}. The success event
+ *     resolves and error event rejects.
+ * This method adds event listeners, and then removes all the added listeners
+ * when one of listened event is fired.
+ * @param {object} element An object supporting events on which to bind the
+ *     promise.
+ * @param {string} resolveEventName [="load"] The event name to bind resolve to.
+ * @param {string} rejectEventName [="error"] The event name to bind reject to.
+ */
+function bindEvents(element, resolveEventName, rejectEventName) {
+  element.eventPromise =
+      bindEvents2(element, resolveEventName, element, rejectEventName);
 }
 
-function parseUrlQueryString(queryString) {
-  var queries = queryString.replace(/^\?/, "").split("&");
-  var params = {};
+// Returns a promise wrapping success and error events of objects.
+// This is a variant of bindEvents that can accept separate objects for each
+// events and two events to reject, and doesn't set `eventPromise`.
+//
+// When `resolveObject`'s `resolveEventName` event (default: "load") is
+// fired, the promise is resolved with the event.
+//
+// When `rejectObject`'s `rejectEventName` event (default: "error") or
+// `rejectObject2`'s `rejectEventName2` event (default: "error") is
+// fired, the promise is rejected.
+//
+// `rejectObject2` is optional.
+function bindEvents2(resolveObject, resolveEventName, rejectObject, rejectEventName, rejectObject2, rejectEventName2) {
+  return new Promise(function(resolve, reject) {
+    const actualResolveEventName = resolveEventName || "load";
+    const actualRejectEventName = rejectEventName || "error";
+    const actualRejectEventName2 = rejectEventName2 || "error";
 
-  for (var i in queries) {
-    var kvp = queries[i].split("=");
-    params[kvp[0]] = kvp[1];
-  }
-
-  return params;
-};
+    const resolveHandler = function(event) {
+      cleanup();
+      resolve(event);
+    };
 
-function appendIframeToBody(url, attributes) {
-  var iframe = document.createElement("iframe");
-  iframe.src = url;
-  // Extend element with attributes. (E.g. "referrerPolicy" or "rel")
-  if (attributes) {
-    for (var attr in attributes) {
-      iframe[attr] = attributes[attr];
+    const rejectHandler = function(event) {
+      // Chromium starts propagating errors from worker.onerror to
+      // window.onerror. This handles the uncaught exceptions in tests.
+      event.preventDefault();
+      cleanup();
+      reject(event);
+    };
+
+    const cleanup = function() {
+      resolveObject.removeEventListener(actualResolveEventName, resolveHandler);
+      rejectObject.removeEventListener(actualRejectEventName, rejectHandler);
+      if (rejectObject2) {
+        rejectObject2.removeEventListener(actualRejectEventName2, rejectHandler);
+      }
+    };
+
+    resolveObject.addEventListener(actualResolveEventName, resolveHandler);
+    rejectObject.addEventListener(actualRejectEventName, rejectHandler);
+    if (rejectObject2) {
+      rejectObject2.addEventListener(actualRejectEventName2, rejectHandler);
     }
-  }
-  document.body.appendChild(iframe);
-
-  return iframe;
+  });
 }
 
-function loadImageInWindow(src, callback, attributes, w) {
-  var image = new w.Image();
-  image.crossOrigin = "Anonymous";
-  image.onload = function() {
-    callback(image);
-  }
+/**
+ * Creates a new DOM element.
+ * @param {string} tagName The type of the DOM element.
+ * @param {object} attrs A JSON with attributes to apply to the element.
+ * @param {DOMElement} parent Optional - an existing DOM element to append to
+ *     If not provided, the returned element will remain orphaned.
+ * @param {boolean} doBindEvents Optional - Whether to bind to load and error
+ *     events and provide the promise wrapping the events via the element's
+ *     {@code eventPromise} property. Default value evaluates to false.
+ * @return {DOMElement} The newly created DOM element.
+ */
+function createElement(tagName, attrs, parentNode, doBindEvents) {
+  var element = document.createElement(tagName);
+
+  if (doBindEvents)
+    bindEvents(element);
+
+  // We set the attributes after binding to events to catch any
+  // event-triggering attribute changes. E.g. form submission.
+  //
+  // But be careful with images: unlike other elements they will start the load
+  // as soon as the attr is set, even if not in the document yet, and sometimes
+  // complete it synchronously, so the append doesn't have the effect we want.
+  // So for images, we want to set the attrs after appending, whereas for other
+  // elements we want to do it before appending.
+  var isImg = (tagName == "img");
+  if (!isImg)
+    setAttributes(element, attrs);
+
+  if (parentNode)
+    parentNode.appendChild(element);
+
+  if (isImg)
+    setAttributes(element, attrs);
+
+  return element;
+}
+
+function createRequestViaElement(tagName, attrs, parentNode) {
+  return createElement(tagName, attrs, parentNode, true).eventPromise;
+}
+
+/**
+ * Creates a new empty iframe and appends it to {@code document.body} .
+ * @param {string} name The name and ID of the new iframe.
+ * @param {boolean} doBindEvents Whether to bind load and error events.
+ * @return {DOMElement} The newly created iframe.
+ */
+function createHelperIframe(name, doBindEvents) {
+  return createElement("iframe",
+                       {"name": name, "id": name},
+                       document.body,
+                       doBindEvents);
+}
 
-  // Extend element with attributes. (E.g. "referrerPolicy" or "rel")
-  if (attributes) {
-    for (var attr in attributes) {
-      image[attr] = attributes[attr];
+/**
+ * requestVia*() functions return promises that are resolved on successful
+ * requests with objects of the same "type", i.e. objects that contains
+ * the same sets of keys that are fixed within one category of tests (e.g.
+ * within wpt/referrer-policy tests).
+ * wrapResult() (that should be defined outside this file) is used to convert
+ * the response bodies of subresources into the expected result objects in some
+ * cases, and in other cases the result objects are constructed more directly.
+ * TODO(https://crbug.com/906850): Clean up the semantics around this, e.g.
+ * use (or not use) wrapResult() consistently, unify the arguments, etc.
+ */
+
+/**
+ * Creates a new iframe, binds load and error events, sets the src attribute and
+ *     appends it to {@code document.body} .
+ * @param {string} url The src for the iframe.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaIframe(url, additionalAttributes) {
+  const iframe = createElement(
+      "iframe",
+      Object.assign({"src": url}, additionalAttributes),
+      document.body,
+      false);
+  return bindEvents2(window, "message", iframe, "error", window, "error")
+      .then(event => {
+          assert_equals(event.source, iframe.contentWindow);
+          return event.data;
+        });
+}
+
+/**
+ * Creates a new image, binds load and error events, sets the src attribute and
+ *     appends it to {@code document.body} .
+ * @param {string} url The src for the image.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaImage(url) {
+  return createRequestViaElement("img", {"src": url}, document.body);
+}
+
+// Helpers for requestViaImageForReferrerPolicy().
+function loadImageInWindow(src, attributes, w) {
+  return new Promise((resolve, reject) => {
+    var image = new w.Image();
+    image.crossOrigin = "Anonymous";
+    image.onload = function() {
+      resolve(image);
+    };
+
+    // Extend element with attributes. (E.g. "referrerPolicy" or "rel")
+    if (attributes) {
+      for (var attr in attributes) {
+        image[attr] = attributes[attr];
+      }
     }
-  }
 
-  image.src = src;
-  w.document.body.appendChild(image)
+    image.src = src;
+    w.document.body.appendChild(image)
+  });
 }
 
 function extractImageData(img) {
     var canvas = document.createElement("canvas");
     var context = canvas.getContext('2d');
     context.drawImage(img, 0, 0);
     var imgData = context.getImageData(0, 0, img.clientWidth, img.clientHeight);
     return imgData.data;
@@ -78,189 +291,409 @@ function decodeImageData(rgba) {
 
   // Remove trailing nulls from data.
   rgb = rgb.subarray(0, rgb_length);
   var string_data = (new TextDecoder("ascii")).decode(rgb);
 
   return JSON.parse(string_data);
 }
 
-function normalizePort(targetPort) {
-  var defaultPorts = [80, 443];
-  var isDefaultPortForProtocol = (defaultPorts.indexOf(targetPort) >= 0);
-
-  return (targetPort == "" || isDefaultPortForProtocol) ?
-          "" : ":" + targetPort;
-}
-
-function wrapResult(url, server_data) {
-  return {
-    location: url,
-    referrer: server_data.headers.referer,
-    headers: server_data.headers
-  }
-}
-
-function queryIframe(url, callback, attributes, referrer_policy, test) {
-  var iframe = appendIframeToBody(url, attributes);
-  var listener = test.step_func(function(event) {
-    if (event.source != iframe.contentWindow)
-      return;
-
-    callback(event.data, url);
-    window.removeEventListener("message", listener);
-  });
-  window.addEventListener("message", listener);
-}
-
-function queryImage(url, callback, attributes, referrerPolicy, test) {
+// A variant of requestViaImage for referrer policy tests.
+// This tests many patterns of <iframe>s to test referrer policy inheritance.
+// TODO(https://crbug.com/906850): Merge this into requestViaImage().
+// <iframe>-related code should be moved outside requestViaImage*().
+function requestViaImageForReferrerPolicy(url, attributes, referrerPolicy) {
   // For images, we'll test:
   // - images in a `srcdoc` frame to ensure that it uses the referrer
   //   policy of its parent,
   // - images in a top-level document,
   // - and images in a `srcdoc` frame with its own referrer policy to
   //   override its parent.
 
+  var iframeWithoutOwnPolicy = document.createElement('iframe');
   var noSrcDocPolicy = new Promise((resolve, reject) => {
-    var iframeWithoutOwnPolicy = document.createElement('iframe');
-    iframeWithoutOwnPolicy.srcdoc = "Hello, world.";
-    iframeWithoutOwnPolicy.onload = test.step_func(function () {
-      var nextUrl = url + "&cache_destroyer2=" + (new Date()).getTime();
-      loadImageInWindow(nextUrl, test.step_func(function (img) {
-        resolve(decodeImageData(extractImageData(img)));
-      }), attributes, iframeWithoutOwnPolicy.contentWindow);
-    });
-    document.body.appendChild(iframeWithoutOwnPolicy);
-  });
+        iframeWithoutOwnPolicy.srcdoc = "Hello, world.";
+        iframeWithoutOwnPolicy.onload = resolve;
+        document.body.appendChild(iframeWithoutOwnPolicy);
+      })
+    .then(() => {
+        var nextUrl = url + "&cache_destroyer2=" + (new Date()).getTime();
+        return loadImageInWindow(nextUrl, attributes,
+                                 iframeWithoutOwnPolicy.contentWindow);
+      })
+    .then(function (img) {
+        return decodeImageData(extractImageData(img));
+      });
 
   // Give a srcdoc iframe a referrer policy different from the top-level page's policy.
   var iframePolicy = (referrerPolicy === "no-referrer") ? "unsafe-url" : "no-referrer";
+  var iframeWithOwnPolicy = document.createElement('iframe');
   var srcDocPolicy = new Promise((resolve, reject) => {
-    var iframeWithOwnPolicy = document.createElement('iframe');
-    iframeWithOwnPolicy.srcdoc = "<meta name='referrer' content='" + iframePolicy + "'>Hello world.";
+        iframeWithOwnPolicy.srcdoc = "<meta name='referrer' content='" + iframePolicy + "'>Hello world.";
+        iframeWithOwnPolicy.onload = resolve;
+        document.body.appendChild(iframeWithOwnPolicy);
+      })
+    .then(() => {
+        var nextUrl = url + "&cache_destroyer3=" + (new Date()).getTime();
+        return loadImageInWindow(nextUrl, null,
+                                 iframeWithOwnPolicy.contentWindow);
+      })
+    .then(function (img) {
+        return decodeImageData(extractImageData(img));
+      });
 
-    iframeWithOwnPolicy.onload = test.step_func(function () {
-      var nextUrl = url + "&cache_destroyer3=" + (new Date()).getTime();
-      loadImageInWindow(nextUrl, test.step_func(function (img) {
-        resolve(decodeImageData(extractImageData(img)));
-      }), null, iframeWithOwnPolicy.contentWindow);
-    });
-    document.body.appendChild(iframeWithOwnPolicy);
-  });
+  var pagePolicy = loadImageInWindow(url, attributes, window)
+    .then(function (img) {
+        return decodeImageData(extractImageData(img));
+      });
 
-  var pagePolicy = new Promise((resolve, reject) => {
-    loadImageInWindow(url, test.step_func(function (img) {
-      resolve(decodeImageData(extractImageData(img)));
-    }), attributes, window);
-  });
-
-  Promise.all([noSrcDocPolicy, srcDocPolicy, pagePolicy]).then(test.step_func(values => {
+  return Promise.all([noSrcDocPolicy, srcDocPolicy, pagePolicy]).then(values => {
     assert_equals(values[0].headers.referer, values[2].headers.referer, "Referrer inside 'srcdoc' without its own policy should be the same as embedder's referrer.");
     assert_equals((iframePolicy === "no-referrer" ? undefined : document.location.href), values[1].headers.referer, "Referrer inside 'srcdoc' should use the iframe's policy if it has one");
-    callback(wrapResult(url, values[2]), url);
-  }));
-}
-
-function queryXhr(url, callback, attributes, referrer_policy, test) {
-  var xhr = new XMLHttpRequest();
-  xhr.open('GET', url, true);
-  xhr.onreadystatechange = test.step_func(function(e) {
-    if (xhr.readyState == 4 && xhr.status == 200) {
-      var server_data = JSON.parse(xhr.responseText);
-      callback(wrapResult(url, server_data), url);
-    }
-  });
-  xhr.send();
-}
-
-function queryWorker(url, callback, attributes, referrer_policy, test) {
-  var worker = new Worker(url);
-  worker.onmessage = test.step_func(function(event) {
-    var server_data = event.data;
-    callback(wrapResult(url, server_data), url);
-  });
-}
-
-function queryModuleWorkerTopLevel(url, callback, attributes, referrer_policy, test) {
-  var worker = new Worker(url, {type: "module"});
-  worker.onmessage = test.step_func(function(event) {
-    var server_data = event.data;
-    callback(wrapResult(url, server_data), url);
+    return wrapResult(values[2]);
   });
 }
 
-function querySharedWorker(url, callback, attributes, referrer_policy, test) {
-  var worker = new SharedWorker(url);
-  worker.port.onmessage = test.step_func(function(event) {
-    var server_data = event.data;
-    callback(wrapResult(url, server_data), url);
-  });
+/**
+ * Initiates a new XHR GET request to provided URL.
+ * @param {string} url The endpoint URL for the XHR.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaXhr(url) {
+  return xhrRequest(url).then(result => wrapResult(result));
+}
+
+/**
+ * Initiates a new GET request to provided URL via the Fetch API.
+ * @param {string} url The endpoint URL for the Fetch.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaFetch(url) {
+  return fetch(url)
+    .then(res => res.json())
+    .then(j => wrapResult(j));
+}
+
+function dedicatedWorkerUrlThatFetches(url) {
+  return `data:text/javascript,
+    fetch('${url}')
+      .then(() => postMessage(''),
+            () => postMessage(''));`;
+}
+
+function workerUrlThatImports(url) {
+  return `data:text/javascript,import '${url}';`;
+}
+
+/**
+ * Creates a new Worker, binds message and error events wrapping them into.
+ *     {@code worker.eventPromise} and posts an empty string message to start
+ *     the worker.
+ * @param {string} url The endpoint URL for the worker script.
+ * @param {object} options The options for Worker constructor.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaDedicatedWorker(url, options) {
+  var worker;
+  try {
+    worker = new Worker(url, options);
+  } catch (e) {
+    return Promise.reject(e);
+  }
+  worker.postMessage('');
+  return bindEvents2(worker, "message", worker, "error")
+    .then(event => wrapResult(event.data));
+}
+
+function requestViaSharedWorker(url) {
+  var worker;
+  try {
+    worker = new SharedWorker(url);
+  } catch(e) {
+    return Promise.reject(e);
+  }
+  const promise = bindEvents2(worker.port, "message", worker, "error")
+    .then(event => wrapResult(event.data));
+  worker.port.start();
+  return promise;
+}
+
+// Returns a reference to a worklet object corresponding to a given type.
+function get_worklet(type) {
+  if (type == 'animation')
+    return CSS.animationWorklet;
+  if (type == 'layout')
+    return CSS.layoutWorklet;
+  if (type == 'paint')
+    return CSS.paintWorklet;
+  if (type == 'audio')
+    return new OfflineAudioContext(2,44100*40,44100).audioWorklet;
+
+  assert_unreached('unknown worklet type is passed.');
+  return undefined;
 }
 
-function queryFetch(url, callback, attributes, referrer_policy, test) {
-  fetch(url).then(test.step_func(function(response) {
-      response.json().then(test.step_func(function(server_data) {
-        callback(wrapResult(url, server_data), url);
-      }));
-    })
-  );
+function requestViaWorklet(type, url) {
+  try {
+    return get_worklet(type).addModule(url);
+  } catch (e) {
+    return Promise.reject(e);
+  }
+}
+
+/**
+ * Sets the href attribute on a navigable DOM element and performs a navigation
+ *     by clicking it. To avoid navigating away from the current execution
+ *     context, a target attribute is set to point to a new helper iframe.
+ * @param {DOMElement} navigableElement The navigable DOMElement
+ * @param {string} url The href for the navigable element.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaNavigable(navigableElement, url) {
+  var iframe = createHelperIframe(guid(), false);
+  setAttributes(navigableElement,
+                {"href": url,
+                 "target": iframe.name});
+
+  const promise =
+    bindEvents2(window, "message", iframe, "error", window, "error")
+      .then(event => {
+          assert_equals(event.source, iframe.contentWindow, "event.source");
+          return event.data;
+        });
+  navigableElement.click();
+  return promise;
+}
+
+/**
+ * Creates a new anchor element, appends it to {@code document.body} and
+ *     performs the navigation.
+ * @param {string} url The URL to navigate to.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaAnchor(url, additionalAttributes) {
+  var a = createElement(
+      "a",
+      Object.assign({"innerHTML": "Link to resource"}, additionalAttributes),
+      document.body);
+
+  return requestViaNavigable(a, url);
+}
+
+/**
+ * Creates a new area element, appends it to {@code document.body} and performs
+ *     the navigation.
+ * @param {string} url The URL to navigate to.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaArea(url, additionalAttributes) {
+  var area = createElement(
+      "area",
+      Object.assign({}, additionalAttributes),
+      document.body);
+
+  // TODO(kristijanburnik): Append to map and add image.
+  return requestViaNavigable(area, url);
+}
+
+/**
+ * Creates a new script element, sets the src to url, and appends it to
+ *     {@code document.body}.
+ * @param {string} url The src URL.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaScript(url, additionalAttributes) {
+  const script = createElement(
+      "script",
+      Object.assign({"src": url}, additionalAttributes),
+      document.body,
+      false);
+
+  return bindEvents2(window, "message", script, "error", window, "error")
+    .then(event => wrapResult(event.data));
 }
 
-function queryNavigable(element, url, callback, attributes, test) {
-  var navigable = element
-  navigable.href = url;
-  navigable.target = "helper-iframe";
+/**
+ * Creates a new form element, sets attributes, appends it to
+ *     {@code document.body} and submits the form.
+ * @param {string} url The URL to submit to.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaForm(url) {
+  var iframe = createHelperIframe(guid());
+  var form = createElement("form",
+                           {"action": url,
+                            "method": "POST",
+                            "target": iframe.name},
+                           document.body);
+  bindEvents(iframe);
+  form.submit();
 
-  var helperIframe = document.createElement("iframe")
-  helperIframe.name = "helper-iframe"
-  document.body.appendChild(helperIframe)
+  return iframe.eventPromise;
+}
+
+/**
+ * Creates a new link element for a stylesheet, binds load and error events,
+ *     sets the href to url and appends it to {@code document.head}.
+ * @param {string} url The URL for a stylesheet.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaLinkStylesheet(url) {
+  return createRequestViaElement("link",
+                                 {"rel": "stylesheet", "href": url},
+                                 document.head);
+}
 
-  // Extend element with attributes. (E.g. "referrer_policy" or "rel")
-  if (attributes) {
-    for (var attr in attributes) {
-      navigable[attr] = attributes[attr];
-    }
+/**
+ * Creates a new link element for a prefetch, binds load and error events, sets
+ *     the href to url and appends it to {@code document.head}.
+ * @param {string} url The URL of a resource to prefetch.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaLinkPrefetch(url) {
+  var link = document.createElement('link');
+  if (link.relList && link.relList.supports && link.relList.supports("prefetch")) {
+    return createRequestViaElement("link",
+                                   {"rel": "prefetch", "href": url},
+                                   document.head);
+  } else {
+    return Promise.reject("This browser does not support 'prefetch'.");
   }
+}
 
-  var listener = test.step_func(function(event) {
-    if (event.source != helperIframe.contentWindow)
-      return;
-    callback(event.data, url);
-    window.removeEventListener("message", listener);
-  });
-  window.addEventListener("message", listener);
-
-  navigable.click();
+/**
+ * Initiates a new beacon request.
+ * @param {string} url The URL of a resource to prefetch.
+ * @return {Promise} The promise for success/error events.
+ */
+async function requestViaSendBeacon(url) {
+  function wait(ms) {
+    return new Promise(resolve => step_timeout(resolve, ms));
+  }
+  if (!navigator.sendBeacon(url)) {
+    // If mixed-content check fails, it should return false.
+    throw new Error('sendBeacon() fails.');
+  }
+  // We don't have a means to see the result of sendBeacon() request
+  // for sure. Let's wait for a while and let the generic test function
+  // ask the server for the result.
+  await wait(500);
+  return 'allowed';
 }
 
-function queryLink(url, callback, attributes, referrer_policy, test) {
-  var a = document.createElement("a");
-  a.innerHTML = "Link to subresource";
-  document.body.appendChild(a);
-  queryNavigable(a, url, callback, attributes, test)
+/**
+ * Creates a new media element with a child source element, binds loadeddata and
+ *     error events, sets attributes and appends to document.body.
+ * @param {string} type The type of the media element (audio/video/picture).
+ * @param {object} media_attrs The attributes for the media element.
+ * @param {object} source_attrs The attributes for the child source element.
+ * @return {DOMElement} The newly created media element.
+ */
+function createMediaElement(type, media_attrs, source_attrs) {
+  var mediaElement = createElement(type, {});
+
+  var sourceElement = createElement("source", {});
+
+  mediaElement.eventPromise = new Promise(function(resolve, reject) {
+    mediaElement.addEventListener("loadeddata", function (e) {
+      resolve(e);
+    });
+
+    // Safari doesn't fire an `error` event when blocking mixed content.
+    mediaElement.addEventListener("stalled", function(e) {
+      reject(e);
+    });
+
+    sourceElement.addEventListener("error", function(e) {
+      reject(e);
+    });
+  });
+
+  setAttributes(mediaElement, media_attrs);
+  setAttributes(sourceElement, source_attrs);
+
+  mediaElement.appendChild(sourceElement);
+  document.body.appendChild(mediaElement);
+
+  return mediaElement;
 }
 
-function queryAreaLink(url, callback, attributes, referrer_policy, test) {
-  var area = document.createElement("area");
-  // TODO(kristijanburnik): Append to map and add image.
-  document.body.appendChild(area);
-  queryNavigable(area, url, callback, attributes, test)
+/**
+ * Creates a new video element, binds loadeddata and error events, sets
+ *     attributes and source URL and appends to {@code document.body}.
+ * @param {string} url The URL of the video.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaVideo(url) {
+  return createMediaElement("video",
+                            {},
+                            {"src": url}).eventPromise;
 }
 
-function queryScript(url, callback, attributes, referrer_policy, test) {
-  var script = document.createElement("script");
-  script.src = url;
-  script.referrerPolicy = referrer_policy;
+/**
+ * Creates a new audio element, binds loadeddata and error events, sets
+ *     attributes and source URL and appends to {@code document.body}.
+ * @param {string} url The URL of the audio.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaAudio(url) {
+  return createMediaElement("audio",
+                            {},
+                            {"type": "audio/wav", "src": url}).eventPromise;
+}
 
-  var listener = test.step_func(function(event) {
-    var server_data = event.data;
-    callback(wrapResult(url, server_data), url);
-    window.removeEventListener("message", listener);
-  });
-  window.addEventListener("message", listener);
+/**
+ * Creates a new picture element, binds loadeddata and error events, sets
+ *     attributes and source URL and appends to {@code document.body}. Also
+ *     creates new image element appending it to the picture
+ * @param {string} url The URL of the image for the source and image elements.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaPicture(url) {
+  var picture = createMediaElement("picture", {}, {"srcset": url,
+                                                "type": "image/png"});
+  return createRequestViaElement("img", {"src": url}, picture);
+}
 
-  document.body.appendChild(script);
+/**
+ * Creates a new object element, binds load and error events, sets the data to
+ *     url, and appends it to {@code document.body}.
+ * @param {string} url The data URL.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaObject(url) {
+  return createRequestViaElement("object", {"data": url, "type": "text/html"}, document.body);
 }
 
- // SanityChecker does nothing in release mode.
+/**
+ * Creates a new WebSocket pointing to {@code url} and sends a message string
+ * "echo". The {@code message} and {@code error} events are triggering the
+ * returned promise resolve/reject events.
+ * @param {string} url The URL for WebSocket to connect to.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaWebSocket(url) {
+  return new Promise(function(resolve, reject) {
+    var websocket = new WebSocket(url);
+
+    websocket.addEventListener("message", function(e) {
+      resolve(e.data);
+    });
+
+    websocket.addEventListener("open", function(e) {
+      websocket.send("echo");
+    });
+
+    websocket.addEventListener("error", function(e) {
+      reject(e)
+    });
+  })
+  .then(data => {
+      return JSON.parse(data);
+    });
+}
+
+// SanityChecker does nothing in release mode. See sanity-checker.js for debug
+// mode.
 function SanityChecker() {}
 SanityChecker.prototype.checkScenario = function() {};
+SanityChecker.prototype.setFailTimeout = function(test, timeout) {};
 SanityChecker.prototype.checkSubresourceResult = function() {};
--- a/testing/web-platform/tests/referrer-policy/generic/iframe-inheritance.html
+++ b/testing/web-platform/tests/referrer-policy/generic/iframe-inheritance.html
@@ -3,16 +3,17 @@
   <head>
     <title>Referrer Policy: iframes correctly inherit the ancestor's referrer</title>
     <link rel="author" title="Jochen Eisinger" href="mailto:jochen@chromium.org">
     <link rel="help" href="https://www.w3.org/TR/referrer-policy/#referrer-policy-delivery-nested">
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
     <meta name="referrer" content="origin">
   </head>
   <body onload="runTest()">
     <h1>Referrer Policy: iframes correctly inherit the ancestor's referrer</h1>
     <script>
       var test = async_test("iframes correctly inherit the ancestor's referrer");
       window.addEventListener("message", test.step_func((msg) => {
         assert_equals(msg.data.referrer, document.location.origin + "/");
@@ -20,20 +21,24 @@
       }));
 
       function runTest() {
         var iframe = document.createElement("iframe");
         iframe.src = "about:blank";
         document.body.appendChild(iframe);
         iframe.contentDocument.write(`
             <script src = "/referrer-policy/generic/common.js"></` + `script>
+            <script src = "/referrer-policy/generic/referrer-policy-test-case.js"></` + `script>
             <script>
               var urlPath = "/referrer-policy/generic/subresource/xhr.py";
               var url = "${location.protocol}//www1.${location.hostname}:${location.port}" + urlPath;
-              queryXhr(url, (msg) => {
-                  parent.postMessage({referrer: msg.referrer}, "*")});
+              requestViaXhr(url).then((msg) => {
+                  parent.postMessage({referrer: msg.referrer}, "*")})
+                .catch((e) => {
+                    parent.postMessage({referrer: "FAILURE"}, "*");
+                  });
             </` + "script>");
         }
       </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/link-rel-prefetch.html
+++ b/testing/web-platform/tests/referrer-policy/generic/link-rel-prefetch.html
@@ -9,25 +9,21 @@
     <meta name="referrer" content="origin">
     <link rel="prefetch" href="/referrer-policy/generic/subresource/image.py">
   </head>
   <body>
     <p>Check that resources loaded via link rel prefetch use the referrer
     and referrer policy from the document.</p>
 
     <script>
-      var prefetch_test = async_test("Prefetched image.");
-
       var img_url = "/referrer-policy/generic/subresource/image.py";
-      prefetch_test.step_timeout(
-          function() {
-            loadImageInWindow(img_url, function (img) {
-              var message = decodeImageData(extractImageData(img));
-              prefetch_test.step(function() { assert_equals(message.headers.referer, document.location.origin + "/")});
-              prefetch_test.done();
-            }, null, window);
-          },
-          1000);
+      promise_test((t) => timeoutPromise(t, 1000)
+        .then(() => loadImageInWindow(img_url, null, window))
+        .then(function (img) {
+            var message = decodeImageData(extractImageData(img));
+            assert_equals(message.headers.referer, document.location.origin + "/");
+          }),
+        "Prefetched image.");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/multiple-headers-and-values.html
+++ b/testing/web-platform/tests/referrer-policy/generic/multiple-headers-and-values.html
@@ -1,29 +1,31 @@
 <!DOCTYPE html>
 <html>
   <head>
     <title>Referrer Policy: multiple Referrer-Policy header and header values are allowed</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
 
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: multiple Referrer-Policy header and header values are allowed</h1>
     <p></p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
-      var test = async_test("Image uses the last recognized Referrer-Policy header value");
-      var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
-      var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
-                urlPath;
-      queryImage(url, test.step_func(function(message) {
-        assert_equals(message.referrer, document.location.origin + "/");
-        test.done();
-      }), null, 'no-referrer', test);
+      promise_test(() => {
+        var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
+        var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
+                  urlPath;
+        return requestViaImageForReferrerPolicy(url, null, 'no-referrer')
+          .then(function(message) {
+              assert_equals(message.referrer, document.location.origin + "/");
+            });
+      }, "Image uses the last recognized Referrer-Policy header value");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/multiple-headers-combined.html
+++ b/testing/web-platform/tests/referrer-policy/generic/multiple-headers-combined.html
@@ -1,29 +1,31 @@
 <!DOCTYPE html>
 <html>
   <head>
     <title>Referrer Policy: multiple Referrer-Policy header values are allowed</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
 
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: multiple Referrer-Policy header values are allowed</h1>
     <p></p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
-      var test = async_test("Image uses the last recognized Referrer-Policy header value");
-      var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
-      var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
-                urlPath;
-      queryImage(url, test.step_func(function(message) {
-        assert_equals(message.referrer, document.location.origin + "/");
-        test.done();
-      }), null, 'no-referrer', test);
+      promise_test(() => {
+        var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
+        var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
+                  urlPath;
+        return requestViaImageForReferrerPolicy(url, null, 'no-referrer')
+          .then(function(message) {
+              assert_equals(message.referrer, document.location.origin + "/");
+            });
+      }, "Image uses the last recognized Referrer-Policy header value");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/multiple-headers-one-invalid.html
+++ b/testing/web-platform/tests/referrer-policy/generic/multiple-headers-one-invalid.html
@@ -1,29 +1,31 @@
 <!DOCTYPE html>
 <html>
   <head>
     <title>Referrer Policy: multiple Referrer-Policy headers with one invalid</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
 
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: multiple Referrer-Policy headers with one invalid</h1>
     <p></p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
-      var test = async_test("Referrer policy header parsing fails if one header is invalid");
-      var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
-      var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
-                urlPath;
-      queryImage(url, test.step_func(function(message) {
-        assert_equals(message.referrer, document.location.href);
-        test.done();
-      }), null, 'no-referrer', test);
+      promise_test(() => {
+        var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
+        var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
+                  urlPath;
+        return requestViaImageForReferrerPolicy(url, null, 'no-referrer')
+          .then(function(message) {
+              assert_equals(message.referrer, document.location.href);
+            });
+      }, "Referrer policy header parsing fails if one header is invalid");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/multiple-headers-one-unknown-token.html
+++ b/testing/web-platform/tests/referrer-policy/generic/multiple-headers-one-unknown-token.html
@@ -1,29 +1,31 @@
 <!DOCTYPE html>
 <html>
   <head>
     <title>Referrer Policy: multiple Referrer-Policy headers with one invalid</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
 
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: multiple Referrer-Policy headers with one invalid</h1>
     <p></p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
-      var test = async_test("Image uses last recognized referrer policy token from Referrer-Policy headers");
-      var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
-      var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
-                urlPath;
-      queryImage(url, test.step_func(function(message) {
-        assert_equals(message.referrer, document.location.origin + "/");
-        test.done();
-      }), null, 'no-referrer', test);
+      promise_test(() => {
+        var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
+        var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
+                  urlPath;
+        return requestViaImageForReferrerPolicy(url, null, 'no-referrer')
+          .then(function(message) {
+              assert_equals(message.referrer, document.location.origin + "/");
+            });
+      }, "Image uses last recognized referrer policy token from Referrer-Policy headers");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/multiple-headers.html
+++ b/testing/web-platform/tests/referrer-policy/generic/multiple-headers.html
@@ -1,29 +1,31 @@
 <!DOCTYPE html>
 <html>
   <head>
     <title>Referrer Policy: multiple Referrer-Policy headers are allowed</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
 
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: multiple Referrer-Policy headers are allowed</h1>
     <p></p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
-      var test = async_test("Image uses the last recognized Referrer-Policy header");
-      var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
-      var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
-                urlPath;
-      queryImage(url, test.step_func(function(message) {
-        assert_equals(message.referrer, document.location.origin + "/");
-        test.done();
-      }), null, 'no-referrer', test);
+      promise_test(() => {
+        var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
+        var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
+                  urlPath;
+        return requestViaImageForReferrerPolicy(url, null, 'no-referrer')
+          .then(function(message) {
+              assert_equals(message.referrer, document.location.origin + "/");
+            });
+      }, "Image uses the last recognized Referrer-Policy header");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/referrer-policy-test-case.js
+++ b/testing/web-platform/tests/referrer-policy/generic/referrer-policy-test-case.js
@@ -1,34 +1,58 @@
+function wrapResult(server_data) {
+  return {
+    referrer: server_data.headers.referer,
+    headers: server_data.headers
+  }
+}
+
+// NOTE: This method only strips the fragment and is not in accordance to the
+// recommended draft specification:
+// https://w3c.github.io/webappsec/specs/referrer-policy/#null
+// TODO(kristijanburnik): Implement this helper as defined by spec once added
+// scenarios for URLs containing username/password/etc.
+function stripUrlForUseAsReferrer(url) {
+  return url.replace(/#.*$/, "");
+}
+
+function normalizePort(targetPort) {
+  var defaultPorts = [80, 443];
+  var isDefaultPortForProtocol = (defaultPorts.indexOf(targetPort) >= 0);
+
+  return (targetPort == "" || isDefaultPortForProtocol) ?
+          "" : ":" + targetPort;
+}
+
 function ReferrerPolicyTestCase(scenario, testDescription, sanityChecker) {
   // Pass and skip rest of the test if browser does not support fetch.
   if (scenario.subresource == "fetch-request" && !window.fetch) {
     // TODO(kristijanburnik): This should be refactored.
     return {
       start: function() {
         test(function() { assert_true(true); },
              "[ReferrerPolicyTestCase] Skipping test: Fetch is not supported.");
       }
     };
   }
 
   // This check is A NOOP in release.
   sanityChecker.checkScenario(scenario);
 
   var subresourceInvoker = {
-    "a-tag": queryLink,
-    "area-tag": queryAreaLink,
-    "fetch-request": queryFetch,
-    "iframe-tag": queryIframe,
-    "img-tag":  queryImage,
-    "script-tag": queryScript,
-    "worker-request": queryWorker,
-    "module-worker": queryModuleWorkerTopLevel,
-    "shared-worker": querySharedWorker,
-    "xhr-request": queryXhr
+    "a-tag": requestViaAnchor,
+    "area-tag": requestViaArea,
+    "fetch-request": requestViaFetch,
+    "iframe-tag": requestViaIframe,
+    "img-tag":  requestViaImageForReferrerPolicy,
+    "script-tag": requestViaScript,
+    "worker-request": url => requestViaDedicatedWorker(url, {}),
+    "module-worker": url => requestViaDedicatedWorker(url, {type: "module"}),
+    "shared-worker": requestViaSharedWorker,
+    "xhr-request": requestViaXhr
   };
 
   var referrerUrlResolver = {
     "omitted": function() {
       return undefined;
     },
     "origin": function() {
       return self.origin + "/";
@@ -36,18 +60,16 @@ function ReferrerPolicyTestCase(scenario
     "stripped-referrer": function() {
       return stripUrlForUseAsReferrer(location.toString());
     }
   };
 
   var t = {
     _scenario: scenario,
     _testDescription: testDescription,
-    _subresourceUrl: null,
-    _expectedReferrerUrl: null,
     _constructSubresourceUrl: function() {
       // TODO(kristijanburnik): We should assert that these two domains are
       // different. E.g. If someone runs the tets over www, this would fail.
       var domainForOrigin = {
         "cross-origin":"{{domains[www1]}}",
         "same-origin": location.hostname
       };
 
@@ -55,74 +77,67 @@ function ReferrerPolicyTestCase(scenario
       // http://wptserve.readthedocs.org/en/latest/pipes.html#built-in-pipes
       var portForProtocol = {
         "http": parseInt("{{ports[http][0]}}"),
         "https": parseInt("{{ports[https][0]}}")
       }
 
       var targetPort = portForProtocol[t._scenario.target_protocol];
 
-      t._subresourceUrl = t._scenario.target_protocol + "://" +
-                          domainForOrigin[t._scenario.origin] +
-                          normalizePort(targetPort) +
-                          t._scenario["subresource_path"] +
-                          "?redirection=" + t._scenario["redirection"] +
-                          "&cache_destroyer=" + (new Date()).getTime();
+      return t._scenario.target_protocol + "://" +
+             domainForOrigin[t._scenario.origin] +
+             normalizePort(targetPort) +
+             t._scenario["subresource_path"] +
+             "?redirection=" + t._scenario["redirection"] +
+             "&cache_destroyer=" + (new Date()).getTime();
     },
 
     _constructExpectedReferrerUrl: function() {
-      t._expectedReferrerUrl = referrerUrlResolver[t._scenario.referrer_url]();
+      return referrerUrlResolver[t._scenario.referrer_url]();
     },
 
-    _invokeSubresource: function(callback, test) {
+    // Returns a promise.
+    _invokeSubresource: function(resourceRequestUrl) {
       var invoker = subresourceInvoker[t._scenario.subresource];
       // Depending on the delivery method, extend the subresource element with
       // these attributes.
       var elementAttributesForDeliveryMethod = {
         "attr-referrer":  {referrerPolicy: t._scenario.referrer_policy},
         "rel-noreferrer": {rel: "noreferrer"}
       };
 
       var delivery_method = t._scenario.delivery_method;
 
       if (delivery_method in elementAttributesForDeliveryMethod) {
-        invoker(t._subresourceUrl,
-                callback,
-                elementAttributesForDeliveryMethod[delivery_method],
-                t._scenario.referrer_policy,
-                test);
+        return invoker(resourceRequestUrl,
+                       elementAttributesForDeliveryMethod[delivery_method],
+                       t._scenario.referrer_policy);
       } else {
-        invoker(t._subresourceUrl, callback, null, t._scenario.referrer_policy, test);
+        return invoker(resourceRequestUrl, {}, t._scenario.referrer_policy);
       }
-
     },
 
     start: function() {
-      async_test(function(test) {
-
-        t._constructSubresourceUrl();
-        t._constructExpectedReferrerUrl();
-
-        t._invokeSubresource(test.step_func(function(result) {
-          // Check if the result is in valid format. NOOP in release.
-          sanityChecker.checkSubresourceResult(
-              test, t._scenario, t._subresourceUrl, result);
+      promise_test(test => {
+          const resourceRequestUrl = t._constructSubresourceUrl();
+          const expectedReferrerUrl = t._constructExpectedReferrerUrl();
+          return t._invokeSubresource(resourceRequestUrl)
+            .then(result => {
+                // Check if the result is in valid format. NOOP in release.
+                sanityChecker.checkSubresourceResult(
+                    test, t._scenario, resourceRequestUrl, result);
 
-          // Check the reported URL.
-          test.step(function() {
-            assert_equals(result.referrer,
-                          t._expectedReferrerUrl,
-                          "Reported Referrer URL is '" +
-                          t._scenario.referrer_url + "'.");
-            assert_equals(result.headers.referer,
-                          t._expectedReferrerUrl,
-                          "Reported Referrer URL from HTTP header is '" +
-                          t._expectedReferrerUrl + "'");
-          }, "Reported Referrer URL is as expected: " + t._scenario.referrer_url);
-
-          test.done();
-        }), test);
-      }, t._testDescription);
+                // Check the reported URL.
+                assert_equals(result.referrer,
+                              expectedReferrerUrl,
+                              "Reported Referrer URL is '" +
+                              t._scenario.referrer_url + "'.");
+                assert_equals(result.headers.referer,
+                              expectedReferrerUrl,
+                              "Reported Referrer URL from HTTP header is '" +
+                              expectedReferrerUrl + "'");
+              });
+        }, t._testDescription);
     }
   }
 
   return t;
 }
--- a/testing/web-platform/tests/referrer-policy/generic/sandboxed-iframe-with-opaque-origin.html
+++ b/testing/web-platform/tests/referrer-policy/generic/sandboxed-iframe-with-opaque-origin.html
@@ -3,16 +3,17 @@
   <head>
     <title>Referrer Policy: Sandboxed iframes with opaque origins don't send referrers</title>
     <link rel="author" title="Jochen Eisinger" href="mailto:jochen@chromium.org">
     <link rel="help" href="https://www.w3.org/TR/referrer-policy/#determine-requests-referrer">
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: A document with an opaque origin doesn't send referrers</h1>
     <script>
       function testSandboxedIframe(description, sandboxAttributes, expectedReferrer) {
         async_test(function(test) {
           window.addEventListener("message", test.step_func((msg) => {
             if (msg.data.description === description) {
@@ -21,22 +22,26 @@
             }
           }));
 
           var iframe = document.createElement("iframe");
           iframe.sandbox = sandboxAttributes;
           iframe.srcdoc = `
               <meta name = "referrer" content = "always">
               <script src = "/referrer-policy/generic/common.js"></` + `script>
+              <script src = "/referrer-policy/generic/referrer-policy-test-case.js"></` + `script>
               <script>
                 var urlPath = "/referrer-policy/generic/subresource/xhr.py";
                 var url = "${location.protocol}//www1.${location.hostname}:${location.port}" + urlPath;
-                queryXhr(url, (msg) => {
+                requestViaXhr(url).then((msg) => {
                     parent.postMessage({referrer: msg.referrer, description: "${description}"}, "*")
-                }, null, null, test);
+                  })
+                  .catch((e) => {
+                    parent.postMessage({referrer: "FAILURE", description: "${description}"}, "*")
+                  });
               </` + "script>";
           document.body.appendChild(iframe);
         }, description);
       }
 
       testSandboxedIframe("Sandboxed iframe with opaque origin doesn't send referrers.", "allow-scripts", undefined);
       testSandboxedIframe("Sandboxed iframe with tuple origin sends referrers.", "allow-same-origin allow-scripts", document.location.href);
     </script>
--- a/testing/web-platform/tests/referrer-policy/generic/subresource-test/area-navigate.html
+++ b/testing/web-platform/tests/referrer-policy/generic/subresource-test/area-navigate.html
@@ -2,37 +2,39 @@
 <!-- TODO(kristijanburnik): Remove subres. duplication. Reuse a template. -->
 <html>
   <head>
     <title>Area Link messaging - cross-origin Area Link navigation</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Area Link messaging - cross-origin Area Link navigation</h1>
     <p>If you can read JSON encoded HTTP request headers of the Area link below,
        the messaging works as expected.</p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
-        var urlPath = '/referrer-policy/generic/subresource/document.py';
-        var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
-                  urlPath;
-        queryAreaLink(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, null, messaging_test);
-      }, "Area is responding with HTTP headers");
+      promise_test(function() {
+          var urlPath = '/referrer-policy/generic/subresource/document.py';
+          var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
+                    urlPath;
+          return requestViaArea(url)
+            .then(function(message) {
+              var pre = document.getElementById('received_message')
+              var headers = message.headers;
+              pre.innerHTML = "";
+              pre.innerHTML += url + ":\n\n";
+              pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+              assert_own_property(headers, "host")
+              assert_own_property(headers, "connection")
+            });
+        },
+        "Area is responding with HTTP headers");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/subresource-test/fetch-messaging.html
+++ b/testing/web-platform/tests/referrer-policy/generic/subresource-test/fetch-messaging.html
@@ -2,46 +2,42 @@
 <!-- TODO(kristijanburnik): Remove subres. duplication. Reuse a template. -->
 <html>
   <head>
     <title>Fetch messaging - same-origin Fetch request</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Fetch messaging - same-origin Fetch request</h1>
     <p>If you can read JSON encoded HTTP request headers of the Fetch below,
        the messaging works as expected.</p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
       test(function() {
         assert_true(!!window.fetch, "Fetch is not supported by this browser.");
       }, "Fetch is supported by the browser.");
 
-      (function() {
-        if (!window.fetch)
-          return;
-
-        async_test(function(fetch_test) {
+      promise_test(function() {
           var urlPath = '/referrer-policy/generic/subresource/xhr.py';
           var url = location.protocol + "//" + location.hostname + ":" +
                     location.port + urlPath;
-          queryFetch(url, function(message) {
-            var pre = document.getElementById('received_message')
-            var headers = message.headers;
-            pre.innerHTML = "";
-            pre.innerHTML += url + ":\n\n";
-            pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n";
-            assert_own_property(headers, "host")
-            assert_own_property(headers, "connection")
-            fetch_test.done();
-          }, null, null, fetch_test);
-        }, "Fetch is responding with HTTP headers");
-      })();
+          return requestViaFetch(url)
+            .then(function(message) {
+                var pre = document.getElementById('received_message')
+                var headers = message.headers;
+                pre.innerHTML = "";
+                pre.innerHTML += url + ":\n\n";
+                pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n";
+                assert_own_property(headers, "host")
+                assert_own_property(headers, "connection")
+              });
+      }, "Fetch is responding with HTTP headers");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/subresource-test/iframe-messaging.html
+++ b/testing/web-platform/tests/referrer-policy/generic/subresource-test/iframe-messaging.html
@@ -2,37 +2,38 @@
 <!-- TODO(kristijanburnik): Remove subres. duplication. Reuse a template. -->
 <html>
   <head>
     <title>Iframe messaging - cross-origin iframe request</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Iframe messaging - cross-origin iframe request</h1>
     <p>If you can read JSON encoded HTTP request headers of the iframe below,
        the messaging works as expected.</p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/document.py';
         var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
                   urlPath;
-        queryIframe(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, null, messaging_test);
+        return requestViaIframe(url)
+          .then(function(message) {
+            var pre = document.getElementById('received_message')
+            var headers = message.headers;
+            pre.innerHTML = "";
+            pre.innerHTML += url + ":\n\n";
+            pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+            assert_own_property(headers, "host")
+            assert_own_property(headers, "connection")
+          });
       }, "Iframe is responding with HTTP headers");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/subresource-test/image-decoding.html
+++ b/testing/web-platform/tests/referrer-policy/generic/subresource-test/image-decoding.html
@@ -2,37 +2,38 @@
 <!-- TODO(kristijanburnik): Remove subres. duplication. Reuse a template. -->
 <html>
   <head>
     <title>Image decoding - cross-origin image request</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Image decoding - cross-origin image request</h1>
     <p>If you can read JSON encoded HTTP headers of the image below,
        the decoding works as expected.</p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/image.py';
         var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
                   urlPath + "?cache_destroyer=" + (new Date()).getTime();
-        queryImage(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, "always", messaging_test);
+        return requestViaImageForReferrerPolicy(url, undefined, "always")
+          .then(function(message) {
+            var pre = document.getElementById('received_message')
+            var headers = message.headers;
+            pre.innerHTML = "";
+            pre.innerHTML += url + ":\n\n";
+            pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+            assert_own_property(headers, "host")
+            assert_own_property(headers, "connection")
+          });
       }, "Image is encoding headers as JSON.");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/subresource-test/link-navigate.html
+++ b/testing/web-platform/tests/referrer-policy/generic/subresource-test/link-navigate.html
@@ -2,37 +2,38 @@
 <!-- TODO(kristijanburnik): Remove subres. duplication. Reuse a template. -->
 <html>
   <head>
     <title>Link messaging - cross-origin Link navigation</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Link messaging - cross-origin Link navigation</h1>
     <p>If you can read JSON encoded HTTP request headers of the Link below,
        the messaging works as expected.</p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/document.py';
         var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
                   urlPath;
-        queryLink(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, null, messaging_test);
+        return requestViaAnchor(url)
+          .then(function(message) {
+              var pre = document.getElementById('received_message')
+              var headers = message.headers;
+              pre.innerHTML = "";
+              pre.innerHTML += url + ":\n\n";
+              pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+              assert_own_property(headers, "host")
+              assert_own_property(headers, "connection")
+            });
       }, "Link is responding with HTTP headers");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/subresource-test/script-messaging.html
+++ b/testing/web-platform/tests/referrer-policy/generic/subresource-test/script-messaging.html
@@ -2,37 +2,38 @@
 <!-- TODO(kristijanburnik): Remove subres. duplication. Reuse a template. -->
 <html>
   <head>
     <title>Script messaging - cross-origin Script request</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Script messaging - cross-origin Script request</h1>
     <p>If you can read JSON encoded HTTP request headers of the Script below,
        the messaging works as expected.</p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/script.py';
         var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
                   urlPath;
-        queryScript(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, null, messaging_test);
+        return requestViaScript(url)
+          .then(function(message) {
+              var pre = document.getElementById('received_message')
+              var headers = message.headers;
+              pre.innerHTML = "";
+              pre.innerHTML += url + ":\n\n";
+              pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+              assert_own_property(headers, "host")
+              assert_own_property(headers, "connection")
+            });
       }, "Script is responding with HTTP headers");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/subresource-test/worker-messaging.html
+++ b/testing/web-platform/tests/referrer-policy/generic/subresource-test/worker-messaging.html
@@ -2,37 +2,38 @@
 <!-- TODO(kristijanburnik): Remove subres. duplication. Reuse a template. -->
 <html>
   <head>
     <title>Worker messaging - same-origin Worker request</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Worker messaging - same-origin Worker request</h1>
     <p>If you can read JSON encoded HTTP request headers of the Worker below,
        the messaging works as expected.</p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/worker.py';
         var url = location.protocol + "//" + location.hostname + ":" +
                   location.port + urlPath;
-        queryWorker(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, null, messaging_test);
+        return requestViaDedicatedWorker(url, {})
+          .then(function(message) {
+              var pre = document.getElementById('received_message')
+              var headers = message.headers;
+              pre.innerHTML = "";
+              pre.innerHTML += url + ":\n\n";
+              pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+              assert_own_property(headers, "host")
+              assert_own_property(headers, "connection")
+            });
       }, "Worker is responding with HTTP headers");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/subresource-test/xhr-messaging.html
+++ b/testing/web-platform/tests/referrer-policy/generic/subresource-test/xhr-messaging.html
@@ -2,37 +2,38 @@
 <!-- TODO(kristijanburnik): Remove subres. duplication. Reuse a template. -->
 <html>
   <head>
     <title>XHR messaging - cross-origin XHR request</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>XHR messaging - cross-origin XHR request</h1>
     <p>If you can read JSON encoded HTTP request headers of the XHR below,
        the messaging works as expected.</p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(messaging_test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/xhr.py';
         var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
                   urlPath;
-        queryXhr(url, function(message) {
-          var pre = document.getElementById('received_message')
-          var headers = message.headers;
-          pre.innerHTML = "";
-          pre.innerHTML += url + ":\n\n";
-          pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
-          assert_own_property(headers, "host")
-          assert_own_property(headers, "connection")
-          messaging_test.done();
-        }, null, null, messaging_test);
+        return requestViaXhr(url)
+          .then(function(message) {
+              var pre = document.getElementById('received_message')
+              var headers = message.headers;
+              pre.innerHTML = "";
+              pre.innerHTML += url + ":\n\n";
+              pre.innerHTML += JSON.stringify(headers, null, 2) + "\n\n"
+              assert_own_property(headers, "host")
+              assert_own_property(headers, "connection")
+            });
       }, "XHR is responding with HTTP headers");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/referrer-policy/generic/unsupported-csp-referrer-directive.html
+++ b/testing/web-platform/tests/referrer-policy/generic/unsupported-csp-referrer-directive.html
@@ -2,30 +2,31 @@
 <html>
   <head>
     <title>Referrer Policy: CSP 'referrer' directive should not be supported</title>
     <meta http-equiv="Content-Security-Policy" content="referrer no-referrer">
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <!-- Common global functions for referrer-policy tests. -->
     <script src="/referrer-policy/generic/common.js"></script>
+    <script src="/referrer-policy/generic/referrer-policy-test-case.js"></script>
   </head>
   <body>
     <h1>Referrer Policy: CSP 'referrer' directive should not be supported</h1>
     <p>CSP used to have a 'referrer' directive to set a Referrer Policy. This directive has been removed and should not be supported.</p>
 
     <pre id="received_message">Running...</pre>
 
     <script>
-      async_test(function(test) {
+      promise_test(function() {
         var urlPath = '/referrer-policy/generic/subresource/image.py?cache_destroyer=' + (new Date()).getTime();
         var url = location.protocol + "//www1." + location.hostname + ":" + location.port +
                   urlPath;
-        queryImage(url, function(message) {
-          assert_equals(message.referrer, document.location.href);
-          test.done();
-        }, null, 'always', test);
+        return requestViaImageForReferrerPolicy(url, null, 'always')
+          .then(function(message) {
+            assert_equals(message.referrer, document.location.href);
+          });
       }, "Image has a referrer despite CSP 'referrer' directive");
     </script>
 
     <div id="log"></div>
   </body>
 </html>
--- a/testing/web-platform/tests/upgrade-insecure-requests/support/testharness-helper.sub.js
+++ b/testing/web-platform/tests/upgrade-insecure-requests/support/testharness-helper.sub.js
@@ -1,8 +1,14 @@
+// Used by /mixed-content/generic/common.js.
+function wrapResult(server_data) {
+  // Currently the returned value is not used in mixed-content tests.
+  return null;
+}
+
 const Host = {
   SAME_ORIGIN: "same-origin",
   CROSS_ORIGIN: "cross-origin",
 };
 
 const Protocol = {
   INSECURE: "insecure",
   SECURE: "secure",