Bug 1162772, part 6 - Add Web Platform Tests for Secure Contexts. r=bz
☠☠ backed out by fda8ec39ce04 ☠ ☠
authorJonathan Watt <jwatt@jwatt.org>
Fri, 29 Apr 2016 23:02:12 +0100
changeset 295598 5e1a999558939aa410044b86a4437d1485c0112b
parent 295597 a921444a4b344117f1ab87392f47ef617e23c351
child 295599 f3a5c8b5e17073a1e68f079da93f8dbe10e454a9
push id19015
push usercbook@mozilla.com
push dateMon, 02 May 2016 09:39:23 +0000
treeherderfx-team@2080375bc69d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1162772
milestone49.0a1
Bug 1162772, part 6 - Add Web Platform Tests for Secure Contexts. r=bz MozReview-Commit-ID: JU2bzytnkkI
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/secure-contexts/basic-popup-and-iframe-tests.html
testing/web-platform/tests/secure-contexts/basic-popup-and-iframe-tests.https.html
testing/web-platform/tests/secure-contexts/basic-popup-and-iframe-tests.https.js
testing/web-platform/tests/secure-contexts/postMessage-helper.html
testing/web-platform/tests/secure-contexts/postMessage-helper.https.html
testing/web-platform/tests/secure-contexts/server-locations.sub.js
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -35371,16 +35371,28 @@
           }
         ],
         "html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-4.html": [
           {
             "path": "html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-4.html",
             "url": "/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-4.html"
           }
         ],
