Origin Policy: Web Platform Tests for Origin Policy w/ CSP.

These tests follow the format currently under discussion here:
https://github.com/WICG/origin-policy/pull/39

Bug: 751996
Change-Id: Ief33c794498cb3ed84dac670ecff4ddc366b9592
Reviewed-on: https://chromium-review.googlesource.com/1130531
Reviewed-by: Mike West <[email protected]>
Commit-Queue: Daniel Vogelheim <[email protected]>
Cr-Commit-Position: refs/heads/master@{#580876}
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index a8dd25f8..2edadf7 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -4623,6 +4623,10 @@
 crbug.com/853360 [ Mac ] fast/forms/calendar-picker/month-picker-appearance.html [ Failure ]
 crbug.com/853360 [ Mac ] fast/forms/select/menulist-appearance-rtl.html [ Failure ]
 
+# Origin Policy: Skip tests that rely on --feature-enabled=OriginPolicy, so
+#   they can be run via virtual/origin-policy instead.
+crbug.com/751996 external/wpt/origin-policy [ Skip ]
+
 # Sheriff 2018-06-18
 crbug.com/853852 [ Win7 Linux ] virtual/threaded/external/wpt/feature-policy/experimental-features/vertical-scroll-wheel-block-manual.tentative.html [ Pass Timeout ]
 crbug.com/853852 [ Linux ] virtual/video-surface-layer/external/wpt/feature-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index 4bbc2c7..bf3ea3a1 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -712,5 +712,10 @@
     "prefix": "mojo-blob-urls",
     "base": "external/wpt/FileAPI/url",
     "args": ["--enable-features=MojoBlobURLs"]
+  },
+  {
+    "prefix": "origin-policy",
+    "base": "external/wpt/origin-policy",
+    "args": ["--enable-features=OriginPolicy"]
   }
 ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/.well-known/origin-policy/policy-csp-1 b/third_party/WebKit/LayoutTests/external/wpt/.well-known/origin-policy/policy-csp-1
