Fix up wpt/preload

 - Some tests in wpt/preload use Resource Timing entries to make sure
   that no requests are made. We're changing that (Resource Timing
   entries should be created even when blocked by CSP - see
   https://github.com/whatwg/fetch/issues/1215). Stop using
   Resource Timing entries and check that with server side scripts.
 - http/tests/preload/preload-csp.html is covered by some WPTs. Let's
   remove it.

Change-Id: I3c2cdfa2459d212657be7569c5290c48b39d6f05
Bug: 1275564
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3708287
Reviewed-by: Yoav Weiss <yoavweiss@chromium.org>
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1019490}
diff --git a/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload-nonce.html b/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload-nonce.html
index 19e09472e..2a5bc1a 100644
--- a/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload-nonce.html
+++ b/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload-nonce.html
@@ -1,39 +1,50 @@
 <!DOCTYPE html>
 <script nonce="abc" src="/resources/testharness.js"></script>
 <script nonce="abc" src="/resources/testharnessreport.js"></script>
+<script nonce="abc" src="/common/utils.js"></script>
 <script nonce="abc" src="/preload/resources/preload_helper.js"></script>
 <body>
 <script nonce="abc">
 
-async_test(function(t) {
-    verifyPreloadAndRTSupport();
-    var link = document.createElement("link");
-    link.as = "script";
-    link.rel = "preload";
-    link.href = "resources/dummy.js?with-nonce";
-    link.nonce = "abc";
-    link.onload = link.onerror = t.step_func(function() {
-        t.step_timeout(function() {
-            verifyNumberOfResourceTimingEntries("resources/dummy.js?with-nonce", 1);
-            t.done();
-        }, 0);
-    });
-    document.body.appendChild(link);
+promise_test(async (t) => {
+  verifyPreloadAndRTSupport();
+  const id = token();
+  const link = document.createElement("link");
+  link.as = "script";
+  link.rel = "preload";
+  link.href = stashPutUrl(id);
+  link.nonce = "abc";
+
+  const load = new Promise((resolve) => {
+    link.onload = resolve;
+  });
+  link.onerror = t.unreached_func("link.onerror");
+
+  document.body.appendChild(link);
+  await load;
+
+  const arrived = await hasArrivedAtServer(id);
+  assert_true(arrived, "The preload should've arrived at the server.");
 }, "link preload with nonce attribute");
 
-async_test(function(t) {
-    verifyPreloadAndRTSupport();
-    var link = document.createElement("link");
-    link.as = "script";
-    link.rel = "preload";
-    link.href = "resources/dummy.js?without-nonce";
-    link.onload = link.onerror = t.step_func(function() {
-        t.step_timeout(function() {
-            verifyNumberOfResourceTimingEntries("resources/dummy.js?without-nonce", 0);
-            t.done();
-        }, 0);
-    });
-    document.body.appendChild(link);
+promise_test(async (t) => {
+  verifyPreloadAndRTSupport();
+  const id = token();
+  const link = document.createElement("link");
+  link.as = "script";
+  link.rel = "preload";
+  link.href = stashPutUrl(id);
+
+  const error = new Promise((resolve) => {
+    link.onerror = resolve;
+  });
+  link.onload = t.unreached_func("link.onload");
+
+  document.body.appendChild(link);
+  await error;
+
+  const arrived = await hasArrivedAtServer(id);
+  assert_false(arrived, "The preload should've arrived at the server.");
 }, "link preload without nonce attribute");
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html
index 74ea870..cd2d8fbb 100644
--- a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html
+++ b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html
@@ -1,33 +1,53 @@
 <!DOCTYPE html>
-<title>Makes sure that Link headers preload resources with CSP nonce</title>
-<script nonce="abc" src="/resources/testharness.js"></script>
-<script nonce="abc" src="/resources/testharnessreport.js"></script>
-<script nonce="abc" src="/preload/resources/preload_helper.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/preload/resources/preload_helper.js"></script>
 <body>
-<script nonce="abc">
-    setup({single_test: true});
+<script>
 
-    var iterations = 0;
+async_test(t => {
+  const id = token();
+  const pageUrl =
+    '/common/blank.html?pipe=' +
+    '|header(content-security-policy, script-src \'nonce-abc\')' +
+    `|header(link, <${encodedStashPutUrl(id)}>;rel=preload;as=script)`;
 
-    function check_finished() {
-        if (numberOfResourceTimingEntries("resources/dummy.js?from-header&without-nonce") == 0 &&
-            numberOfResourceTimingEntries("resources/dummy.js?from-header&with-nonce") == 1) {
-            done();
-        }
-        iterations++;
-        if (iterations == 10) {
-            // At least one is expected to fail, but this should give details to the exact failure(s).
-            verifyNumberOfResourceTimingEntries("resources/dummy.js?from-header&without-nonce", 0);
-            verifyNumberOfResourceTimingEntries("resources/dummy.js?from-header&with-nonce", 1);
-            done();
-        } else {
-            step_timeout(check_finished, 500);
-        }
+  const w = window.open(pageUrl);
+  t.add_cleanup(() => w.close());
+
+  step_timeout(async () => {
+    try {
+      const arrived = await hasArrivedAtServer(id);
+      assert_false(arrived, 'The preload should be blocked.');
+      t.done();
+    } catch (e) {
+      t.step(() => {throw e;});
     }
+  }, 3000);
+}, 'without nonce');
 
-    window.addEventListener("load", function() {
-        verifyPreloadAndRTSupport();
-        step_timeout(check_finished, 500);
-    });
+async_test(t => {
+  const id = token();
+  const pageUrl =
+    '/common/blank.html?pipe=' +
+    '|header(content-security-policy, script-src \'nonce-az\')' +
+    `|header(link, <${encodedStashPutUrl(id)}>;rel=preload;as=script;nonce=az)`;
+  const w = window.open(pageUrl);
+  t.add_cleanup(() => w.close());
+
+  // TODO: Use step_wait after
+  // https://github.com/web-platform-tests/wpt/pull/34289 is merged.
+  step_timeout(async () => {
+    try {
+      const arrived = await hasArrivedAtServer(id);
+      assert_true(arrived, 'The preload should have arrived at the server.');
+      t.done();
+    } catch (e) {
+      t.step(() => {throw e;});
+    }
+  }, 3000);
+}, 'with nonce');
+
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html.headers b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html.headers
deleted file mode 100644
index a54b6937..0000000
--- a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html.headers
+++ /dev/null
@@ -1,3 +0,0 @@
-Content-Security-Policy: script-src 'nonce-abc'
-Link: </preload/resources/dummy.js?from-header&without-nonce>;rel=preload;as=script
-Link: </preload/resources/dummy.js?from-header&with-nonce>;rel=preload;as=script;nonce=abc
diff --git a/third_party/blink/web_tests/external/wpt/preload/preload-csp.sub.html b/third_party/blink/web_tests/external/wpt/preload/preload-csp.sub.html
index a11214e9..7d367bf 100644
--- a/third_party/blink/web_tests/external/wpt/preload/preload-csp.sub.html
+++ b/third_party/blink/web_tests/external/wpt/preload/preload-csp.sub.html
@@ -1,55 +1,35 @@
 <!DOCTYPE html>
-<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; font-src 'none'; style-src 'none'; img-src 'none'; media-src 'none'; connect-src 'none'">
+<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; font-src 'none'; style-src 'none'; img-src 'none'; media-src 'none';">
 <title>Makes sure that preload requests respect CSP</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
 <script src="/preload/resources/preload_helper.js"></script>
-<link rel=preload href="{{host}}:{{ports[http][1]}}/preload/resources/dummy.js" as=style>
-<link rel=preload href="resources/dummy.css" as=style>
-<link rel=preload href="resources/square.png" as=image>
-<link rel=preload href="/fonts/CanvasTest.ttf" as=font crossorigin>
-<link rel=preload href="resources/white.mp4" as=video>
-<link rel=preload href="resources/sound_5.oga" as=audio>
-<link rel=preload href="resources/foo.vtt" as=track>
-<link rel=preload href="resources/dummy.xml?foo=bar" as=foobarxmlthing>
-<link rel=preload href="resources/dummy.xml">
+<link rel=preload href="http://{{host}}:{{ports[http][1]}}/preload/resources/stash-put.py?key={{uuid()}}" as=style>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=style>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=image>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=font crossorigin>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=video>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=audio>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=track>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=foobarxmlthing>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}">
 <body>
 <script>
-    setup({single_test: true});
-
-    var iterations = 0;
-
-    function check_finished() {
-        if (numberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js") == 0 &&
-            numberOfResourceTimingEntries("resources/dummy.css") == 0 &&
-            numberOfResourceTimingEntries("resources/square.png") == 0 &&
-            numberOfResourceTimingEntries("/fonts/CanvasTest.ttf") == 0 &&
-            numberOfResourceTimingEntries("resources/white.mp4") == 0 &&
-            numberOfResourceTimingEntries("resources/sound_5.oga") == 0 &&
-            numberOfResourceTimingEntries("resources/foo.vtt") == 0 &&
-            numberOfResourceTimingEntries("resources/dummy.xml") == 0) {
-            done();
-        }
-        iterations++;
-        if (iterations == 10) {
-            // At least one is expected to fail, but this should give details to the exact failure(s).
-            verifyNumberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js", 0);
-            verifyNumberOfResourceTimingEntries("resources/dummy.css", 0);
-            verifyNumberOfResourceTimingEntries("resources/square.png", 0);
-            verifyNumberOfResourceTimingEntries("/fonts/CanvasTest.ttf", 0);
-            verifyNumberOfResourceTimingEntries("resources/white.mp4", 0);
-            verifyNumberOfResourceTimingEntries("resources/sound_5.oga", 0);
-            verifyNumberOfResourceTimingEntries("resources/foo.vtt", 0);
-            verifyNumberOfResourceTimingEntries("resources/dummy.xml", 0);
-            done();
-        } else {
-            step_timeout(check_finished, 500);
-        }
+promise_test(async (t) => {
+  verifyPreloadAndRTSupport();
+  const keys = [];
+  const links = document.querySelectorAll('link');
+  for (const link of links) {
+    if (link.rel === 'preload') {
+      const r = /\?key=([a-zA-Z0-9\-]+)$/;
+      keys.push(link.href.match(r)[1]);
     }
+  }
+  await new Promise((resolve) => step_timeout(resolve, 3000));
 
-    window.addEventListener("load", function() {
-        verifyPreloadAndRTSupport();
-        step_timeout(check_finished, 500);
-    });
+  for (const key of keys) {
+    assert_false(await hasArrivedAtServer(key));
+  }
+}, 'Preload requests are blocked by CSP.');
 </script>
-
diff --git a/third_party/blink/web_tests/external/wpt/preload/preload-default-csp.sub.html b/third_party/blink/web_tests/external/wpt/preload/preload-default-csp.sub.html
index c649a53f..8d280c4 100644
--- a/third_party/blink/web_tests/external/wpt/preload/preload-default-csp.sub.html
+++ b/third_party/blink/web_tests/external/wpt/preload/preload-default-csp.sub.html
@@ -1,55 +1,35 @@
 <!DOCTYPE html>
-<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; default-src 'none'">
+<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'; default-src 'none'; connect-src 'self';">
 <title>Makes sure that preload requests respect CSP</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/preload/resources/preload_helper.js"></script>
-<link rel=preload href="{{host}}:{{ports[http][1]}}/preload/resources/dummy.js" as=style>
-<link rel=preload href="resources/dummy.css" as=style>
-<link rel=preload href="resources/square.png" as=image>
-<link rel=preload href="/fonts/CanvasTest.ttf" as=font crossorigin>
-<link rel=preload href="resources/white.mp4" as=video>
-<link rel=preload href="resources/sound_5.oga" as=audio>
-<link rel=preload href="resources/foo.vtt" as=track>
-<link rel=preload href="resources/dummy.xml?foo=bar" as=foobarxmlthing>
-<link rel=preload href="resources/dummy.xml">
+<link rel=preload href="http://{{host}}:{{ports[http][1]}}/preload/resources/stash-put.py?key={{uuid()}}" as=style>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=style>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=image>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=font crossorigin>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=video>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=audio>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=track>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}" as=foobarxmlthing>
+<link rel=preload href="/preload/resources/stash-put.py?key={{uuid()}}">
 <body>
 <script>
-    setup({single_test: true});
-
-    var iterations = 0;
-
-    function check_finished() {
-        if (numberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js") == 0 &&
-            numberOfResourceTimingEntries("resources/dummy.css") == 0 &&
-            numberOfResourceTimingEntries("resources/square.png") == 0 &&
-            numberOfResourceTimingEntries("/fonts/CanvasTest.ttf") == 0 &&
-            numberOfResourceTimingEntries("resources/white.mp4") == 0 &&
-            numberOfResourceTimingEntries("resources/sound_5.oga") == 0 &&
-            numberOfResourceTimingEntries("resources/foo.vtt") == 0 &&
-            numberOfResourceTimingEntries("resources/dummy.xml") == 0) {
-            done();
-        }
-        iterations++;
-        if (iterations == 10) {
-            // At least one is expected to fail, but this should give details to the exact failure(s).
-            verifyNumberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js", 0);
-            verifyNumberOfResourceTimingEntries("resources/dummy.css", 0);
-            verifyNumberOfResourceTimingEntries("resources/square.png", 0);
-            verifyNumberOfResourceTimingEntries("/fonts/CanvasTest.ttf", 0);
-            verifyNumberOfResourceTimingEntries("resources/white.mp4", 0);
-            verifyNumberOfResourceTimingEntries("resources/sound_5.oga", 0);
-            verifyNumberOfResourceTimingEntries("resources/foo.vtt", 0);
-            verifyNumberOfResourceTimingEntries("resources/dummy.xml", 0);
-            done();
-        } else {
-            step_timeout(check_finished, 500);
-        }
+promise_test(async (t) => {
+  verifyPreloadAndRTSupport();
+  const keys = [];
+  const links = document.querySelectorAll('link');
+  for (const link of links) {
+    if (link.rel === 'preload') {
+      const r = /\?key=([a-zA-Z0-9\-]+)$/;
+      keys.push(link.href.match(r)[1]);
     }
+  }
+  await new Promise((resolve) => step_timeout(resolve, 3000));
 
-    window.addEventListener("load", function() {
-        verifyPreloadAndRTSupport();
-        step_timeout(check_finished, 500);
-    });
+  for (const key of keys) {
+    assert_false(await hasArrivedAtServer(key));
+  }
+}, 'Preload requests are blocked by CSP ("default-src \'none\').');
 </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/preload/preload-strict-dynamic.html b/third_party/blink/web_tests/external/wpt/preload/preload-strict-dynamic.html
deleted file mode 100644
index 7639565..0000000
--- a/third_party/blink/web_tests/external/wpt/preload/preload-strict-dynamic.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!DOCTYPE html>
-<head>
-<script src="/resources/testharness.js" nonce="123"></script>
-<script src="/resources/testharnessreport.js" nonce="123"></script>
-<title>CSP strict-dynamic + preload</title>
-<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-123' 'strict-dynamic'" />
-</head>
-<body>
-<link id="static-no-nonce" href="resources/dummy.js?static-no-nonce" rel=preload as=script>
-<link id="static-nonce" href="resources/dummy.js?static-nonce" rel=preload as=script nonce="123">
-<script nonce="123">
-  let counter = 0;
-  let cspViolation = false;
-  let isLoaded = (url) => {
-    let entries = performance.getEntriesByType("resource");
-    for (let entry of entries) {
-      if (entry.name.indexOf(url) != -1 ) {
-        return true;
-      }
-    }
-    return false;
-  }
-  window.addEventListener("securitypolicyviolation", (e) => {
-    counter++;
-    if (e.violatedDirective == "script-src-elem" && e.blockedURI.includes("static-no-nonce")) {
-      cspViolation = true;
-    }
-  });
-  let link = document.createElement("link");
-  link.rel = "preload";
-  link.href = "resources/dummy.js?dynamic-nonce";
-  link.as = "script";
-  link.onload = () => { ++counter; };
-  document.head.appendChild(link);
-  link = document.getElementById("static-no-nonce");
-  link.addEventListener("error", () => { ++counter; });
-  link = document.getElementById("static-nonce");
-  link.addEventListener("load", () => { ++counter; });
-  let t = async_test('preload from nonced script should work with strict-dynamic. preloaded script from markup should not.');
-  let timerCounter = 0;
-  setInterval(t.step_func(() => {
-    if (counter >= 4 || timerCounter > 5) {
-      assert_true(isLoaded("dynamic-nonce"), "dynamic inserted preload script should have been loaded");
-      assert_true(isLoaded("static-nonce"), "preload tag with a nonce should have been loaded");
-      assert_false(isLoaded("static-no-nonce"), "preload tag without a nonce should not have been loaded");
-      assert_true(cspViolation, "CSP violation should have fired");
-      t.done();
-    }
-    ++timerCounter;
-  }), 100);
-
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/preload/preload-strict-dynamic.sub.html b/third_party/blink/web_tests/external/wpt/preload/preload-strict-dynamic.sub.html
new file mode 100644
index 0000000..bdd7a17
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/preload-strict-dynamic.sub.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js" nonce="123"></script>
+<script src="/resources/testharnessreport.js" nonce="123"></script>
+<script src="/common/utils.js" nonce="123"></script>
+<script src="/preload/resources/preload_helper.js" nonce="123"></script>
+<title>CSP strict-dynamic + preload</title>
+<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-123' 'strict-dynamic'" />
+</head>
+<body>
+<script nonce="123">
+const PATTERN = /\?key=([a-zA-Z0-9\-]+)$/;
+
+// We use async_test instead of promise_test in this file because these
+// tests take long time to run and we want to run them in parallel.
+async_test((t) => {
+  Promise.resolve().then(async () => {
+    let sawViolation = false;
+    self.addEventListener('securitypolicyviolation', (e) => {
+      const link = document.querySelector('#static-no-nonce');
+      if (e.violatedDirective == 'script-src-elem' && e.blockedURI === link.href) {
+        sawViolation = true;
+      }
+    });
+
+    await new Promise((resolve) => step_timeout(resolve, 3000));
+
+    const link = document.querySelector('#static-no-nonce');
+    const key = link.href.match(PATTERN)[1]
+
+    assert_true(sawViolation, 'sawViolation');
+    assert_false(await hasArrivedAtServer(key), 'hasArrivedAtServer');
+    t.done();
+  }).catch(t.step_func((e) => {
+    throw e;
+  }));
+}, 'static-no-nonce');
+
+async_test((t) => {
+  Promise.resolve().then(async () => {
+    let sawViolation = false;
+    self.addEventListener('securitypolicyviolation', (e) => {
+      const link = document.querySelector('#static-nonce');
+      if (e.violatedDirective == 'script-src-elem' && e.blockedURI === link.href) {
+        sawViolation = true;
+      }
+    });
+
+    // TODO: Use step_wait after
+    // https://github.com/web-platform-tests/wpt/pull/34289 is merged.
+    await new Promise((resolve) => step_timeout(resolve, 3000));
+
+    const link = document.querySelector('#static-nonce');
+    const key = link.href.match(PATTERN)[1]
+
+    assert_false(sawViolation, 'sawViolation');
+    assert_true(await hasArrivedAtServer(key), 'hasArrivedAtServer');
+    t.done();
+  }).catch(t.step_func((e) => {
+    throw e;
+  }));
+}, 'static-nonce');
+
+async_test((t) => {
+  Promise.resolve().then(async () => {
+    const link = document.createElement('link');
+    link.rel = 'preload';
+    const id = token();
+    link.href = `/preload/resources/stash-put.py?key=${id}`;
+    link.as = 'script';
+
+    document.head.appendChild(link);
+    await new Promise((resolve, reject) => {
+      link.addEventListener('load', resolve, {once: true});
+      link.addEventListener('error', resolve, {once: true});
+    });
+    assert_true(await hasArrivedAtServer(id), 'hasArrivedAtServer');
+    t.done();
+  }).catch(t.step_func((e) => {
+    throw e;
+  }));
+}, 'dynamic');
+</script>
+
+<link id="static-no-nonce" href="/preload/resources/stash-put.py?key={{uuid()}}" rel=preload as=script>
+<link id="static-nonce" href="/preload/resources/stash-put.py?key={{uuid()}}" rel=preload as=script nonce="123">
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/preload/resources/preload_helper.js b/third_party/blink/web_tests/external/wpt/preload/resources/preload_helper.js
index 1c7c1a27..5b7a6eb 100644
--- a/third_party/blink/web_tests/external/wpt/preload/resources/preload_helper.js
+++ b/third_party/blink/web_tests/external/wpt/preload/resources/preload_helper.js
@@ -1,3 +1,18 @@
+function stashPutUrl(token) {
+  return `/preload/resources/stash-put.py?key=${token}`;
+}
+
+function encodedStashPutUrl(token) {
+  return encodeURIComponent(stashPutUrl(token));
+}
+
+async function hasArrivedAtServer(token) {
+  const res = await fetch(`/preload/resources/stash-take.py?key=${token}`);
+  assert_true(res.status === 200 || res.status === 404,
+              'status must be either 200 or 404');
+  return res.status === 200;
+}
+
 function verifyPreloadAndRTSupport()
 {
     var link = window.document.createElement("link");
diff --git a/third_party/blink/web_tests/external/wpt/preload/resources/stash-put.py b/third_party/blink/web_tests/external/wpt/preload/resources/stash-put.py
new file mode 100644
index 0000000..f4bc8794
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/resources/stash-put.py
@@ -0,0 +1,20 @@
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+    if request.method == u'OPTIONS':
+        # CORS preflight
+        response.headers.set(b'Access-Control-Allow-Origin', b'*')
+        response.headers.set(b'Access-Control-Allow-Methods', b'*')
+        response.headers.set(b'Access-Control-Allow-Headers', b'*')
+        return 'done'
+
+    url_dir = u'/'.join(request.url_parts.path.split(u'/')[:-1]) + u'/'
+    key = request.GET.first(b"key")
+    if b"value" in request.GET:
+        value = request.GET.first(b"value")
+    else:
+        value = b"value"
+    # value here must be a text string. It will be json.dump()'ed in stash-take.py.
+    request.server.stash.put(key, isomorphic_decode(value), url_dir)
+    response.headers.set(b'Access-Control-Allow-Origin', b'*')
+    return "done"
diff --git a/third_party/blink/web_tests/external/wpt/preload/resources/stash-take.py b/third_party/blink/web_tests/external/wpt/preload/resources/stash-take.py
new file mode 100644
index 0000000..9977197
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/resources/stash-take.py
@@ -0,0 +1,14 @@
+from wptserve.handlers import json_handler
+
+
+@json_handler
+def main(request, response):
+    dir = u'/'.join(request.url_parts.path.split(u'/')[:-1]) + u'/'
+    key = request.GET.first(b"key")
+    response.headers.set(b'Access-Control-Allow-Origin', b'*')
+    value = request.server.stash.take(key, dir)
+    if value is None:
+      response.status = 404
+      return 'No entry is found'
+    response.status = 200
+    return value
diff --git a/third_party/blink/web_tests/http/tests/preload/preload-csp.html b/third_party/blink/web_tests/http/tests/preload/preload-csp.html
deleted file mode 100644
index 7a06d81f..0000000
--- a/third_party/blink/web_tests/http/tests/preload/preload-csp.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE html>
-<meta http-equiv="Content-Security-Policy" content="font-src 'none'">
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script>
-    var t = async_test('Makes sure that preload requests respect CSP');
-</script>
-<link rel=preload href="../resources/Ahem.ttf" as=font crossorigin>
-<script src="../resources/slow-script.pl?delay=200"></script>
-<script>
-    window.onload = t.step(function(){
-        var entries = performance.getEntriesByName(new URL("../resources/Ahem.ttf", location.href).href);
-        assert_equals(entries.length, 0);
-        t.done();
-    });
-</script>
-