+        "secure-contexts/basic-popup-and-iframe-tests.html": [
+          {
+            "path": "secure-contexts/basic-popup-and-iframe-tests.html",
+            "url": "/secure-contexts/basic-popup-and-iframe-tests.html"
+          }
+        ],
+        "secure-contexts/basic-popup-and-iframe-tests.https.html": [
+          {
+            "path": "secure-contexts/basic-popup-and-iframe-tests.https.html",
+            "url": "/secure-contexts/basic-popup-and-iframe-tests.https.html"
+          }
+        ],
         "service-workers/service-worker/navigate-window.https.html": [
           {
             "path": "service-workers/service-worker/navigate-window.https.html",
             "url": "/service-workers/service-worker/navigate-window.https.html"
           }
         ],
         "service-workers/service-worker/update-recovery.https.html": [
           {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/secure-contexts/basic-popup-and-iframe-tests.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Test Window.isSecureContext for HTTP creator</title>
+    <meta name="help" href="https://w3c.github.io/webappsec-secure-contexts/#monkey-patching-global-object">
+    <meta name="author" title="Jonathan Watt" href="https://jwatt.org/">
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+    <script src="server-locations.sub.js"></script>
+    <script>
+
+// This file is the equivasent of the https version, but rather than
+// having a copy of its script file we figure out the URI of the script on the
+// https server and load it here.
+let script = document.createElement("script");
+script.setAttribute("src", https_dir + "basic-popup-and-iframe-tests.https.js");
+document.head.appendChild(script);
+
+    </script>
+  </head>
+  <body onload="begin();">
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/secure-contexts/basic-popup-and-iframe-tests.https.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset=utf-8>
+    <title>Test Window.isSecureContext for HTTPS creator</title>
+    <meta name="help" href="https://w3c.github.io/webappsec-secure-contexts/#monkey-patching-global-object">
+    <meta name="author" title="Jonathan Watt" href="https://jwatt.org/">
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+    <script src="server-locations.sub.js"></script>
+    <script src="basic-popup-and-iframe-tests.https.js"></script>
+  </head>
+  <body onload="begin();">
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/secure-contexts/basic-popup-and-iframe-tests.https.js
@@ -0,0 +1,279 @@
+
+/**
+ * This test checks the Secure Context state of documents for various
+ * permutations of document URI types and loading methods.
+ *
+ * The hierarchy that is tested is:
+ *
+ *   creator-doc > createe-doc
+ *
+ * The creator-doc is one of:
+ *
+ *   http:
+ *   https:
+ *
+ * The createe-doc is loaded as either a:
+ *
+ *   popup
+ *   iframe
+ *   sandboxed-iframe
+ *
+ * into which we load and test:
+ *
+ *   http:
+ *   https:
+ *   blob:
+ *   javascript:
+ *   about:blank
+ *   initial about:blank
+ *   srcdoc
+ *
+ * TODO once web-platform-tests supports it:
+ *   - test http://localhost
+ *   - test file:
+ *
+ * TODO once https://github.com/w3c/webappsec-secure-contexts/issues/26 is resolved
+ *   - test data:
+ */
+
+
+setup({explicit_done:true});
+
+
+const host_and_dirname = location.host +
+                         location.pathname.substr(0, location.pathname.lastIndexOf("/") + 1);
+
+
+// Flags to indicate where document types should be loaded for testing:
+const eLoadInPopup             = (1 << 0);
+const eLoadInUnsandboxedIframe = (1 << 1);
+const eLoadInSandboxedIframe   = (1 << 2);
+const eLoadInEverything        = eLoadInPopup | eLoadInUnsandboxedIframe | eLoadInSandboxedIframe;
+
+// Flags indicating if a document type is expected to be a Secure Context:
+const eSecureNo              = 1;
+const eSecureIfCreatorSecure = 2;
+
+// Flags indicating how the result of a test is obtained:
+const eResultFromPostMessage       = 1;
+const eResultFromExaminationOnLoad = 2;
+const eResultFromExaminationSync   = 3;
+
+
+const loadTypes = [
+  new LoadType("an http: URI",
+               eLoadInEverything,
+               http_dir + "postMessage-helper.html",
+               eSecureNo,
+               eResultFromPostMessage),
+  new LoadType("an https: URI",
+               eLoadInEverything,
+               https_dir + "postMessage-helper.https.html",
+               eSecureIfCreatorSecure,
+               eResultFromPostMessage),
+  new LoadType("a blob: URI",
+               eLoadInEverything,
+               URL.createObjectURL(new Blob(["<script>(opener||parent).postMessage(isSecureContext, '*')</script>"])),
+               eSecureIfCreatorSecure,
+               eResultFromPostMessage),
+  new LoadType("a srcdoc",
+               // popup not relevant:
+               eLoadInUnsandboxedIframe | eLoadInSandboxedIframe,
+               "<script>(opener||parent).postMessage(isSecureContext, '*')</script>",
+               eSecureIfCreatorSecure,
+               eResultFromPostMessage),
+  new LoadType("a javascript: URI",
+               // can't load in sandbox:
+               eLoadInUnsandboxedIframe | eLoadInPopup,
+               "javascript:(opener||parent).postMessage(isSecureContext, '*')",
+               eSecureIfCreatorSecure,
+               eResultFromPostMessage),
+  new LoadType("about:blank",
+               // can't obtain state if sandboxed:
+               eLoadInUnsandboxedIframe | eLoadInPopup,
+               "about:blank",
+               eSecureIfCreatorSecure,
+               eResultFromExaminationOnLoad),
+  new LoadType("initial about:blank",
+               // can't obtain state if sandboxed:
+               eLoadInUnsandboxedIframe | eLoadInPopup,
+               "about:blank", // we don't wait for this to load, so whatever
+               eSecureIfCreatorSecure,
+               eResultFromExaminationSync),
+];
+
+const loadTargets = [
+  new LoadTarget("an iframe",          eLoadInUnsandboxedIframe),
+  new LoadTarget("a sandboxed iframe", eLoadInSandboxedIframe),
+  new LoadTarget("a popup",            eLoadInPopup),
+];
+
+
+function LoadType(description, loadInFlags, uri, expectedSecureFlag, resultFrom) {
+  this.desc = description;
+  this.loadInFlags = loadInFlags;
+  this.uri = uri;
+  this.expectedSecureFlag = expectedSecureFlag;
+  this.resultFrom = resultFrom;
+}
+
+
+function LoadTarget(description, loadInFlag) {
+  this.desc = description;
+  this.loadInFlag = loadInFlag;
+}
+
+LoadTarget.prototype.open = function(loadType) {
+  let loadTarget = this;
+  this.currentTest.step(function() {
+    assert_true((loadTarget.loadInFlag & loadType.loadInFlags) != 0,
+                loadType.desc + " cannot be tested in " + loadTarget.desc);
+  });
+  if (this.loadInFlag == eLoadInUnsandboxedIframe) {
+    let iframe = document.createElement("iframe");
+    document.body.appendChild(iframe);
+    iframe[loadType.desc == "a srcdoc" ? "srcdoc" : "src"] = loadType.uri;
+    return iframe;
+  }
+  if (this.loadInFlag == eLoadInSandboxedIframe) {
+    let iframe = document.body.appendChild(document.createElement("iframe"));
+    iframe.setAttribute("sandbox", "allow-scripts");
+    iframe[loadType.desc == "a srcdoc" ? "srcdoc" : "src"] = loadType.uri;
+    return iframe;
+  }
+  if (this.loadInFlag == eLoadInPopup) {
+    return window.open(loadType.uri);
+  }
+  this.currentTest.step(function() {
+    assert_unreached("Unknown load type flag: " + loadInFlags);
+  });
+  return null;
+}
+
+LoadTarget.prototype.close = function(domTarget) {
+  if (this.loadInFlag == eLoadInUnsandboxedIframe ||
+      this.loadInFlag == eLoadInSandboxedIframe) {
+    domTarget.remove();
+    return;
+  }
+  if (this.loadInFlag == eLoadInPopup) {
+    domTarget.close();
+    return;
+  }
+  this.currentTest.step(function() {
+    assert_unreached("Unknown load type flag: " + loadInFlags);
+  });
+}
+
+LoadTarget.prototype.load_and_get_result_for = function(loadType) {
+  if (!(loadType.loadInFlags & this.loadInFlag)) {
+    return Promise.reject("not applicable");
+  }
+  if (!(this.loadInFlag & eLoadInPopup) &&
+      location.protocol == "https:" &&
+      loadType.uri.substr(0,5) == "http:") {
+    // Mixed content blocker will prevent this load
+    return Promise.reject("not applicable");
+  }
+  this.currentTest = async_test("Test Window.isSecureContext in " + this.desc +
+                                " loading " + loadType.desc)
+  if (loadType.resultFrom == eResultFromExaminationSync) {
+    let domTarget = this.open(loadType);
+    let result = domTarget instanceof Window ?
+          domTarget.isSecureContext : domTarget.contentWindow.isSecureContext;
+    this.close(domTarget);
+    return Promise.resolve(result);
+  }
+  let target = this;
+  if (loadType.resultFrom == eResultFromExaminationOnLoad) {
+    return new Promise(function(resolve, reject) {
+      function handleLoad(event) {
+        let result = domTarget instanceof Window ?
+              domTarget.isSecureContext : domTarget.contentWindow.isSecureContext;
+        domTarget.removeEventListener("load", handleLoad);
+        target.close(domTarget);
+        resolve(result);
+      }
+      let domTarget = target.open(loadType);
+      domTarget.addEventListener("load", handleLoad, false);
+    });
+  }
+  if (loadType.resultFrom == eResultFromPostMessage) {
+    return new Promise(function(resolve, reject) {
+      function handleMessage(event) {
+        window.removeEventListener("message", handleMessage);
+        target.close(domTarget);
+        resolve(event.data);
+      }
+      window.addEventListener("message", handleMessage, false);
+      let domTarget = target.open(loadType);
+   });
+  }
+  return Promise.reject("unexpected 'result from' type");
+}
+
+
+let current_type_index = -1;
+let current_target_index = 0;
+
+function run_next_test() {
+  current_type_index++;
+  if (current_type_index >= loadTypes.length) {
+    current_type_index = 0;
+    current_target_index++;
+    if (current_target_index >= loadTargets.length) {
+      done();
+      return; // all test permutations complete
+    }
+  }
+  let loadTarget = loadTargets[current_target_index];
+  let loadType = loadTypes[current_type_index];
+  loadTarget.load_and_get_result_for(loadType).then(
+    function(value) {
+      run_next_test_soon();
+      loadTarget.currentTest.step(function() {
+        if (loadType.expectedSecureFlag == eSecureNo) {
+          assert_false(value, loadType.desc + " in " + loadTarget.desc + " should not create a Secure Context");
+        } else if (loadType.expectedSecureFlag == eSecureIfCreatorSecure) {
+          if (!window.isSecureContext) {
+            assert_false(value, loadType.desc + " in " + loadTarget.desc + " should not create a Secure Context when its creator is not a Secure Context.");
+          } else {
+            assert_true(value, loadType.desc + " in " + loadTarget.desc + " should create a Secure Context when its creator is a Secure Context");
+          }
+        } else {
+          assert_unreached(loadType.desc + " - unknown expected secure flag: " + expectedSecureFlag);
+        }
+        loadTarget.currentTest.done();
+      });
+    },
+    function(failReason) {
+      run_next_test_soon();
+      if (failReason == "not applicable") {
+        return;
+      }
+      loadTarget.currentTest.step(function() {
+        assert_unreached(loadType.desc + " - got unexpected rejected promise");
+      });
+    }
+  );
+}
+
+function run_next_test_soon() {
+  setTimeout(run_next_test, 0);
+}
+
+function begin() {
+  test(function() {
+    if (location.protocol == "http:") {
+      assert_false(isSecureContext,
+                   "http: creator should not be a Secure Context");
+    } else if (location.protocol == "https:") {
+      assert_true(isSecureContext,
+                  "https: creator should be a Secure Context");
+    } else {
+      assert_unreached("Unknown location.protocol");
+    }
+  });
+  run_next_test();
+}
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/secure-contexts/postMessage-helper.html
@@ -0,0 +1,1 @@
+<script>(opener||parent).postMessage(isSecureContext, '*')</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/secure-contexts/postMessage-helper.https.html
@@ -0,0 +1,1 @@
+<script>(opener||parent).postMessage(isSecureContext, '*')</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/secure-contexts/server-locations.sub.js
@@ -0,0 +1,6 @@
+var https_dir = "https://{{location[hostname]}}:{{ports[https][0]}}{{location[path]}}";
+https_dir = https_dir.substr(0, https_dir.lastIndexOf("/") + 1);
+
+var http_dir = "http://{{location[hostname]}}:{{ports[http][0]}}{{location[path]}}";
+http_dir = http_dir.substr(0, http_dir.lastIndexOf("/") + 1);
+