diff --git a/content/base/test/xcsp/chrome.ini b/content/base/test/xcsp/chrome.ini
new file mode 100644
index 00000000000..fb55f3e1272
--- /dev/null
+++ b/content/base/test/xcsp/chrome.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+
+[test_csp_bug768029.html]
+[test_csp_bug773891.html]
diff --git a/content/base/test/xcsp/file_CSP.css b/content/base/test/xcsp/file_CSP.css
new file mode 100644
index 00000000000..f83930e541c
--- /dev/null
+++ b/content/base/test/xcsp/file_CSP.css
@@ -0,0 +1,20 @@
+/*
+ * Moved this CSS from an inline stylesheet to an external file when we added
+ * inline-style blocking in bug 763879.
+ * This test may hang if the load for this .css file is blocked due to a
+ * malfunction of CSP, but should pass if the style_good test passes.
+ */
+
+/* CSS font embedding tests */
+@font-face {
+ font-family: "arbitrary_good";
+ src: url('file_CSP.sjs?testid=font_good&type=application/octet-stream');
+}
+@font-face {
+ font-family: "arbitrary_bad";
+ src: url('http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=font_bad&type=application/octet-stream');
+}
+
+.div_arbitrary_good { font-family: "arbitrary_good"; }
+.div_arbitrary_bad { font-family: "arbitrary_bad"; }
+
diff --git a/content/base/test/xcsp/file_CSP.sjs b/content/base/test/xcsp/file_CSP.sjs
new file mode 100644
index 00000000000..85c2df3ba47
--- /dev/null
+++ b/content/base/test/xcsp/file_CSP.sjs
@@ -0,0 +1,26 @@
+// SJS file for CSP mochitests
+
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ var isPreflight = request.method == "OPTIONS";
+
+
+ //avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ if ("type" in query) {
+ response.setHeader("Content-Type", unescape(query['type']), false);
+ } else {
+ response.setHeader("Content-Type", "text/html", false);
+ }
+
+ if ("content" in query) {
+ response.write(unescape(query['content']));
+ }
+}
diff --git a/content/base/test/xcsp/file_CSP_evalscript_main.js b/content/base/test/xcsp/file_CSP_evalscript_main.js
new file mode 100644
index 00000000000..a00fbb8362e
--- /dev/null
+++ b/content/base/test/xcsp/file_CSP_evalscript_main.js
@@ -0,0 +1,126 @@
+// some javascript for the CSP eval() tests
+
+function logResult(str, passed) {
+ var elt = document.createElement('div');
+ var color = passed ? "#cfc;" : "#fcc";
+ elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+ elt.innerHTML = str;
+ document.body.appendChild(elt);
+}
+
+window._testResults = {};
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+ return function(shouldrun, what, data) {
+ window._testResults[what] = "ran";
+ window.parent.scriptRan(shouldrun, what, data);
+ logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+ };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+ return function(shouldrun, what, data) {
+ window._testResults[what] = "blocked";
+ window.parent.scriptBlocked(shouldrun, what, data);
+ logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+ };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+ // setTimeout(String) test -- mutate something in the window._testResults
+ // obj, then check it.
+ {
+ var str_setTimeoutWithStringRan = 'onevalexecuted(false, "setTimeout(String)", "setTimeout with a string was enabled.");';
+ function fcn_setTimeoutWithStringCheck() {
+ if (this._testResults["setTimeout(String)"] !== "ran") {
+ onevalblocked(false, "setTimeout(String)",
+ "setTimeout with a string was blocked");
+ }
+ }
+ setTimeout(fcn_setTimeoutWithStringCheck.bind(window), 10);
+ setTimeout(str_setTimeoutWithStringRan, 10);
+ }
+
+ // setTimeout(function) test -- mutate something in the window._testResults
+ // obj, then check it.
+ {
+ function fcn_setTimeoutWithFunctionRan() {
+ onevalexecuted(true, "setTimeout(function)",
+ "setTimeout with a function was enabled.")
+ }
+ function fcn_setTimeoutWithFunctionCheck() {
+ if (this._testResults["setTimeout(function)"] !== "ran") {
+ onevalblocked(true, "setTimeout(function)",
+ "setTimeout with a function was blocked");
+ }
+ }
+ setTimeout(fcn_setTimeoutWithFunctionRan.bind(window), 10);
+ setTimeout(fcn_setTimeoutWithFunctionCheck.bind(window), 10);
+ }
+
+ // eval() test -- should throw exception as per spec
+ try {
+ eval('onevalexecuted(false, "eval(String)", "eval() was enabled.");');
+ } catch (e) {
+ onevalblocked(false, "eval(String)",
+ "eval() was blocked");
+ }
+
+ // eval(foo,bar) test -- should throw exception as per spec
+ try {
+ eval('onevalexecuted(false, "eval(String,scope)", "eval() was enabled.");',1);
+ } catch (e) {
+ onevalblocked(false, "eval(String,object)",
+ "eval() with scope was blocked");
+ }
+
+ // [foo,bar].sort(eval) test -- should throw exception as per spec
+ try {
+ ['onevalexecuted(false, "[String, obj].sort(eval)", "eval() was enabled.");',1].sort(eval);
+ } catch (e) {
+ onevalblocked(false, "[String, obj].sort(eval)",
+ "eval() with scope via sort was blocked");
+ }
+
+ // [].sort.call([foo,bar], eval) test -- should throw exception as per spec
+ try {
+ [].sort.call(['onevalexecuted(false, "[String, obj].sort(eval)", "eval() was enabled.");',1], eval);
+ } catch (e) {
+ onevalblocked(false, "[].sort.call([String, obj], eval)",
+ "eval() with scope via sort/call was blocked");
+ }
+
+ // new Function() test -- should throw exception as per spec
+ try {
+ var fcn = new Function('onevalexecuted(false, "new Function(String)", "new Function(String) was enabled.");');
+ fcn();
+ } catch (e) {
+ onevalblocked(false, "new Function(String)",
+ "new Function(String) was blocked.");
+ }
+
+ // setTimeout(eval, 0, str)
+ {
+ // error is not catchable here, instead, we're going to side-effect
+ // 'worked'.
+ var worked = false;
+
+ setTimeout(eval, 0, 'worked = true');
+ setTimeout(function(worked) {
+ if (worked) {
+ onevalexecuted(false, "setTimeout(eval, 0, str)",
+ "setTimeout(eval, 0, string) was enabled.");
+ } else {
+ onevalblocked(false, "setTimeout(eval, 0, str)",
+ "setTimeout(eval, 0, str) was blocked.");
+ }
+ }, 0, worked);
+ }
+
+}, false);
+
+
+
diff --git a/content/base/test/xcsp/file_CSP_evalscript_main_getCRMFRequest.html b/content/base/test/xcsp/file_CSP_evalscript_main_getCRMFRequest.html
new file mode 100644
index 00000000000..7b122389be5
--- /dev/null
+++ b/content/base/test/xcsp/file_CSP_evalscript_main_getCRMFRequest.html
@@ -0,0 +1,12 @@
+
+
+ CSP eval script tests
+
+
+
+
+ Foo.
+
+
+
diff --git a/content/base/test/xcsp/file_CSP_evalscript_main_getCRMFRequest.html^headers^ b/content/base/test/xcsp/file_CSP_evalscript_main_getCRMFRequest.html^headers^
new file mode 100644
index 00000000000..a9c761cfc14
--- /dev/null
+++ b/content/base/test/xcsp/file_CSP_evalscript_main_getCRMFRequest.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+X-Content-Security-Policy: default-src 'self'
diff --git a/content/base/test/xcsp/file_CSP_evalscript_main_getCRMFRequest.js b/content/base/test/xcsp/file_CSP_evalscript_main_getCRMFRequest.js
new file mode 100644
index 00000000000..90826e3bc9d
--- /dev/null
+++ b/content/base/test/xcsp/file_CSP_evalscript_main_getCRMFRequest.js
@@ -0,0 +1,48 @@
+// some javascript for the CSP eval() tests
+
+function logResult(str, passed) {
+ var elt = document.createElement('div');
+ var color = passed ? "#cfc;" : "#fcc";
+ elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+ elt.innerHTML = str;
+ document.body.appendChild(elt);
+}
+
+window._testResults = {};
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+ return function(shouldrun, what, data) {
+ window._testResults[what] = "ran";
+ window.parent.scriptRan(shouldrun, what, data);
+ logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+ };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+ return function(shouldrun, what, data) {
+ window._testResults[what] = "blocked";
+ window.parent.scriptBlocked(shouldrun, what, data);
+ logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+ };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+ // generateCRMFRequest test -- make sure we cannot eval the callback if CSP is in effect
+ try {
+ var script = 'console.log("dynamic script eval\'d in crypto.generateCRMFRequest should be disallowed")';
+ crypto.generateCRMFRequest('CN=0', 0, 0, null, script, 384, null, 'rsa-dual-use');
+ onevalexecuted(false, "crypto.generateCRMFRequest()",
+ "crypto.generateCRMFRequest() should not run!");
+ } catch (e) {
+ onevalblocked(false, "eval(script) inside crypto.generateCRMFRequest",
+ "eval was blocked during crypto.generateCRMFRequest");
+ }
+
+
+}, false);
+
+
+
diff --git a/content/base/test/xcsp/file_CSP_evalscript_no_CSP_at_all.html b/content/base/test/xcsp/file_CSP_evalscript_no_CSP_at_all.html
new file mode 100644
index 00000000000..d36aeaaf106
--- /dev/null
+++ b/content/base/test/xcsp/file_CSP_evalscript_no_CSP_at_all.html
@@ -0,0 +1,12 @@
+
+
+ CSP eval script tests: no CSP specified
+
+
+
+
+ Foo. See bug 824652
+
+
+
diff --git a/content/base/test/xcsp/file_CSP_evalscript_no_CSP_at_all.html^headers^ b/content/base/test/xcsp/file_CSP_evalscript_no_CSP_at_all.html^headers^
new file mode 100644
index 00000000000..9e23c73b7ff
--- /dev/null
+++ b/content/base/test/xcsp/file_CSP_evalscript_no_CSP_at_all.html^headers^
@@ -0,0 +1 @@
+Cache-Control: no-cache
diff --git a/content/base/test/xcsp/file_CSP_evalscript_no_CSP_at_all.js b/content/base/test/xcsp/file_CSP_evalscript_no_CSP_at_all.js
new file mode 100644
index 00000000000..520491fb8ef
--- /dev/null
+++ b/content/base/test/xcsp/file_CSP_evalscript_no_CSP_at_all.js
@@ -0,0 +1,42 @@
+// some javascript for the CSP eval() tests
+// all of these evals should succeed, as the document loading this script
+// has script-src 'self' 'unsafe-eval'
+
+function logResult(str, passed) {
+ var elt = document.createElement('div');
+ var color = passed ? "#cfc;" : "#fcc";
+ elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+ elt.innerHTML = str;
+ document.body.appendChild(elt);
+}
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+ return function(shouldrun, what, data) {
+ window.parent.scriptRan(shouldrun, what, data);
+ logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+ };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+ return function(shouldrun, what, data) {
+ window.parent.scriptBlocked(shouldrun, what, data);
+ logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+ };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+ // test that allows crypto.generateCRMFRequest eval to run when there is no CSP at all in place
+ try {
+ var script =
+ 'console.log("dynamic script passed to crypto.generateCRMFRequest should execute")';
+ crypto.generateCRMFRequest('CN=0', 0, 0, null, script, 384, null, 'rsa-dual-use');
+ onevalexecuted(true, "eval(script) inside crypto.generateCRMFRequest: no CSP at all",
+ "eval executed during crypto.generateCRMFRequest where no CSP is set at all");
+ } catch (e) {
+ onevalblocked(true, "eval(script) inside crypto.generateCRMFRequest",
+ "eval was blocked during crypto.generateCRMFRequest");
+ }
+}, false);
diff --git a/content/base/test/xcsp/file_csp_bug768029.html b/content/base/test/xcsp/file_csp_bug768029.html
new file mode 100644
index 00000000000..d7f2730e6c7
--- /dev/null
+++ b/content/base/test/xcsp/file_csp_bug768029.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ This is an app for testing
+
+
+
+
+
+
+
+
+
+
+
+ Test for CSP applied to (simulated) app.
+
+
+
diff --git a/content/base/test/xcsp/file_csp_bug768029.sjs b/content/base/test/xcsp/file_csp_bug768029.sjs
new file mode 100644
index 00000000000..9ae353055ef
--- /dev/null
+++ b/content/base/test/xcsp/file_csp_bug768029.sjs
@@ -0,0 +1,29 @@
+function handleRequest(request, response) {
+
+ var query = {};
+
+ request.queryString.split('&').forEach(function(val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ if ("type" in query) {
+ switch (query.type) {
+ case "script":
+ response.setHeader("Content-Type", "application/javascript");
+ response.write("\n\ndocument.write('script loaded\\n
');\n\n");
+ return;
+ case "style":
+ response.setHeader("Content-Type", "text/css");
+ response.write("\n\n.cspfoo { color:red; }\n\n");
+ return;
+ case "img":
+ response.setHeader("Content-Type", "image/png");
+ return;
+ }
+ }
+
+ response.setHeader("Content-Type", "text/plain");
+ response.write("ohnoes!");
+}
diff --git a/content/base/test/xcsp/file_csp_bug773891.html b/content/base/test/xcsp/file_csp_bug773891.html
new file mode 100644
index 00000000000..563e5f1699c
--- /dev/null
+++ b/content/base/test/xcsp/file_csp_bug773891.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ This is an app for csp testing
+
+
+
+
+
+
+
+
+
+
+
+ Test for CSP applied to (simulated) app.
+
+
+
diff --git a/content/base/test/xcsp/file_csp_bug773891.sjs b/content/base/test/xcsp/file_csp_bug773891.sjs
new file mode 100644
index 00000000000..9ae353055ef
--- /dev/null
+++ b/content/base/test/xcsp/file_csp_bug773891.sjs
@@ -0,0 +1,29 @@
+function handleRequest(request, response) {
+
+ var query = {};
+
+ request.queryString.split('&').forEach(function(val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ if ("type" in query) {
+ switch (query.type) {
+ case "script":
+ response.setHeader("Content-Type", "application/javascript");
+ response.write("\n\ndocument.write('script loaded\\n
');\n\n");
+ return;
+ case "style":
+ response.setHeader("Content-Type", "text/css");
+ response.write("\n\n.cspfoo { color:red; }\n\n");
+ return;
+ case "img":
+ response.setHeader("Content-Type", "image/png");
+ return;
+ }
+ }
+
+ response.setHeader("Content-Type", "text/plain");
+ response.write("ohnoes!");
+}
diff --git a/content/base/test/xcsp/file_csp_redirects_main.html b/content/base/test/xcsp/file_csp_redirects_main.html
new file mode 100644
index 00000000000..102f7469282
--- /dev/null
+++ b/content/base/test/xcsp/file_csp_redirects_main.html
@@ -0,0 +1,44 @@
+
+
+CSP redirect tests
+
+
+
+
+
+
+
diff --git a/content/base/test/xcsp/file_csp_redirects_page.sjs b/content/base/test/xcsp/file_csp_redirects_page.sjs
new file mode 100644
index 00000000000..7f5d6bad41e
--- /dev/null
+++ b/content/base/test/xcsp/file_csp_redirects_page.sjs
@@ -0,0 +1,133 @@
+// SJS file for CSP redirect mochitests
+// This file serves pages which can optionally specify a Content Security Policy
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html", false);
+
+ var resource = "/tests/content/base/test/csp/file_csp_redirects_resource.sjs";
+
+ // CSP header value
+ if (query["csp"] == 1) {
+ if (query["spec"] == 1) {
+ response.setHeader("Content-Security-Policy", "default-src 'self' ; style-src 'self' 'unsafe-inline'", false);
+ } else {
+ response.setHeader("X-Content-Security-Policy", "allow 'self'", false);
+ }
+ }
+
+ // downloadable font that redirects to another site
+ if (query["testid"] == "font-src") {
+ var resp = '' +
+ 'test
';
+ response.write(resp);
+ return;
+ }
+
+ if (query["testid"] == "font-src-spec-compliant") {
+ var resp = '' +
+ 'test
';
+ response.write(resp);
+ return;
+ }
+
+ // iframe that redirects to another site
+ if (query["testid"] == "frame-src") {
+ response.write('');
+ return;
+ }
+
+ if (query["testid"] == "frame-src-spec-compliant") {
+ response.write('');
+ return;
+ }
+
+ // image that redirects to another site
+ if (query["testid"] == "img-src") {
+ response.write('');
+ return;
+ }
+
+ if (query["testid"] == "img-src-spec-compliant") {
+ response.write('');
+ return;
+ }
+
+ // video content that redirects to another site
+ if (query["testid"] == "media-src") {
+ response.write('');
+ return;
+ }
+
+ if (query["testid"] == "media-src-spec-compliant") {
+ response.write('');
+ return;
+ }
+
+ // object content that redirects to another site
+ if (query["testid"] == "object-src") {
+ response.write('');
+ return;
+ }
+
+ if (query["testid"] == "object-src-spec-compliant") {
+ response.write('');
+ return;
+ }
+
+ // external script that redirects to another site
+ if (query["testid"] == "script-src") {
+ response.write('');
+ return;
+ }
+
+ if (query["testid"] == "script-src-spec-compliant") {
+ response.write('');
+ return;
+ }
+
+ // external stylesheet that redirects to another site
+ if (query["testid"] == "style-src") {
+ response.write('');
+ return;
+ }
+
+ if (query["testid"] == "style-src-spec-compliant") {
+ response.write('');
+ return;
+ }
+
+ // worker script resource that redirects to another site
+ if (query["testid"] == "worker") {
+ response.write('');
+ return;
+ }
+
+ if (query["testid"] == "worker-spec-compliant") {
+ response.write('');
+ return;
+ }
+
+ // script that XHR's to a resource that redirects to another site
+ if (query["testid"] == "xhr-src") {
+ response.write('');
+ return;
+ }
+
+ if (query["testid"] == "xhr-src-spec-compliant") {
+ response.write('');
+ return;
+ }
+}
diff --git a/content/base/test/xcsp/file_csp_redirects_resource.sjs b/content/base/test/xcsp/file_csp_redirects_resource.sjs
new file mode 100644
index 00000000000..60924ee09da
--- /dev/null
+++ b/content/base/test/xcsp/file_csp_redirects_resource.sjs
@@ -0,0 +1,128 @@
+// SJS file to serve resources for CSP redirect tests
+// This file mimics serving resources, e.g. fonts, images, etc., which a CSP
+// can include. The resource may redirect to a different resource, if specified.
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ var thisSite = "http://mochi.test:8888";
+ var otherSite = "http://example.com";
+ var resource = "/tests/content/base/test/csp/file_csp_redirects_resource.sjs";
+
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // redirect to a resource on this site
+ if (query["redir"] == "same") {
+ var loc = thisSite+resource+"?res="+query["res"]+"&testid="+query["id"];
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", loc, false);
+ return;
+ }
+
+ // redirect to a resource on a different site
+ else if (query["redir"] == "other") {
+ var loc = otherSite+resource+"?res="+query["res"]+"&testid="+query["id"];
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", loc, false);
+ return;
+ }
+
+ // not a redirect. serve some content.
+ // the content doesn't have to be valid, since we're only checking whether
+ // the request for the content was sent or not.
+
+ // downloadable font
+ if (query["res"] == "font") {
+ response.setHeader("Access-Control-Allow-Origin", "*", false);
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write("font data...");
+ return;
+ }
+
+ if (query["res"] == "font-spec-compliant") {
+ response.setHeader("Access-Control-Allow-Origin", "*", false);
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write("font data...");
+ return;
+ }
+
+ // iframe with arbitrary content
+ if (query["res"] == "iframe") {
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("iframe content...");
+ return;
+ }
+
+ // image
+ if (query["res"] == "image") {
+ response.setHeader("Content-Type", "image/gif", false);
+ response.write("image data...");
+ return;
+ }
+
+ // media content, e.g. Ogg video
+ if (query["res"] == "media") {
+ response.setHeader("Content-Type", "video/ogg", false);
+ response.write("video data...");
+ return;
+ }
+
+ // plugin content, e.g.