new file mode 100644
index 0000000..adbfc362
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/.well-known/origin-policy/policy-csp-1
@@ -0,0 +1,3 @@
+{
+  "content-security-policy": [{ "policy": "script-src 'self' 'unsafe-inline'" }]
+}
diff --git a/third_party/WebKit/LayoutTests/external/wpt/.well-known/origin-policy/policy-csp-2 b/third_party/WebKit/LayoutTests/external/wpt/.well-known/origin-policy/policy-csp-2
new file mode 100644
index 0000000..e896da3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/.well-known/origin-policy/policy-csp-2
@@ -0,0 +1,5 @@
+{
+  "content-security-policy": [{
+    "policy": "script-src 'self' 'nonce-test'"
+  }]
+}
diff --git a/third_party/WebKit/LayoutTests/external/wpt/.well-known/origin-policy/policy-noimg b/third_party/WebKit/LayoutTests/external/wpt/.well-known/origin-policy/policy-noimg
new file mode 100644
index 0000000..fce2d40
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/.well-known/origin-policy/policy-noimg
@@ -0,0 +1,3 @@
+{
+  "content-security-policy": [{ "policy": "img-src 'none'" }]
+}
diff --git a/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy-single-report.https.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy-single-report.https.tentative.html
new file mode 100644
index 0000000..24ee8999
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy-single-report.https.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script src='/https/chromium.googlesource.com/resources/testharness.js'></script>
+  <script src='/https/chromium.googlesource.com/resources/testharnessreport.js'></script>
+</head>
+<body>
+  <iframe id=frame></iframe>
+  <script>
+    async_test(t => {
+      let violations = [];
+      window.addEventListener("message", (e) => {
+        violations.push(e);
+        t.step_timeout(() => {
+          assert_equals(violations.length, 1);
+          t.done();
+        });
+      });
+
+      let forbidden_image = "<img src=https://127.0.0.1:1234/bla.jpg>";
+      let event_bouncer = "<script>document.addEventListener(" +
+          "'securitypolicyviolation'," +
+          "(e) => window.parent.postMessage(e.blockedURI, '*'));</sc" +
+          "ript>";
+      document.getElementById("frame").src =
+        "data:text/html;charset=utf-8," + event_bouncer + forbidden_image;
+    }, "Origin-Policy-based CSP violation should trigger 1 violation event");
+  </script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy-single-report.https.tentative.html.headers b/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy-single-report.https.tentative.html.headers
new file mode 100644
index 0000000..cb596cf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy-single-report.https.tentative.html.headers
@@ -0,0 +1 @@
+Sec-Origin-Policy: policy-noimg
diff --git a/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy.https.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy.https.tentative.html
new file mode 100644
index 0000000..34a71fe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/origin-policy/origin-policy.https.tentative.html
@@ -0,0 +1,143 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script src='/https/chromium.googlesource.com/resources/testharness.js'></script>
+  <script src='/https/chromium.googlesource.com/resources/testharnessreport.js'></script>
+</head>
+<body>
+  <div id=log></div>
+  <iframe id=frame></iframe>
+  <script>
+    // Navigate the frame to a test page with the given policy and wait for
+    // postMessage to arrive. Resolve the result promise with the message.
+    function navigate(policy) {
+      return new Promise(resolve => {
+        window.addEventListener("message", event => { resolve(event.data); },
+                                { once: true });
+        document.getElementById("frame").src =
+            "/origin-policy/sec-origin-policy-header.html.py?policy=" + policy;
+      });
+    }
+
+    // Check whether the message returned from the frame meets our expectations.
+    function expect(expect_script, expect_eval, message) {
+      assert_own_property(message, "inline_allowed");
+      assert_own_property(message, "eval_allowed");
+      assert_equals(message.inline_allowed, expect_script);
+      assert_equals(message.eval_allowed, expect_eval);
+    }
+
+    // Generate a more descriptive error message. Re-throw the error.
+    function descriptive_message(policy, expect_inline, expect_eval,
+                                 index, error) {
+      error.message = `Error occured on entry #${index + 1} ["${policy
+          }", ${expect_inline}, ${expect_eval}]: "${error}".`;
+      throw(error);
+    }
+
+    // Run the navigation + expectation checking for one test case line.
+    function test_case_entry([policy, expect_inline, expect_eval], index) {
+      return navigate(policy)
+          .then(message => expect(expect_inline, expect_eval, message))
+
+          // This catch handler merely logs a more friendly message,
+          // pointing you to the exact line of the failing test.
+          .catch(error => descriptive_message(policy, expect_inline,
+                                              expect_eval, index, error));
+    }
+
+    function origin_policy_csp_test_case(test_case_list) {
+      return t => {
+        // Setup the promise chain for the test.
+        let chain = Promise.resolve();
+        for ([index, val] of test_case_list.entries())
+          chain = chain.then(test_case_entry.bind(this, val, index));
+
+        // Delete the policy as the last element of the chain, on both
+        // resolve + reject paths, so that a left-over policy won't break
+        // subsequent tests.
+        return chain.then(() => navigate("0"),
+                          (error) => { navigate("0"); throw error; });
+      }
+    }
+
+
+    // Sanity check: A request with no policy.
+    promise_test(origin_policy_csp_test_case([
+        ["", true, true],  // No policy.
+    ]), "sanity check");
+
+    // Basic functionality. A policy should have an effect.
+    promise_test(origin_policy_csp_test_case([
+        ["",             true, true],  // No policy.
+        ["policy-csp-1", true, false], // policy-csp-1, which forbids eval.
+        ["0",            true, true],  // Delete the policy again.
+      ]), "The basics: A policy should have an effect..");
+
+    // Basic functionality. Set a policy. Make sure it "sticks".
+    promise_test(origin_policy_csp_test_case([
+        ["",             true, true],  // No policy.
+        ["policy-csp-1", true, false], // policy-csp-1, which forbids eval.
+        ["",             true, false], // No policy. Should remember p...-csp-1.
+        ["0",            true, true],  // Delete the policy again.
+      ]), "The basics: A policy should stick.");
+
+    // Set, update, and delete a policy.
+    promise_test(origin_policy_csp_test_case([
+        ["",             true,  true],
+        ["policy-csp-1", true,  false], // policy-csp-1, which forbids eval.
+        ["policy-csp-2", false, false], // policy-csp-2, forbids script + eval.
+        ["0",            true,  true],  // Delete the policy.
+      ]), "Policy set, update, and delete.");
+
+    // Set, update, and delete a policy. Check on each step whether it 'sticks'.
+    promise_test(origin_policy_csp_test_case([
+        ["",             true,  true],
+        ["policy-csp-1", true,  false], // policy-csp-1, which forbids eval.
+        ["",             true,  false],
+        ["policy-csp-2", false, false], // Forbid script + eval.
+        ["",             false, false],
+        ["0",            true,  true],  // Delete the policy.
+        ["",             true,  true],
+      ]), "Policy set-update-delete cycle with checks.");
+
+    // Set a policy, update, then revert to the old one.
+    promise_test(origin_policy_csp_test_case([
+        ["",             true,  true],
+        ["policy-csp-1", true,  false], // policy-csp-1, which forbids eval.
+        ["policy-csp-2", false, false], // Forbid script + eval.
+        ["policy-csp-1", true,  false], // policy-csp-1 again.
+        ["0",            true,  true],
+      ]), "Policy set-update-delete cycle.");
+
+    // Set, delete, re-set, and re-delete a policy.
+    promise_test(origin_policy_csp_test_case([
+        ["",             true, true],  // No policy.
+        ["policy-csp-1", true, false], // policy-csp-1, which forbids eval.
+        ["",             true, false],
+        ["0",            true, true],  // Delete the policy.
+        ["",             true, true],
+        ["policy-csp-1", true, false], // Set policy after policy was deleted.
+        ["",             true, false],
+        ["0",            true, true],  // Delete the policy again.
+        ["",             true, true],
+      ]), "Policy re-set and re-delete.");
+
+    // We've had some bugs with repeated policies being set, so lets just
+    // run through a set-update-delete cycle but with every request being
+    // made twice.
+    promise_test(origin_policy_csp_test_case([
+        ["",             true,  true],
+        ["",             true,  true],
+        ["policy-csp-1", true,  false],
+        ["policy-csp-1", true,  false],
+        ["policy-csp-2", false, false],
+        ["policy-csp-2", false, false],
+        ["0",            true,  true],
+        ["0",            true,  true],
+        ["",             true,  true],
+        ["",             true,  true],
+      ]), "Double Trouble");
+  </script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/origin-policy/sec-origin-policy-header.html.py b/third_party/WebKit/LayoutTests/external/wpt/origin-policy/sec-origin-policy-header.html.py
new file mode 100644
index 0000000..8e62b6c2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/origin-policy/sec-origin-policy-header.html.py
@@ -0,0 +1,64 @@
+def main(request, response):
+  """Send a response with the origin policy indicated by the ?policy= argument.
+
+     Won't send a policy when the browser doesn't indicate support.
+     The response tests whether inline script and eval are allowed, and will
+     send a corresponding message to the parent frame.
+     For easier debugging, we'll also show the results in-page.
+  """
+  origin_policy_header = "Sec-Origin-Policy"
+  request_policy = request.headers.get(origin_policy_header)
+  response_policy = request.GET.first("policy", default="")
+
+  if request_policy and response_policy:
+    response.headers.set(origin_policy_header, response_policy)
+    response.headers.set("Vary", "sec-origin-policy")
+
+  response.headers.set("Content-Type", "text/html");
+  return """
+    <html>
+    <head>
+     <title>Page with an Origin Policy</title>
+    </head>
+    <body>
+    <script nonce=test>
+      let inlineAllowed = false;
+      let evalAllowed = false;
+      try { eval('evalAllowed = true;'); } catch (e) {};
+    </script>
+    <script>
+      inlineAllowed = true;
+    </script>
+
+    <p>Reveal whether CSP with "unsafe-inline" or "unsafe-eval" is present:</p>
+    <ul>
+      <li>inline script allowed: <span id=inline_allowed></span></li>
+      <li>eval allowed: <span id=eval_allowed></span></li>
+    </ul>
+
+    <script nonce=test>
+      const result = {
+        "inline_allowed": inlineAllowed,
+        "eval_allowed": evalAllowed,
+      };
+
+      // Mirror content into the page for easy debugging:
+      const styles = {
+        true: "font-weight: bold; color: green;",
+        false: "font-weight: bold; color: red",
+      }
+      for (const [key, value] of Object.entries(result)) {
+        let element = document.getElementById(key);
+        element.textContent = value.toString();
+        element.style = styles[value];
+      }
+
+      // Send result to parent frame for evaluation.
+      if (window.parent) {
+        window.parent.postMessage(result, "*");
+      }
+    </script>
+    </body>
+    </html>
+  """
+
diff --git a/third_party/WebKit/LayoutTests/virtual/origin-policy/external/wpt/origin-policy/README.txt b/third_party/WebKit/LayoutTests/virtual/origin-policy/external/wpt/origin-policy/README.txt
new file mode 100644
index 0000000..309d6722
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/origin-policy/external/wpt/origin-policy/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in external/wpt/origin-policy with
+# --enable-blink-features=OriginPolicy.
+# Origin Policy is being standardized here: https://github.com/WICG/origin-policy