summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/mixed-content/generic
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /testing/web-platform/tests/mixed-content/generic
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'testing/web-platform/tests/mixed-content/generic')
-rw-r--r--testing/web-platform/tests/mixed-content/generic/common.js398
-rw-r--r--testing/web-platform/tests/mixed-content/generic/expect.py102
-rw-r--r--testing/web-platform/tests/mixed-content/generic/mixed-content-test-case.js163
-rw-r--r--testing/web-platform/tests/mixed-content/generic/sanity-checker.js53
-rw-r--r--testing/web-platform/tests/mixed-content/generic/template/disclaimer.template1
-rw-r--r--testing/web-platform/tests/mixed-content/generic/template/spec_json.js.template1
-rw-r--r--testing/web-platform/tests/mixed-content/generic/template/test.debug.html.template31
-rw-r--r--testing/web-platform/tests/mixed-content/generic/template/test.js.template13
-rw-r--r--testing/web-platform/tests/mixed-content/generic/template/test.release.html.template20
-rw-r--r--testing/web-platform/tests/mixed-content/generic/template/test_description.template7
-rw-r--r--testing/web-platform/tests/mixed-content/generic/tools/__init__.py0
-rwxr-xr-xtesting/web-platform/tests/mixed-content/generic/tools/clean.py35
-rw-r--r--testing/web-platform/tests/mixed-content/generic/tools/common_paths.py58
-rwxr-xr-xtesting/web-platform/tests/mixed-content/generic/tools/generate.py157
-rwxr-xr-xtesting/web-platform/tests/mixed-content/generic/tools/regenerate3
-rwxr-xr-xtesting/web-platform/tests/mixed-content/generic/tools/spec_validator.py159
-rw-r--r--testing/web-platform/tests/mixed-content/generic/worker.js1
17 files changed, 1202 insertions, 0 deletions
diff --git a/testing/web-platform/tests/mixed-content/generic/common.js b/testing/web-platform/tests/mixed-content/generic/common.js
new file mode 100644
index 000000000..36427a466
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/common.js
@@ -0,0 +1,398 @@
+/**
+ * @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.
+ */
+
+/**
+ * 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)
+ return reject(Error(xhr.statusText));
+
+ 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.
+ * @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", resolve);
+ element.addEventListener(rejectEventName || "error",
+ function(e) { e.preventDefault(); reject(); } );
+ });
+}
+
+/**
+ * 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, parent, 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 (parent)
+ parent.appendChild(element);
+
+ if (isImg)
+ setAttributes(element, attrs);
+
+ return element;
+}
+
+function createRequestViaElement(tagName, attrs, parent) {
+ return createElement(tagName, attrs, parent, 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);
+}
+
+/**
+ * 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);
+}
+
+/**
+ * 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);
+}
+
+/**
+ * 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);
+}
+
+/**
+ * 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);
+}
+
+/**
+ * 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.
+ * @return {Promise} The promise for success/error events.
+ */
+function requestViaWorker(url) {
+ var worker = new Worker(url);
+ bindEvents(worker, "message", "error");
+ worker.postMessage('');
+
+ return worker.eventPromise;
+}
+
+/**
+ * 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);
+ setAttributes(navigableElement,
+ {"href": url,
+ "target": iframe.name});
+ navigableElement.click();
+
+ return iframe.eventPromise;
+}
+
+/**
+ * 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);
+
+ 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);
+
+ 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);
+}
+
+/**
+ * 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();
+
+ 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);
+}
+
+/**
+ * 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) {
+ // TODO(kristijanburnik): Check if prefetch should support load and error
+ // events. For now we assume it's not specified.
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Link_prefetching_FAQ
+ return createRequestViaElement("link",
+ {"rel": "prefetch", "href": url},
+ document.head);
+}
+
+/**
+ * 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);
+
+ mediaElement.eventPromise = new Promise(function(resolve, reject) {
+ mediaElement.addEventListener("loadeddata", resolve);
+ // Notice that the source element will raise the error.
+ sourceElement.addEventListener("error", reject);
+ });
+
+ setAttributes(mediaElement, media_attrs);
+ setAttributes(sourceElement, source_attrs);
+ document.body.appendChild(mediaElement);
+
+ return mediaElement;
+}
+
+/**
+ * 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",
+ {},
+ {type: "video/mp4", src: url}).eventPromise;
+}
+
+/**
+ * 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/mpeg", src: url}).eventPromise;
+}
+
+/**
+ * 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);
+}
+
+/**
+ * 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}, document.body);
+}
+
+/**
+ * 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(JSON.parse(e.data));
+ });
+
+ websocket.addEventListener("open", function(e) {
+ websocket.send("echo");
+ });
+
+ websocket.addEventListener("error", function(e) {
+ reject(e)
+ });
+ });
+}
+
+// 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) {};
diff --git a/testing/web-platform/tests/mixed-content/generic/expect.py b/testing/web-platform/tests/mixed-content/generic/expect.py
new file mode 100644
index 000000000..a3ea61b21
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/expect.py
@@ -0,0 +1,102 @@
+import json, os, urllib, urlparse
+
+def redirect(url, response):
+ response.add_required_headers = False
+ response.writer.write_status(301)
+ response.writer.write_header("access-control-allow-origin", "*")
+ response.writer.write_header("location", url)
+ response.writer.end_headers()
+ response.writer.write("")
+
+def create_redirect_url(request, swap_scheme = False):
+ parsed = urlparse.urlsplit(request.url)
+ destination_netloc = parsed.netloc
+ scheme = parsed.scheme
+
+ if swap_scheme:
+ scheme = "http" if parsed.scheme == "https" else "https"
+ hostname = parsed.netloc.split(':')[0]
+ port = request.server.config["ports"][scheme][0]
+ destination_netloc = ":".join([hostname, str(port)])
+
+ # Remove "redirection" from query to avoid redirect loops.
+ parsed_query = dict(urlparse.parse_qsl(parsed.query))
+ assert "redirection" in parsed_query
+ del parsed_query["redirection"]
+
+ destination_url = urlparse.urlunsplit(urlparse.SplitResult(
+ scheme = scheme,
+ netloc = destination_netloc,
+ path = parsed.path,
+ query = urllib.urlencode(parsed_query),
+ fragment = None))
+
+ return destination_url
+
+def main(request, response):
+ if "redirection" in request.GET:
+ redirection = request.GET["redirection"]
+ if redirection == "no-redirect":
+ pass
+ elif redirection == "keep-scheme-redirect":
+ redirect(create_redirect_url(request, swap_scheme=False), response)
+ return
+ elif redirection == "swap-scheme-redirect":
+ redirect(create_redirect_url(request, swap_scheme=True), response)
+ return
+ else:
+ raise ValueError ("Invalid redirect type: %s" % redirection)
+
+ content_type = "text/plain"
+ response_data = ""
+
+ if "action" in request.GET:
+ action = request.GET["action"]
+
+ if "content_type" in request.GET:
+ content_type = request.GET["content_type"]
+
+ key = request.GET["key"]
+ stash = request.server.stash
+ path = request.GET.get("path", request.url.split('?'))[0]
+
+ if action == "put":
+ value = request.GET["value"]
+ stash.take(key=key, path=path)
+ stash.put(key=key, value=value, path=path)
+ response_data = json.dumps({"status": "success", "result": key})
+ elif action == "purge":
+ value = stash.take(key=key, path=path)
+ if content_type == "image/png":
+ response_data = open(os.path.join(request.doc_root,
+ "images",
+ "smiley.png"), "rb").read()
+ elif content_type == "audio/mpeg":
+ response_data = open(os.path.join(request.doc_root,
+ "media",
+ "sound_5.oga"), "rb").read()
+ elif content_type == "video/mp4":
+ response_data = open(os.path.join(request.doc_root,
+ "media",
+ "movie_5.mp4"), "rb").read()
+ elif content_type == "application/javascript":
+ response_data = open(os.path.join(request.doc_root,
+ "mixed-content",
+ "generic",
+ "worker.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"
+ response_data = json.dumps({"status": status, "result": value})
+
+ response.add_required_headers = False
+ response.writer.write_status(200)
+ response.writer.write_header("content-type", content_type)
+ response.writer.write_header("cache-control", "no-cache; must-revalidate")
+ response.writer.end_headers()
+ response.writer.write(response_data)
diff --git 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
new file mode 100644
index 000000000..7f3a7bfa9
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/mixed-content-test-case.js
@@ -0,0 +1,163 @@
+/**
+ * @fileoverview Test case for mixed-content in Web Platform Tests.
+ * @author burnik@google.com (Kristijan Burnik)
+ */
+
+/**
+ * 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
+ * running scenario. Useful in debug mode. See ./sanity-checker.js.
+ * Run {@code ./tools/generate.py -h} for info on test generating modes.
+ * @return {object} Object wrapping the start method used to run the test.
+ */
+function MixedContentTestCase(scenario, description, sanityChecker) {
+ var httpProtocol = "http";
+ var httpsProtocol = "https";
+ var wsProtocol = "ws";
+ var wssProtocol = "wss";
+
+ var sameOriginHost = location.hostname;
+ var crossOriginHost = "{{domains[www1]}}";
+
+ // These values can evaluate to either empty strings or a ":port" string.
+ var httpPort = getNormalizedPort(parseInt("{{ports[http][0]}}", 10));
+ var httpsPort = getNormalizedPort(parseInt("{{ports[https][0]}}", 10));
+ var wsPort = getNormalizedPort(parseInt("{{ports[ws][0]}}", 10));
+ var wssPort = getNormalizedPort(parseInt("{{ports[wss][0]}}", 10));
+
+ var resourcePath = "/mixed-content/generic/expect.py";
+ var wsResourcePath = "/stash_responder";
+
+ // Map all endpoints to scenario for use in the test.
+ var endpoint = {
+ "same-origin":
+ location.origin + resourcePath,
+ "same-host-https":
+ httpsProtocol + "://" + sameOriginHost + httpsPort + resourcePath,
+ "same-host-http":
+ httpProtocol + "://" + sameOriginHost + httpPort + resourcePath,
+ "cross-origin-https":
+ httpsProtocol + "://" + crossOriginHost + httpsPort + resourcePath,
+ "cross-origin-http":
+ httpProtocol + "://" + crossOriginHost + httpPort + resourcePath,
+ "same-host-wss":
+ wssProtocol + "://" + sameOriginHost + wssPort + wsResourcePath,
+ "same-host-ws":
+ wsProtocol + "://" + sameOriginHost + wsPort + wsResourcePath,
+ "cross-origin-wss":
+ wssProtocol + "://" + crossOriginHost + wssPort + wsResourcePath,
+ "cross-origin-ws":
+ wsProtocol + "://" + crossOriginHost + wsPort + wsResourcePath
+ };
+
+ // Mapping all the resource requesting methods to the scenario.
+ var resourceMap = {
+ "a-tag": requestViaAnchor,
+ "area-tag": requestViaArea,
+ "fetch-request": requestViaFetch,
+ "form-tag": requestViaForm,
+ "iframe-tag": requestViaIframe,
+ "img-tag": requestViaImage,
+ "script-tag": requestViaScript,
+ "worker-request": requestViaWorker,
+ "xhr-request": requestViaXhr,
+ "audio-tag": requestViaAudio,
+ "video-tag": requestViaVideo,
+ "picture-tag": requestViaPicture,
+ "object-tag": requestViaObject,
+ "link-css-tag": requestViaLinkStylesheet,
+ "link-prefetch-tag": requestViaLinkPrefetch,
+ "websocket-request": requestViaWebSocket
+ };
+
+ sanityChecker.checkScenario(scenario, resourceMap);
+
+ // Mapping all expected MIME types to the scenario.
+ var contentType = {
+ "a-tag": "text/html",
+ "area-tag": "text/html",
+ "fetch-request": "application/json",
+ "form-tag": "text/html",
+ "iframe-tag": "text/html",
+ "img-tag": "image/png",
+ "script-tag": "text/javascript",
+ "worker-request": "application/javascript",
+ "xhr-request": "application/json",
+ "audio-tag": "audio/mpeg",
+ "video-tag": "video/mp4",
+ "picture-tag": "image/png",
+ "object-tag": "text/html",
+ "link-css-tag": "text/css",
+ "link-prefetch-tag": "text/html",
+ "websocket-request": "application/json"
+ };
+
+ var mixed_content_test = async_test(description);
+
+ function runTest() {
+ sanityChecker.setFailTimeout(mixed_content_test);
+
+ var key = guid();
+ var value = guid();
+ // We use the same path for both HTTP/S and WS/S stash requests.
+ var stash_path = encodeURIComponent("/mixed-content");
+ var announceResourceRequestUrl = endpoint['same-origin'] +
+ "?action=put&key=" + key +
+ "&value=" + value +
+ "&path=" + stash_path;
+ var assertResourceRequestUrl = endpoint['same-origin'] +
+ "?action=take&key=" + key +
+ "&path=" + stash_path;
+ var resourceRequestUrl = endpoint[scenario.origin] + "?redirection=" +
+ scenario.redirection + "&action=purge&key=" + key +
+ "&path=" + stash_path + "&content_type=" +
+ contentType[scenario.subresource];
+
+ xhrRequest(announceResourceRequestUrl)
+ .then(function(response) {
+ // Send out the real resource request.
+ // This should tear down the key if it's not blocked.
+ return resourceMap[scenario.subresource](resourceRequestUrl);
+ })
+ .then(function() {
+ mixed_content_test.step(function() {
+ assert_equals("allowed", scenario.expectation,
+ "The triggered event should match '" +
+ scenario.expectation + "'.");
+ }, "Check if success event was triggered.");
+
+ // Send request to check if the key has been torn down.
+ return xhrRequest(assertResourceRequestUrl);
+ }, function(error) {
+ mixed_content_test.step(function() {
+ assert_equals("blocked", scenario.expectation,
+ "The triggered event should match '" +
+ scenario.expectation + "'.");
+ // TODO(kristijanburnik): param "error" can be an event or error.
+ // Map assertion by resource.
+ // e.g.: assert_equals(e.type, "error");
+ }, "Check if error event was triggered.");
+
+ // When requestResource fails, we also check the key state.
+ return xhrRequest(assertResourceRequestUrl);
+ })
+ .then(function(response) {
+ // Now check if the value has been torn down. If it's still there,
+ // we have blocked the request to mixed-content.
+ mixed_content_test.step(function() {
+ assert_equals(response.status, scenario.expectation,
+ "The resource request should be '" + scenario.expectation +
+ "'.");
+ }, "Check if request was sent.");
+ mixed_content_test.done();
+ });
+
+ } // runTest
+
+ return {start: runTest};
+} // MixedContentTestCase
diff --git a/testing/web-platform/tests/mixed-content/generic/sanity-checker.js b/testing/web-platform/tests/mixed-content/generic/sanity-checker.js
new file mode 100644
index 000000000..55a103adf
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/sanity-checker.js
@@ -0,0 +1,53 @@
+// The SanityChecker is used in debug mode to identify problems with the
+// structure of the testsuite and to force early test failures.
+// In release mode it is mocked out to do nothing.
+function SanityChecker() {}
+
+SanityChecker.prototype.checkScenario = function(scenario, resourceInvoker) {
+ // Check if scenario is valid.
+ test(function() {
+ var expectedFields = SPEC_JSON["test_expansion_schema"];
+
+ for (var field in expectedFields) {
+ if (field == "expansion")
+ continue
+
+ assert_own_property(scenario, field,
+ "The scenario should contain field '" + field + "'.")
+
+ var expectedFieldList = expectedFields[field];
+ if (!expectedFieldList.hasOwnProperty('length')) {
+ var expectedFieldList = [];
+ for (var key in expectedFields[field]) {
+ expectedFieldList = expectedFieldList.concat(expectedFields[field][key])
+ }
+ }
+ assert_in_array(scenario[field], expectedFieldList,
+ "Scenario's " + field + " is one of: " +
+ expectedFieldList.join(", ")) + "."
+ }
+
+ // Check if the protocol is matched.
+ assert_equals(scenario["source_scheme"] + ":", location.protocol,
+ "Protocol of the test page should match the scenario.")
+
+ assert_own_property(resourceInvoker, scenario.subresource,
+ "Subresource should be supported");
+
+ }, "[MixedContentTestCase] The test scenario should be valid.");
+}
+
+// For easier debugging runs, we can fail a test earlier.
+SanityChecker.prototype.setFailTimeout = function(test, timeout) {
+ // Due to missing implementations, tests time out, so we fail them early.
+ // TODO(kristijanburnik): Once WPT rolled in:
+ // https://github.com/w3c/testharness.js/pull/127
+ // Refactor to make use of step_timeout.
+ setTimeout(function() {
+ test.step(function() {
+ assert_equals(test.phase, test.phases.COMPLETE,
+ "Expected test to complete.");
+ test.done();
+ })
+ }, timeout || 1000);
+}
diff --git a/testing/web-platform/tests/mixed-content/generic/template/disclaimer.template b/testing/web-platform/tests/mixed-content/generic/template/disclaimer.template
new file mode 100644
index 000000000..66c43ed6f
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/template/disclaimer.template
@@ -0,0 +1 @@
+<!-- DO NOT EDIT! Generated by %(generating_script_filename)s using %(html_template_filename)s. -->
diff --git a/testing/web-platform/tests/mixed-content/generic/template/spec_json.js.template b/testing/web-platform/tests/mixed-content/generic/template/spec_json.js.template
new file mode 100644
index 000000000..e4cbd0342
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/template/spec_json.js.template
@@ -0,0 +1 @@
+var SPEC_JSON = %(spec_json)s;
diff --git a/testing/web-platform/tests/mixed-content/generic/template/test.debug.html.template b/testing/web-platform/tests/mixed-content/generic/template/test.debug.html.template
new file mode 100644
index 000000000..013bb6250
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/template/test.debug.html.template
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+%(generated_disclaimer)s
+<html>
+ <head>
+ <title>Mixed-Content: %(spec_title)s</title>
+ <meta charset='utf-8'>
+ <meta name="description" content="%(spec_description)s">
+ <meta name="assert" content="%(test_description)s">%(meta_opt_in)s
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Common global functions for mixed-content tests. -->
+ <script src="/mixed-content/generic/common.js"></script>
+ <!-- The original specification JSON for validating the scenario. -->
+ <script src="/mixed-content/spec_json.js"></script>
+ <!-- Internal checking of the tests -->
+ <script src="/mixed-content/generic/sanity-checker.js"></script>
+ <!-- Simple wrapper API for all mixed-content test cases. -->
+ <script src="/mixed-content/generic/mixed-content-test-case.js?pipe=sub"></script>
+ </head>
+ <body>
+ <h1>%(spec_title)s</h1>
+ <h2>%(spec_description)s</h2>
+ <pre>%(test_description)s</pre>
+
+ <p>See <a href="%(spec_specification_url)s" target="_blank">specification</a>
+ details for this test.</p>
+
+ <div id="log"></div>
+ <script>%(test_js)s</script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/mixed-content/generic/template/test.js.template b/testing/web-platform/tests/mixed-content/generic/template/test.js.template
new file mode 100644
index 000000000..b8c0769fc
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/template/test.js.template
@@ -0,0 +1,13 @@
+MixedContentTestCase(
+ {
+ "opt_in_method": "%(opt_in_method)s",
+ "origin": "%(origin)s",
+ "source_scheme": "%(source_scheme)s",
+ "context_nesting": "%(context_nesting)s",
+ "redirection": "%(redirection)s",
+ "subresource": "%(subresource)s",
+ "expectation": "%(expectation)s"
+ },
+ document.querySelector("meta[name=assert]").content,
+ new SanityChecker()
+).start();
diff --git a/testing/web-platform/tests/mixed-content/generic/template/test.release.html.template b/testing/web-platform/tests/mixed-content/generic/template/test.release.html.template
new file mode 100644
index 000000000..ca77389cc
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/template/test.release.html.template
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+%(generated_disclaimer)s
+<html>
+ <head>
+ <title>Mixed-Content: %(spec_title)s</title>
+ <meta charset='utf-8'>
+ <meta name="description" content="%(spec_description)s">
+ <link rel="author" title="Kristijan Burnik" href="burnik@chromium.org">
+ <link rel="help" href="%(spec_specification_url)s">
+ <meta name="assert" content="%(test_description)s">%(meta_opt_in)s
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/mixed-content/generic/common.js"></script>
+ <script src="/mixed-content/generic/mixed-content-test-case.js?pipe=sub"></script>
+ </head>
+ <body>
+ <script>%(test_js)s</script>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/mixed-content/generic/template/test_description.template b/testing/web-platform/tests/mixed-content/generic/template/test_description.template
new file mode 100644
index 000000000..33dbcaa05
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/template/test_description.template
@@ -0,0 +1,7 @@
+opt_in_method: %(opt_in_method)s
+origin: %(origin)s
+source_scheme: %(source_scheme)s
+context_nesting: %(context_nesting)s
+redirection: %(redirection)s
+subresource: %(subresource)s
+expectation: %(expectation)s
diff --git a/testing/web-platform/tests/mixed-content/generic/tools/__init__.py b/testing/web-platform/tests/mixed-content/generic/tools/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/tools/__init__.py
diff --git a/testing/web-platform/tests/mixed-content/generic/tools/clean.py b/testing/web-platform/tests/mixed-content/generic/tools/clean.py
new file mode 100755
index 000000000..9416f0b5b
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/tools/clean.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+
+import os, json
+from common_paths import *
+import spec_validator
+
+def rmtree(top):
+ top = os.path.abspath(top)
+ assert top != os.path.expanduser("~")
+ assert len(top) > len(os.path.expanduser("~"))
+ assert "web-platform-tests" in top
+ assert "mixed-content" in top
+
+ for root, dirs, files in os.walk(top, topdown=False):
+ for name in files:
+ os.remove(os.path.join(root, name))
+ for name in dirs:
+ os.rmdir(os.path.join(root, name))
+
+ os.rmdir(top)
+
+def main():
+ spec_json = load_spec_json();
+ spec_validator.assert_valid_spec_json(spec_json)
+
+ for spec in spec_json['specification']:
+ generated_dir = os.path.join(spec_directory, spec["name"])
+ if (os.path.isdir(generated_dir)):
+ rmtree(generated_dir)
+
+ if (os.path.isfile(generated_spec_json_filename)):
+ os.remove(generated_spec_json_filename)
+
+if __name__ == '__main__':
+ main()
diff --git a/testing/web-platform/tests/mixed-content/generic/tools/common_paths.py b/testing/web-platform/tests/mixed-content/generic/tools/common_paths.py
new file mode 100644
index 000000000..5c2807d28
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/tools/common_paths.py
@@ -0,0 +1,58 @@
+import os, sys, json, re
+
+script_directory = os.path.dirname(os.path.abspath(__file__))
+generic_directory = os.path.abspath(os.path.join(script_directory, '..'))
+
+template_directory = os.path.abspath(os.path.join(script_directory,
+ '..',
+ 'template'))
+spec_directory = os.path.abspath(os.path.join(script_directory, '..', '..'))
+test_root_directory = os.path.abspath(os.path.join(script_directory,
+ '..', '..', '..'))
+
+spec_filename = os.path.join(spec_directory, "spec.src.json")
+generated_spec_json_filename = os.path.join(spec_directory, "spec_json.js")
+
+selection_pattern = '%(opt_in_method)s/' + \
+ '%(origin)s/' + \
+ '%(subresource)s/' + \
+ '%(context_nesting)s/' + \
+ '%(redirection)s/'
+
+test_file_path_pattern = '%(spec_name)s/' + selection_pattern + \
+ '%(name)s.%(source_scheme)s.html'
+
+
+def get_template(basename):
+ with open(os.path.join(template_directory, basename), "r") as f:
+ return f.read()
+
+
+def write_file(filename, contents):
+ with open(filename, "w") as f:
+ f.write(contents)
+
+
+def read_nth_line(fp, line_number):
+ fp.seek(0)
+ for i, line in enumerate(fp):
+ if (i + 1) == line_number:
+ return line
+
+
+def load_spec_json(path_to_spec = None):
+ if path_to_spec is None:
+ path_to_spec = spec_filename
+
+ re_error_location = re.compile('line ([0-9]+) column ([0-9]+)')
+ with open(path_to_spec, "r") as f:
+ try:
+ return json.load(f)
+ except ValueError, ex:
+ print ex.message
+ match = re_error_location.search(ex.message)
+ if match:
+ line_number, column = int(match.group(1)), int(match.group(2))
+ print read_nth_line(f, line_number).rstrip()
+ print " " * (column - 1) + "^"
+ sys.exit(1)
diff --git a/testing/web-platform/tests/mixed-content/generic/tools/generate.py b/testing/web-platform/tests/mixed-content/generic/tools/generate.py
new file mode 100755
index 000000000..6dcaebdc3
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/tools/generate.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python
+
+import os, sys, json
+from common_paths import *
+import spec_validator
+import argparse
+
+
+def expand_pattern(expansion_pattern, test_expansion_schema):
+ expansion = {}
+ for artifact_key in expansion_pattern:
+ artifact_value = expansion_pattern[artifact_key]
+ if artifact_value == '*':
+ expansion[artifact_key] = test_expansion_schema[artifact_key]
+ elif isinstance(artifact_value, list):
+ expansion[artifact_key] = artifact_value
+ elif isinstance(artifact_value, dict):
+ # Flattened expansion.
+ expansion[artifact_key] = []
+ values_dict = expand_pattern(artifact_value,
+ test_expansion_schema[artifact_key])
+ for sub_key in values_dict.keys():
+ expansion[artifact_key] += values_dict[sub_key]
+ else:
+ expansion[artifact_key] = [artifact_value]
+
+ return expansion
+
+
+def permute_expansion(expansion, artifact_order, selection = {}, artifact_index = 0):
+ assert isinstance(artifact_order, list), "artifact_order should be a list"
+
+ if artifact_index >= len(artifact_order):
+ yield selection
+ return
+
+ artifact_key = artifact_order[artifact_index]
+
+ for artifact_value in expansion[artifact_key]:
+ selection[artifact_key] = artifact_value
+ for next_selection in permute_expansion(expansion,
+ artifact_order,
+ selection,
+ artifact_index + 1):
+ yield next_selection
+
+
+def generate_selection(selection, spec, test_html_template_basename):
+ selection['spec_name'] = spec['name']
+ selection['spec_title'] = spec['title']
+ selection['spec_description'] = spec['description']
+ selection['spec_specification_url'] = spec['specification_url']
+
+ test_filename = test_file_path_pattern % selection
+ test_headers_filename = test_filename + ".headers"
+ test_directory = os.path.dirname(test_filename)
+ full_path = os.path.join(spec_directory, test_directory)
+
+ test_html_template = get_template(test_html_template_basename)
+ test_js_template = get_template("test.js.template")
+ disclaimer_template = get_template('disclaimer.template')
+ test_description_template = get_template("test_description.template")
+
+ html_template_filename = os.path.join(template_directory,
+ test_html_template_basename)
+ generated_disclaimer = disclaimer_template \
+ % {'generating_script_filename': os.path.relpath(__file__,
+ test_root_directory),
+ 'html_template_filename': os.path.relpath(html_template_filename,
+ test_root_directory)}
+
+ selection['generated_disclaimer'] = generated_disclaimer.rstrip()
+ test_description_template = \
+ test_description_template.rstrip().replace("\n", "\n" + " " * 33)
+ selection['test_description'] = test_description_template % selection
+
+ # Adjust the template for the test invoking JS. Indent it to look nice.
+ indent = "\n" + " " * 6;
+ test_js_template = indent + test_js_template.replace("\n", indent);
+ selection['test_js'] = test_js_template % selection
+
+ # Directory for the test files.
+ try:
+ os.makedirs(full_path)
+ except:
+ pass
+
+ # TODO(kristijanburnik): Implement the opt-in-method here.
+ opt_in_method = selection['opt_in_method']
+ selection['meta_opt_in'] = ''
+ if opt_in_method == 'meta-csp':
+ selection['meta_opt_in'] = '\n <meta http-equiv="Content-Security-Policy" ' + \
+ 'content="block-all-mixed-content">'
+ elif opt_in_method == 'http-csp':
+ opt_in_headers = "Content-Security-Policy: block-all-mixed-content\n"
+ write_file(test_headers_filename, opt_in_headers)
+ elif opt_in_method == 'no-opt-in':
+ pass
+ else:
+ raise ValueError("Invalid opt_in_method %s" % opt_in_method)
+
+ # Write out the generated HTML file.
+ write_file(test_filename, test_html_template % selection)
+
+def generate_test_source_files(spec_json, target):
+ test_expansion_schema = spec_json['test_expansion_schema']
+ specification = spec_json['specification']
+
+ spec_json_js_template = get_template('spec_json.js.template')
+ write_file(generated_spec_json_filename,
+ spec_json_js_template % {'spec_json': json.dumps(spec_json)})
+
+ # Choose a debug/release template depending on the target.
+ html_template = "test.%s.html.template" % target
+
+ artifact_order = test_expansion_schema.keys() + ['name']
+
+ # Create list of excluded tests.
+ exclusion_dict = {}
+ for excluded_pattern in spec_json['excluded_tests']:
+ excluded_expansion = \
+ expand_pattern(excluded_pattern,
+ test_expansion_schema)
+ for excluded_selection in permute_expansion(excluded_expansion, artifact_order):
+ excluded_selection_path = selection_pattern % excluded_selection
+ exclusion_dict[excluded_selection_path] = True
+
+ for spec in specification:
+ for expansion_pattern in spec['test_expansion']:
+ expansion = expand_pattern(expansion_pattern,
+ test_expansion_schema)
+ for selection in permute_expansion(expansion, artifact_order):
+ selection_path = selection_pattern % selection
+ if not selection_path in exclusion_dict:
+ generate_selection(selection,
+ spec,
+ html_template)
+ else:
+ print 'Excluding selection:', selection_path
+
+
+def main(target, spec_filename):
+ spec_json = load_spec_json(spec_filename);
+ spec_validator.assert_valid_spec_json(spec_json)
+ generate_test_source_files(spec_json, target)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Test suite generator utility')
+ parser.add_argument('-t', '--target', type = str,
+ choices = ("release", "debug"), default = "release",
+ help = 'Sets the appropriate template for generating tests')
+ parser.add_argument('-s', '--spec', type = str, default = None,
+ help = 'Specify a file used for describing and generating the tests')
+ # TODO(kristijanburnik): Add option for the spec_json file.
+ args = parser.parse_args()
+ main(args.target, args.spec)
diff --git a/testing/web-platform/tests/mixed-content/generic/tools/regenerate b/testing/web-platform/tests/mixed-content/generic/tools/regenerate
new file mode 100755
index 000000000..e6bd63519
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/tools/regenerate
@@ -0,0 +1,3 @@
+#!/bin/bash
+DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+python $DIR/clean.py && python $DIR/generate.py
diff --git a/testing/web-platform/tests/mixed-content/generic/tools/spec_validator.py b/testing/web-platform/tests/mixed-content/generic/tools/spec_validator.py
new file mode 100755
index 000000000..a6acc1040
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/tools/spec_validator.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+
+import json, sys
+from common_paths import *
+
+def assert_non_empty_string(obj, field):
+ assert field in obj, 'Missing field "%s"' % field
+ assert isinstance(obj[field], basestring), \
+ 'Field "%s" must be a string' % field
+ assert len(obj[field]) > 0, 'Field "%s" must not be empty' % field
+
+
+def assert_non_empty_list(obj, field):
+ assert isinstance(obj[field], list), \
+ '%s must be a list' % field
+ assert len(obj[field]) > 0, \
+ '%s list must not be empty' % field
+
+
+def assert_non_empty_dict(obj, field):
+ assert isinstance(obj[field], dict), \
+ '%s must be a dict' % field
+ assert len(obj[field]) > 0, \
+ '%s dict must not be empty' % field
+
+
+def assert_contains(obj, field):
+ assert field in obj, 'Must contain field "%s"' % field
+
+
+def assert_string_from(obj, field, items):
+ assert obj[field] in items, \
+ 'Field "%s" must be from: %s' % (field, str(items))
+
+
+def assert_string_or_list_items_from(obj, field, items):
+ if isinstance(obj[field], basestring):
+ assert_string_from(obj, field, items)
+ return
+
+ assert isinstance(obj[field], list), "%s must be a list!" % field
+ for allowed_value in obj[field]:
+ assert allowed_value != '*', "Wildcard is not supported for lists!"
+ assert allowed_value in items, \
+ 'Field "%s" must be from: %s' % (field, str(items))
+
+
+def assert_contains_only_fields(obj, expected_fields):
+ for expected_field in expected_fields:
+ assert_contains(obj, expected_field)
+
+ for actual_field in obj:
+ assert actual_field in expected_fields, \
+ 'Unexpected field "%s".' % actual_field
+
+
+def assert_value_unique_in(value, used_values):
+ assert value not in used_values, 'Duplicate value "%s"!' % str(value)
+ used_values[value] = True
+
+
+def assert_valid_artifact(exp_pattern, artifact_key, schema):
+ if isinstance(schema, list):
+ assert_string_or_list_items_from(exp_pattern, artifact_key,
+ ["*"] + schema)
+ return
+
+ for sub_artifact_key, sub_schema in schema.iteritems():
+ assert_valid_artifact(exp_pattern[artifact_key], sub_artifact_key,
+ sub_schema)
+
+def validate(spec_json, details):
+ """ Validates the json specification for generating tests. """
+
+ details['object'] = spec_json
+ assert_contains_only_fields(spec_json, ["specification",
+ "test_expansion_schema",
+ "excluded_tests"])
+ assert_non_empty_list(spec_json, "specification")
+ assert_non_empty_dict(spec_json, "test_expansion_schema")
+ assert_non_empty_list(spec_json, "excluded_tests")
+
+ specification = spec_json['specification']
+ test_expansion_schema = spec_json['test_expansion_schema']
+ excluded_tests = spec_json['excluded_tests']
+
+ valid_test_expansion_fields = ['name'] + test_expansion_schema.keys()
+
+ # Validate each single spec.
+ for spec in specification:
+ details['object'] = spec
+
+ # Validate required fields for a single spec.
+ assert_contains_only_fields(spec, ['name',
+ 'title',
+ 'description',
+ 'specification_url',
+ 'test_expansion'])
+ assert_non_empty_string(spec, 'name')
+ assert_non_empty_string(spec, 'title')
+ assert_non_empty_string(spec, 'description')
+ assert_non_empty_string(spec, 'specification_url')
+ assert_non_empty_list(spec, 'test_expansion')
+
+ # Validate spec's test expansion.
+ used_spec_names = {}
+
+ for spec_exp in spec['test_expansion']:
+ details['object'] = spec_exp
+ assert_non_empty_string(spec_exp, 'name')
+ # The name is unique in same expansion group.
+ assert_value_unique_in((spec_exp['expansion'], spec_exp['name']),
+ used_spec_names)
+ assert_contains_only_fields(spec_exp, valid_test_expansion_fields)
+
+ for artifact in test_expansion_schema:
+ details['test_expansion_field'] = artifact
+ assert_valid_artifact(spec_exp, artifact,
+ test_expansion_schema[artifact])
+ del details['test_expansion_field']
+
+ # Validate the test_expansion schema members.
+ details['object'] = test_expansion_schema
+ assert_contains_only_fields(test_expansion_schema, ['expansion',
+ 'source_scheme',
+ 'opt_in_method',
+ 'context_nesting',
+ 'redirection',
+ 'subresource',
+ 'origin',
+ 'expectation'])
+ # Validate excluded tests.
+ details['object'] = excluded_tests
+ for excluded_test_expansion in excluded_tests:
+ assert_contains_only_fields(excluded_test_expansion,
+ valid_test_expansion_fields)
+
+
+ del details['object']
+
+
+def assert_valid_spec_json(spec_json):
+ error_details = {}
+ try:
+ validate(spec_json, error_details)
+ except AssertionError, err:
+ print 'ERROR:', err.message
+ print json.dumps(error_details, indent=4)
+ sys.exit(1)
+
+
+def main():
+ spec_json = load_spec_json();
+ assert_valid_spec_json(spec_json)
+ print "Spec JSON is valid."
+
+
+if __name__ == '__main__':
+ main()
diff --git a/testing/web-platform/tests/mixed-content/generic/worker.js b/testing/web-platform/tests/mixed-content/generic/worker.js
new file mode 100644
index 000000000..7e2168bcc
--- /dev/null
+++ b/testing/web-platform/tests/mixed-content/generic/worker.js
@@ -0,0 +1 @@
+postMessage('done');