Bug 1089255 - Implement and test manifest-src CSP directive. r=bholley, r=dveditz, r=ckerschb
authorMarcos Caceres <marcos@marcosc.com>
Tue, 02 Jun 2015 15:42:19 -0400
changeset 246761 6e5683d96cbbfe9ac4efa9a894eac4c055ed40d7
parent 246760 41a8951e52d63b906c05739bd75eb18c4603f9cd
child 246762 012638ffaacc09bf5a5bedda2f8bc927fd97641f
push id28839
push userkwierso@gmail.com
push dateWed, 03 Jun 2015 01:20:15 +0000
treeherdermozilla-central@20a96e15631a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley, dveditz, ckerschb
bugs1089255, 100644
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1089255 - Implement and test manifest-src CSP directive. r=bholley, r=dveditz, r=ckerschb --- dom/base/nsContentPolicyUtils.h | 1 + dom/base/nsDataDocumentContentPolicy.cpp | 3 +- dom/base/nsIContentPolicy.idl | 2 +- dom/base/nsIContentPolicyBase.idl | 7 +- dom/base/nsISimpleContentPolicy.idl | 2 +- dom/base/test/csp/browser.ini | 4 + dom/base/test/csp/browser_test_web_manifest.js | 265 +++++++++++++++++++++ .../csp/browser_test_web_manifest_mixed_content.js | 55 +++++ dom/base/test/csp/file_CSP_web_manifest.html | 6 + dom/base/test/csp/file_CSP_web_manifest.json | 1 + .../test/csp/file_CSP_web_manifest.json^headers^ | 1 + dom/base/test/csp/file_CSP_web_manifest_https.html | 4 + dom/base/test/csp/file_CSP_web_manifest_https.json | 1 + .../csp/file_CSP_web_manifest_mixed_content.html | 9 + .../test/csp/file_CSP_web_manifest_remote.html | 8 + dom/base/test/csp/file_csp_testserver.sjs | 14 +- dom/base/test/csp/mochitest.ini | 7 + dom/base/test/moz.build | 5 +- dom/fetch/InternalRequest.cpp | 3 + dom/fetch/InternalRequest.h | 2 +- .../security/nsIContentSecurityPolicy.idl | 3 +- dom/ipc/manifestMessages.js | 25 +- dom/security/nsCSPUtils.cpp | 7 + dom/security/nsCSPUtils.h | 10 +- dom/security/nsMixedContentBlocker.cpp | 1 + dom/webidl/CSPDictionaries.webidl | 1 + extensions/permissions/nsContentBlocker.cpp | 6 +- netwerk/mime/nsMimeTypes.h | 1 + 28 files changed, 439 insertions(+), 15 deletions(-) create mode 100644 dom/base/test/csp/browser.ini create mode 100644 dom/base/test/csp/browser_test_web_manifest.js create mode 100644 dom/base/test/csp/browser_test_web_manifest_mixed_content.js create mode 100644 dom/base/test/csp/file_CSP_web_manifest.html create mode 100644 dom/base/test/csp/file_CSP_web_manifest.json create mode 100644 dom/base/test/csp/file_CSP_web_manifest.json^headers^ create mode 100644 dom/base/test/csp/file_CSP_web_manifest_https.html create mode 100644 dom/base/test/csp/file_CSP_web_manifest_https.json create mode 100644 dom/base/test/csp/file_CSP_web_manifest_mixed_content.html create mode 100644 dom/base/test/csp/file_CSP_web_manifest_remote.html
dom/base/nsContentPolicyUtils.h
dom/base/nsDataDocumentContentPolicy.cpp
dom/base/nsIContentPolicy.idl
dom/base/nsIContentPolicyBase.idl
dom/base/nsISimpleContentPolicy.idl
dom/base/test/csp/browser.ini
dom/base/test/csp/browser_test_web_manifest.js
dom/base/test/csp/browser_test_web_manifest_mixed_content.js
dom/base/test/csp/file_CSP_web_manifest.html
dom/base/test/csp/file_CSP_web_manifest.json
dom/base/test/csp/file_CSP_web_manifest.json^headers^
dom/base/test/csp/file_CSP_web_manifest_https.html
dom/base/test/csp/file_CSP_web_manifest_https.json
dom/base/test/csp/file_CSP_web_manifest_mixed_content.html
dom/base/test/csp/file_CSP_web_manifest_remote.html
dom/base/test/csp/file_csp_testserver.sjs
dom/base/test/csp/mochitest.ini
dom/base/test/moz.build
dom/fetch/InternalRequest.cpp
dom/fetch/InternalRequest.h
dom/interfaces/security/nsIContentSecurityPolicy.idl
dom/ipc/manifestMessages.js
dom/security/nsCSPUtils.cpp
dom/security/nsCSPUtils.h
dom/security/nsMixedContentBlocker.cpp
dom/webidl/CSPDictionaries.webidl
extensions/permissions/nsContentBlocker.cpp
netwerk/mime/nsMimeTypes.h
--- a/dom/base/nsContentPolicyUtils.h
+++ b/dom/base/nsContentPolicyUtils.h
@@ -108,16 +108,17 @@ NS_CP_ContentTypeName(uint32_t contentTy
     CASE_RETURN( TYPE_FONT              );
     CASE_RETURN( TYPE_MEDIA             );
     CASE_RETURN( TYPE_WEBSOCKET         );
     CASE_RETURN( TYPE_CSP_REPORT        );
     CASE_RETURN( TYPE_XSLT              );
     CASE_RETURN( TYPE_BEACON            );
     CASE_RETURN( TYPE_FETCH             );
     CASE_RETURN( TYPE_IMAGESET          );
+    CASE_RETURN( TYPE_WEB_MANIFEST      );
    default:
     return "<Unknown Type>";
   }
 }
 
 #undef CASE_RETURN
 
 /* Passes on parameters from its "caller"'s context. */
--- a/dom/base/nsDataDocumentContentPolicy.cpp
+++ b/dom/base/nsDataDocumentContentPolicy.cpp
@@ -118,17 +118,18 @@ nsDataDocumentContentPolicy::ShouldLoad(
   }
 
   // For resource documents, blacklist some load types
   if (aContentType == nsIContentPolicy::TYPE_OBJECT ||
       aContentType == nsIContentPolicy::TYPE_DOCUMENT ||
       aContentType == nsIContentPolicy::TYPE_SUBDOCUMENT ||
       aContentType == nsIContentPolicy::TYPE_SCRIPT ||
       aContentType == nsIContentPolicy::TYPE_XSLT ||
-      aContentType == nsIContentPolicy::TYPE_FETCH) {
+      aContentType == nsIContentPolicy::TYPE_FETCH ||
+      aContentType == nsIContentPolicy::TYPE_WEB_MANIFEST) {
     *aDecision = nsIContentPolicy::REJECT_TYPE;
   }
 
   // If you add more restrictions here, make sure to check that
   // CHECK_PRINCIPAL_AND_DATA in nsContentPolicyUtils is still valid.
   // nsContentPolicyUtils may not pass all the parameters to ShouldLoad
 
   return NS_OK;
--- a/dom/base/nsIContentPolicy.idl
+++ b/dom/base/nsIContentPolicy.idl
@@ -15,17 +15,17 @@ interface nsIPrincipal;
  * Interface for content policy mechanism.  Implementations of this
  * interface can be used to control loading of various types of out-of-line
  * content, or processing of certain types of in-line content.
  *
  * WARNING: do not block the caller from shouldLoad or shouldProcess (e.g.,
  * by launching a dialog to prompt the user for something).
  */
 
-[scriptable,uuid(447a2300-ab3c-11e4-bcd8-0800200c9a66)]
+[scriptable,uuid(cb978019-0c5b-4067-abb6-c914461208c1)]
 interface nsIContentPolicy : nsIContentPolicyBase
 {
   /**
    * Should the resource at this location be loaded?
    * ShouldLoad will be called before loading the resource at aContentLocation
    * to determine whether to start the load at all.
    *
    * @param aContentType      the type of content being tested. This will be one
--- a/dom/base/nsIContentPolicyBase.idl
+++ b/dom/base/nsIContentPolicyBase.idl
@@ -19,17 +19,17 @@ typedef unsigned long nsContentPolicyTyp
  * Interface for content policy mechanism.  Implementations of this
  * interface can be used to control loading of various types of out-of-line
  * content, or processing of certain types of in-line content.
  *
  * WARNING: do not block the caller from shouldLoad or shouldProcess (e.g.,
  * by launching a dialog to prompt the user for something).
  */
 
-[scriptable,uuid(21bb54b0-ab3c-11e4-bcd8-0800200c9a66)]
+[scriptable,uuid(4f2655e8-6365-4583-8510-732bff2186c5)]
 interface nsIContentPolicyBase : nsISupports
 {
   /**
    * Indicates a unset or bogus policy type.
    */
   const nsContentPolicyType TYPE_INVALID = 0;
 
   /**
@@ -167,16 +167,21 @@ interface nsIContentPolicyBase : nsISupp
    */
   const nsContentPolicyType TYPE_FETCH = 20;
 
   /**
    * Indicates a <img srcset> or <picture> request.
    */
   const nsContentPolicyType TYPE_IMAGESET = 21;
 
+  /**
+   * Indicates a web manifest.
+   */
+  const nsContentPolicyType TYPE_WEB_MANIFEST = 22;
+
   /* When adding new content types, please update nsContentBlocker,
    * NS_CP_ContentTypeName, nsCSPContext, all nsIContentPolicy
    * implementations, and other things that are not listed here that are
    * related to nsIContentPolicy. */
 
   //////////////////////////////////////////////////////////////////////
 
   /**
--- a/dom/base/nsISimpleContentPolicy.idl
+++ b/dom/base/nsISimpleContentPolicy.idl
@@ -23,17 +23,17 @@ interface nsIDOMElement;
  * to block loads without using cross-process wrappers (CPOWs). Add-ons should
  * prefer this interface to nsIContentPolicy because it should be faster in
  * e10s. In the future, it may also be run asynchronously.
  *
  * WARNING: do not block the caller from shouldLoad or shouldProcess (e.g.,
  * by launching a dialog to prompt the user for something).
  */
 
-[scriptable,uuid(83d93c70-ab46-11e4-bcd8-0800200c9a66)]
+[scriptable,uuid(704b4b8e-2287-498a-9c0a-d1bde547a2d4)]
 interface nsISimpleContentPolicy : nsIContentPolicyBase
 {
   /**
    * Should the resource at this location be loaded?
    * ShouldLoad will be called before loading the resource at aContentLocation
    * to determine whether to start the load at all.
    *
    * @param aContentType      the type of content being tested. This will be one
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/browser.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+skip-if = e10s # Bug 1170385 - csp-on-violate-policy message not sent in browser tests with e10s
+[browser_test_web_manifest.js]
+[browser_test_web_manifest_mixed_content.js]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/browser_test_web_manifest.js
@@ -0,0 +1,265 @@
+/*
+ * Description of the tests:
+ *   These tests check for conformance to the CSP spec as they relate to Web Manifests.
+ *
+ *   In particular, the tests check that default-src and manifest-src directives are
+ *   are respected by the ManifestObtainer.
+ */
+/*globals Components*/
+'use strict';
+requestLongerTimeout(10); // e10s tests take time.
+const {
+  ManifestObtainer
+} = Components.utils.import('resource://gre/modules/WebManifest.jsm', {});
+const path = '/tests/dom/base/test/csp/';
+const testFile = `file=${path}file_CSP_web_manifest.html`;
+const remoteFile = `file=${path}file_CSP_web_manifest_remote.html`;
+const httpsManifest = `file=${path}file_CSP_web_manifest_https.html`;
+const mixedContent = `file=${path}file_CSP_web_manifest_mixed_content.html`;
+const server = 'file_csp_testserver.sjs';
+const defaultURL = `http://example.org${path}${server}`;
+const remoteURL = `http://mochi.test:8888`;
+const secureURL = `https://example.com${path}${server}`;
+const tests = [
+  // CSP block everything, so trying to load a manifest
+  // will result in a policy violation.
+  {
+    expected: `default-src 'none' blocks fetching manifest.`,
+    get tabURL() {
+      let queryParts = [
+        `csp=default-src 'none'`,
+        testFile
+      ];
+      return `${defaultURL}?${queryParts.join('&')}`;
+    },
+    run(topic) {
+      is(topic, 'csp-on-violate-policy', this.expected);
+    }
+  },
+  // CSP allows fetching only from mochi.test:8888,
+  // so trying to load a manifest from same origin
+  // triggers a CSP violation.
+  {
+    expected: `default-src mochi.test:8888 blocks manifest fetching.`,
+    get tabURL() {
+      let queryParts = [
+        `csp=default-src mochi.test:8888`,
+        testFile
+      ];
+      return `${defaultURL}?${queryParts.join('&')}`;
+    },
+    run(topic) {
+      is(topic, 'csp-on-violate-policy', this.expected);
+    }
+  },
+  // CSP restricts fetching to 'self', so allowing the manifest
+  // to load. The name of the manifest is then checked.
+  {
+    expected: `CSP default-src 'self' allows fetch of manifest.`,
+    get tabURL() {
+      let queryParts = [
+        `csp=default-src 'self'`,
+        testFile
+      ];
+      return `${defaultURL}?${queryParts.join('&')}`;
+    },
+    run(manifest) {
+      is(manifest.name, 'loaded', this.expected);
+    }
+  },
+  // CSP only allows fetching from mochi.test:8888 and remoteFile
+  // requests a manifest from that origin, so manifest should load.
+  {
+    expected: 'CSP default-src mochi.test:8888 allows fetching manifest.',
+    get tabURL() {
+      let queryParts = [
+        `csp=default-src http://mochi.test:8888`,
+        remoteFile
+      ];
+      return `${defaultURL}?${queryParts.join('&')}`;
+    },
+    run(manifest) {
+      is(manifest.name, 'loaded', this.expected);
+    }
+  },
+  // default-src blocks everything, so any attempt to
+  // fetch a manifest from another origin will trigger a
+  // policy violation.
+  {
+    expected: `default-src 'none' blocks mochi.test:8888`,
+    get tabURL() {
+      let queryParts = [
+        `csp=default-src 'none'`,
+        remoteFile
+      ];
+      return `${defaultURL}?${queryParts.join('&')}`;
+    },
+    run(topic) {
+      is(topic, 'csp-on-violate-policy', this.expected);
+    }
+  },
+  // CSP allows fetching from self, so manifest should load.
+  {
+    expected: `CSP manifest-src allows self`,
+    get tabURL() {
+      let queryParts = [
+        `manifest-src 'self'`,
+        testFile
+      ];
+      return `${defaultURL}?${queryParts.join('&')}`;
+    },
+    run(manifest) {
+      is(manifest.name, 'loaded', this.expected);
+    }
+  },
+  // CSP allows fetching from example.org, so manifest should load.
+  {
+    expected: `CSP manifest-src allows http://example.org`,
+    get tabURL() {
+      let queryParts = [
+        `manifest-src http://example.org`,
+        testFile
+      ];
+      return `${defaultURL}?${queryParts.join('&')}`;
+    },
+    run(manifest) {
+      is(manifest.name, 'loaded', this.expected);
+    }
+  },
+  // Check interaction with default-src and another origin,
+  // CSP allows fetching from example.org, so manifest should load.
+  {
+    expected: `CSP manifest-src overrides default-src of elsewhere.com`,
+    get tabURL() {
+      let queryParts = [
+        `default-src: http://elsewhere.com; manifest-src http://example.org`,
+        testFile
+      ];
+      return `${defaultURL}?${queryParts.join('&')}`;
+    },
+    run(manifest) {
+      is(manifest.name, 'loaded', this.expected);
+    }
+  },
+  // Check interaction with default-src none,
+  // CSP allows fetching manifest from example.org, so manifest should load.
+  {
+    expected: `CSP manifest-src overrides default-src`,
+    get tabURL() {
+      let queryParts = [
+        `default-src: 'none'; manifest-src 'self'`,
+        testFile
+      ];
+      return `${defaultURL}?${queryParts.join('&')}`;
+    },
+    run(manifest) {
+      is(manifest.name, 'loaded', this.expected);
+    }
+  },
+  // CSP allows fetching from mochi.test:8888, which has a
+  // CORS header set to '*'. So the manifest should load.
+  {
+    expected: `CSP manifest-src allows mochi.test:8888`,
+    get tabURL() {
+      let queryParts = [
+        `csp=default-src *; manifest-src http://mochi.test:8888`,
+        remoteFile
+      ];
+      return `${defaultURL}?${queryParts.join('&')}`;
+    },
+    run(manifest) {
+      is(manifest.name, 'loaded', this.expected);
+    }
+  },
+  // CSP restricts fetching to mochi.test:8888, but the test
+  // file is at example.org. Hence, a policy violation is
+  // triggered.
+  {
+    expected: `CSP blocks manifest fetching from example.org.`,
+    get tabURL() {
+      let queryParts = [
+        `csp=manifest-src mochi.test:8888`,
+        testFile
+      ];
+      return `${defaultURL}?${queryParts.join('&')}`;
+    },
+    run(topic) {
+      is(topic, 'csp-on-violate-policy', this.expected);
+    }
+  },
+  // CSP is set to only allow manifest to be loaded from same origin,
+  // but the remote file attempts to load from a different origin. Thus
+  // this causes a CSP violation.
+  {
+    expected: `CSP manifest-src 'self' blocks cross-origin fetch.`,
+    get tabURL() {
+      let queryParts = [
+        `csp=manifest-src 'self'`,
+        remoteFile
+      ];
+      return `${defaultURL}?${queryParts.join('&')}`;
+    },
+    run(topic) {
+      is(topic, 'csp-on-violate-policy', this.expected);
+    }
+  }
+];
+//jscs:disable
+add_task(function* () {
+  //jscs:enable
+  for (let test of tests) {
+    let tabOptions = {
+      gBrowser: gBrowser,
+      url: test.tabURL,
+    };
+    yield BrowserTestUtils.withNewTab(
+      tabOptions,
+      browser => testObtainingManifest(browser, test)
+    );
+  }
+
+  function* testObtainingManifest(aBrowser, aTest) {
+    const observer = (/blocks/.test(aTest.expected)) ? new NetworkObserver(aTest) : null;
+    const obtainer = new ManifestObtainer();
+    let manifest;
+    // Expect an exception (from promise rejection) if there a content policy
+    // that is violated.
+    try {
+      manifest = yield obtainer.obtainManifest(aBrowser);
+    } catch (e) {
+      const msg = `Expected promise rejection obtaining.`;
+      ok(/blocked the loading of a resource/.test(e.message), msg);
+      if (observer) {
+        yield observer.finished;
+      }
+      return;
+    }
+    // otherwise, we test manifest's content.
+    if (manifest) {
+      aTest.run(manifest);
+    }
+  }
+});
+
+// Helper object used to observe policy violations. It waits 10 seconds
+// for a response, and then times out causing its associated test to fail.
+function NetworkObserver(test) {
+  let finishedTest;
+  let success = false;
+  this.finished = new Promise((resolver) => {
+    finishedTest = resolver;
+  })
+  this.observe = function observer(subject, topic) {
+    SpecialPowers.removeObserver(this, 'csp-on-violate-policy');
+    test.run(topic);
+    finishedTest();
+    success = true;
+  };
+  SpecialPowers.addObserver(this, 'csp-on-violate-policy', false);
+  setTimeout(() => {
+    if (!success) {
+      test.run('This test timed out.');
+      finishedTest();
+    }
+  }, 1000);
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/browser_test_web_manifest_mixed_content.js
@@ -0,0 +1,55 @@
+/*
+ * Description of the test:
+ *   Check that mixed content blocker works prevents fetches of
+ *   mixed content manifests.
+ */
+'use strict';
+const {
+  ManifestObtainer
+} = Components.utils.import('resource://gre/modules/WebManifest.jsm', {});
+const path = '/tests/dom/base/test/csp/';
+const mixedContent = `file=${path}file_CSP_web_manifest_mixed_content.html`;
+const server = 'file_csp_testserver.sjs';
+const secureURL = `https://example.com${path}${server}`;
+const tests = [
+  // Trying to load mixed content in file_CSP_web_manifest_mixed_content.html
+  // needs to result in an error.
+  {
+    expected: `Mixed Content Blocker prevents fetching manifest.`,
+    get tabURL() {
+      let queryParts = [
+        mixedContent
+      ];
+      return `${secureURL}?${queryParts.join('&')}`;
+    },
+    run(error) {
+      // Check reason for error.
+      const check = /blocked the loading of a resource/.test(error.message);
+      ok(check, this.expected);
+    }
+  }
+];
+
+//jscs:disable
+add_task(function*() {
+  //jscs:enable
+  for (let test of tests) {
+    let tabOptions = {
+      gBrowser: gBrowser,
+      url: test.tabURL,
+    };
+    yield BrowserTestUtils.withNewTab(
+      tabOptions,
+      browser => testObtainingManifest(browser, test)
+    );
+  }
+
+  function* testObtainingManifest(aBrowser, aTest) {
+    const obtainer = new ManifestObtainer();
+    try {
+      yield obtainer.obtainManifest(aBrowser);
+    } catch (e) {
+      aTest.run(e)
+    }
+  }
+});
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/file_CSP_web_manifest.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<head>
+<link rel="manifest" href="file_CSP_web_manifest.json">
+</head>
+<h1>Support Page for Web Manifest Tests</h1>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/file_CSP_web_manifest.json
@@ -0,0 +1,1 @@
+{"name": "loaded"}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/file_CSP_web_manifest.json^headers^
@@ -0,0 +1,1 @@
+Access-Control-Allow-Origin: http://example.org
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/file_CSP_web_manifest_https.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<meta charset=utf-8>
+<link rel="manifest" href="https://example.com:443/tests/dom/base/test/csp/file_CSP_web_manifest_https.json">
+<h1>Support Page for Web Manifest Tests</h1>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/file_CSP_web_manifest_https.json
@@ -0,0 +1,1 @@
+{"name": "loaded"}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/file_CSP_web_manifest_mixed_content.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<meta charset=utf-8>
+<head>
+<link
+  rel="manifest"
+  href="http://example.org/tests/dom/base/test/csp/file_csp_testserver.sjs?file=/test/dom/base/test/csp/file_CSP_web_manifest.json&amp;cors=*">
+</head>
+<h1>Support Page for Web Manifest Tests</h1>
+<p>Used to try to load a resource over an insecure connection to trigger mixed content blocking.</p>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/file_CSP_web_manifest_remote.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<link rel="manifest"
+      crossorigin
+	  href="//mochi.test:8888/tests/dom/base/test/csp/file_csp_testserver.sjs?file=/tests/dom/base/test/csp/file_CSP_web_manifest.json&amp;cors=*">
+
+<h1>Support Page for Web Manifest Tests</h1>
+<p>Loads a manifest from mochi.test:8888 with CORS set to "*".</p>
\ No newline at end of file
--- a/dom/base/test/csp/file_csp_testserver.sjs
+++ b/dom/base/test/csp/file_csp_testserver.sjs
@@ -17,29 +17,37 @@ function loadHTMLFromFile(path) {
   var testHTMLFileStream =
     Components.classes["@mozilla.org/network/file-input-stream;1"].
     createInstance(Components.interfaces.nsIFileInputStream);
   testHTMLFileStream.init(testHTMLFile, -1, 0, 0);
   var testHTML = NetUtil.readInputStreamToString(testHTMLFileStream, testHTMLFileStream.available());
   return testHTML;
 }
 
+
 function handleRequest(request, response)
 {
   var query = {};
   request.queryString.split('&').forEach(function (val) {
     var [name, value] = val.split('=');
     query[name] = unescape(value);
   });
 
-  var csp = unescape(query['csp']);
+  var csp = (query['csp']) ? unescape(query['csp']) : "";
   var file = unescape(query['file']);
+  var cors = unescape(query['cors']);
 
   // avoid confusing cache behaviors
   response.setHeader("Cache-Control", "no-cache", false);
 
   // Deliver the CSP policy encoded in the URI
-  response.setHeader("Content-Security-Policy", csp, false);
-
+  if(csp){
+    response.setHeader("Content-Security-Policy", csp, false);
+  }
+  // Deliver the CORS header in the URI
+  if(cors){
+    response.setHeader("Access-Control-Allow-Origin", cors, false);
+  }
   // Send HTML to test allowed/blocked behaviors
   response.setHeader("Content-Type", "text/html", false);
+
   response.write(loadHTMLFromFile(file));
 }
--- a/dom/base/test/csp/mochitest.ini
+++ b/dom/base/test/csp/mochitest.ini
@@ -38,16 +38,23 @@ support-files =
   file_CSP_inlinestyle_main.html
   file_CSP_inlinestyle_main.html^headers^
   file_CSP_inlinestyle_main_allowed.html
   file_CSP_inlinestyle_main_allowed.html^headers^
   file_csp_invalid_source_expression.html
   file_CSP_main.html
   file_CSP_main.html^headers^
   file_CSP_main.js
+  file_CSP_web_manifest.html
+  file_CSP_web_manifest_remote.html
+  file_CSP_web_manifest_https.html
+  file_CSP_web_manifest.json
+  file_CSP_web_manifest.json^headers^
+  file_CSP_web_manifest_https.json
+  file_CSP_web_manifest_mixed_content.html
   file_bug836922_npolicies.html
   file_bug836922_npolicies.html^headers^
   file_bug836922_npolicies_ro_violation.sjs
   file_bug836922_npolicies_violation.sjs
   file_bug886164.html
   file_bug886164.html^headers^
   file_bug886164_2.html
   file_bug886164_2.html^headers^
--- a/dom/base/test/moz.build
+++ b/dom/base/test/moz.build
@@ -32,9 +32,12 @@ if CONFIG['MOZ_CHILD_PERMISSIONS']:
     ]
 
 MOCHITEST_CHROME_MANIFESTS += [
     'chrome.ini',
     'chrome/chrome.ini',
     'csp/chrome.ini',
 ]
 
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
+BROWSER_CHROME_MANIFESTS += [
+    'browser.ini',
+    'csp/browser.ini',
+]
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -163,16 +163,19 @@ InternalRequest::SetContentPolicyType(ns
     mContext = RequestContext::Beacon;
     break;
   case nsIContentPolicy::TYPE_FETCH:
     mContext = RequestContext::Fetch;
     break;
   case nsIContentPolicy::TYPE_IMAGESET:
     mContext = RequestContext::Imageset;
     break;
+  case nsIContentPolicy::TYPE_WEB_MANIFEST:
+    mContext = RequestContext::Manifest;
+    break;
   default:
     MOZ_ASSERT(false, "Unhandled nsContentPolicyType value");
     mContext = RequestContext::Internal;
     break;
   }
 }
 
 } // namespace dom
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -44,17 +44,17 @@ namespace dom {
  * frame             | TYPE_SUBDOCUMENT
  * hyperlink         |
  * iframe            | TYPE_SUBDOCUMENT
  * image             | TYPE_IMAGE
  * imageset          | TYPE_IMAGESET
  * import            | Not supported by Gecko
  * internal          | TYPE_DOCUMENT, TYPE_XBL, TYPE_OTHER
  * location          |
- * manifest          |
+ * manifest          | TYPE_WEB_MANIFEST
  * object            | TYPE_OBJECT
  * ping              | TYPE_PING
  * plugin            | TYPE_OBJECT_SUBREQUEST
  * prefetch          |
  * script            | TYPE_SCRIPT
  * serviceworker     |
  * sharedworker      |
  * subresource       | Not supported by Gecko
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl
+++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl
@@ -15,17 +15,17 @@ interface nsIURI;
  * nsIContentSecurityPolicy
  * Describes an XPCOM component used to model and enforce CSPs.  Instances of
  * this class may have multiple policies within them, but there should only be
  * one of these per document/principal.
  */
 
 typedef unsigned short CSPDirective;
 
-[scriptable, uuid(459fe61a-203e-4460-9ced-352a9bd3aa71)]
+[scriptable, uuid(059b94e3-45c2-4794-ac2a-5b66a66b5967)]
 interface nsIContentSecurityPolicy : nsISerializable
 {
   /**
    * Directives supported by Content Security Policy.  These are enums for
    * the CSPDirective type.
    * The NO_DIRECTIVE entry is  used for checking default permissions and
    * returning failure when asking CSP which directive to check.
    *
@@ -43,16 +43,17 @@ interface nsIContentSecurityPolicy : nsI
   const unsigned short FONT_SRC_DIRECTIVE         = 8;
   const unsigned short CONNECT_SRC_DIRECTIVE      = 9;
   const unsigned short REPORT_URI_DIRECTIVE       = 10;
   const unsigned short FRAME_ANCESTORS_DIRECTIVE  = 11;
   const unsigned short REFLECTED_XSS_DIRECTIVE    = 12;
   const unsigned short BASE_URI_DIRECTIVE         = 13;
   const unsigned short FORM_ACTION_DIRECTIVE      = 14;
   const unsigned short REFERRER_DIRECTIVE         = 15;
+  const unsigned short WEB_MANIFEST_SRC_DIRECTIVE = 16;
 
   /**
    * Accessor method for a read-only string version of the policy at a given
    * index.
    */
   AString getPolicy(in unsigned long index);
 
   /**
--- a/dom/ipc/manifestMessages.js
+++ b/dom/ipc/manifestMessages.js
@@ -9,17 +9,19 @@
  * a <link rel=manifest> element. Then fetches
  * and processes the linked manifest.
  *
  * BUG: https://bugzilla.mozilla.org/show_bug.cgi?id=1083410
  */
 /*globals content, sendAsyncMessage, addMessageListener, Components*/
 'use strict';
 const {
-  utils: Cu
+  utils: Cu,
+  classes: Cc,
+  interfaces: Ci
 } = Components;
 const {
   ManifestProcessor
 } = Cu.import('resource://gre/modules/WebManifest.jsm', {});
 const {
   Task: {
     spawn, async
   }
@@ -29,16 +31,17 @@ addMessageListener('DOM:ManifestObtainer
   const response = {
     msgId: aMsg.data.msgId,
     success: true,
     result: undefined
   };
   try {
     response.result = yield fetchManifest();
   } catch (err) {
+    response.success = false;
     response.result = cloneError(err);
   }
   sendAsyncMessage('DOM:ManifestObtainer:Obtain', response);
 }));
 
 function cloneError(aError) {
   const clone = {
     'fileName': String(aError.fileName),
@@ -59,30 +62,50 @@ function fetchManifest() {
     }
     const elem = content.document.querySelector('link[rel~="manifest"]');
     if (!elem || !elem.getAttribute('href')) {
       let msg = 'No manifest to fetch.';
       throw new Error(msg);
     }
     // Throws on malformed URLs
     const manifestURL = new content.URL(elem.href, elem.baseURI);
+    if (!canLoadManifest(elem)) {
+      let msg = `Content Security Policy: The page's settings blocked the `;
+      msg += `loading of a resource at ${elem.href}`;
+      throw new Error(msg);
+    }
     const reqInit = {
       mode: 'cors'
     };
     if (elem.crossOrigin === 'use-credentials') {
       reqInit.credentials = 'include';
     }
     const req = new content.Request(manifestURL, reqInit);
     req.setContext('manifest');
     const response = yield content.fetch(req);
     const manifest = yield processResponse(response, content);
     return manifest;
   });
 }
 
+function canLoadManifest(aElem) {
+  const contentPolicy = Cc['@mozilla.org/layout/content-policy;1']
+    .getService(Ci.nsIContentPolicy);
+  const mimeType = aElem.type || 'application/manifest+json';
+  const elemURI = BrowserUtils.makeURI(
+    aElem.href, aElem.ownerDocument.characterSet
+  );
+  const shouldLoad = contentPolicy.shouldLoad(
+    Ci.nsIContentPolicy.TYPE_WEB_MANIFEST, elemURI,
+    aElem.ownerDocument.documentURIObject,
+    aElem, mimeType, null
+  );
+  return shouldLoad === Ci.nsIContentPolicy.ACCEPT;
+}
+
 function processResponse(aResp, aContentWindow) {
   return spawn(function* () {
     const badStatus = aResp.status < 200 || aResp.status >= 300;
     if (aResp.type === 'error' || badStatus) {
       let msg =
         `Fetch error: ${aResp.status} - ${aResp.statusText} at ${aResp.url}`;
       throw new Error(msg);
     }
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -144,16 +144,19 @@ CSP_ContentTypeToDirective(nsContentPoli
       return nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE;
 
     case nsIContentPolicy::TYPE_FONT:
       return nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE;
 
     case nsIContentPolicy::TYPE_MEDIA:
       return nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE;
 
+    case nsIContentPolicy::TYPE_WEB_MANIFEST:
+      return nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE;
+
     case nsIContentPolicy::TYPE_SUBDOCUMENT:
       return nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE;
 
     case nsIContentPolicy::TYPE_WEBSOCKET:
     case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
     case nsIContentPolicy::TYPE_BEACON:
     case nsIContentPolicy::TYPE_FETCH:
       return nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE;
@@ -831,16 +834,20 @@ nsCSPDirective::toDomCSPStruct(mozilla::
       outCSP.mReport_uri.Value() = srcs;
       return;
 
     case nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE:
       outCSP.mFrame_ancestors.Construct();
       outCSP.mFrame_ancestors.Value() = srcs;
       return;
 
+    case nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE:
+      outCSP.mManifest_src.Construct();
+      outCSP.mManifest_src.Value() = srcs;
+      return;
     // not supporting REFLECTED_XSS_DIRECTIVE
 
     case nsIContentSecurityPolicy::BASE_URI_DIRECTIVE:
       outCSP.mBase_uri.Construct();
       outCSP.mBase_uri.Value() = srcs;
       return;
 
     case nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE:
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -59,35 +59,39 @@ void CSP_LogMessage(const nsAString& aMe
 #define EVAL_VIOLATION_OBSERVER_TOPIC           "violated base restriction: Code will not be created from strings"
 #define SCRIPT_NONCE_VIOLATION_OBSERVER_TOPIC   "Inline Script had invalid nonce"
 #define STYLE_NONCE_VIOLATION_OBSERVER_TOPIC    "Inline Style had invalid nonce"
 #define SCRIPT_HASH_VIOLATION_OBSERVER_TOPIC    "Inline Script had invalid hash"
 #define STYLE_HASH_VIOLATION_OBSERVER_TOPIC     "Inline Style had invalid hash"
 
 // these strings map to the CSPDirectives in nsIContentSecurityPolicy
 // NOTE: When implementing a new directive, you will need to add it here but also
-// add a corresponding entry to the constants in nsIContentSecurityPolicy.idl and
-// also create an entry for the new directive in nsCSPDirective::toDomCSPStruct().
+// add a corresponding entry to the constants in nsIContentSecurityPolicy.idl
+// and also create an entry for the new directive in
+// nsCSPDirective::toDomCSPStruct() and add it to CSPDictionaries.webidl.
+// Order of elements below important! Make sure it matches the order as in
+// nsIContentSecurityPolicy.idl
 static const char* CSPStrDirectives[] = {
   "-error-",    // NO_DIRECTIVE
   "default-src",     // DEFAULT_SRC_DIRECTIVE
   "script-src",      // SCRIPT_SRC_DIRECTIVE
   "object-src",      // OBJECT_SRC_DIRECTIVE
   "style-src",       // STYLE_SRC_DIRECTIVE
   "img-src",         // IMG_SRC_DIRECTIVE
   "media-src",       // MEDIA_SRC_DIRECTIVE
   "frame-src",       // FRAME_SRC_DIRECTIVE
   "font-src",        // FONT_SRC_DIRECTIVE
   "connect-src",     // CONNECT_SRC_DIRECTIVE
   "report-uri",      // REPORT_URI_DIRECTIVE
   "frame-ancestors", // FRAME_ANCESTORS_DIRECTIVE
   "reflected-xss",   // REFLECTED_XSS_DIRECTIVE
   "base-uri",        // BASE_URI_DIRECTIVE
   "form-action",     // FORM_ACTION_DIRECTIVE
-  "referrer"         // REFERRER_DIRECTIVE
+  "referrer",        // REFERRER_DIRECTIVE
+  "manifest-src"     // MANIFEST_SRC_DIRECTIVE
 };
 
 inline const char* CSP_CSPDirectiveToString(CSPDirective aDir)
 {
   return CSPStrDirectives[static_cast<uint32_t>(aDir)];
 }
 
 inline CSPDirective CSP_StringToCSPDirective(const nsAString& aDir)
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -434,16 +434,17 @@ nsMixedContentBlocker::ShouldLoad(bool a
     case TYPE_CSP_REPORT:
     case TYPE_DTD:
     case TYPE_FETCH:
     case TYPE_FONT:
     case TYPE_OBJECT:
     case TYPE_SCRIPT:
     case TYPE_STYLESHEET:
     case TYPE_SUBDOCUMENT:
+    case TYPE_WEB_MANIFEST:
     case TYPE_XBL:
     case TYPE_XMLHTTPREQUEST:
     case TYPE_XSLT:
     case TYPE_OTHER:
       break;
 
 
     // This content policy works as a whitelist.
--- a/dom/webidl/CSPDictionaries.webidl
+++ b/dom/webidl/CSPDictionaries.webidl
@@ -19,13 +19,14 @@ dictionary CSP {
   sequence<DOMString> font-src;
   sequence<DOMString> connect-src;
   sequence<DOMString> report-uri;
   sequence<DOMString> frame-ancestors;
   // sequence<DOMString> reflected-xss; // not suppored in Firefox
   sequence<DOMString> base-uri;
   sequence<DOMString> form-action;
   sequence<DOMString> referrer;
+  sequence<DOMString> manifest-src;
 };
 
 dictionary CSPPolicies {
   sequence<CSP> csp-policies;
 };
--- a/extensions/permissions/nsContentBlocker.cpp
+++ b/extensions/permissions/nsContentBlocker.cpp
@@ -34,17 +34,21 @@ static const char *kTypeString[] = {"oth
                                     "ping",
                                     "xmlhttprequest",
                                     "objectsubrequest",
                                     "dtd",
                                     "font",
                                     "media",
                                     "websocket",
                                     "csp_report",
-                                    "xslt"};
+                                    "xslt",
+                                    "beacon",
+                                    "fetch",
+                                    "imageset",
+                                    "manifest"};
 
 #define NUMBER_OF_TYPES MOZ_ARRAY_LENGTH(kTypeString)
 uint8_t nsContentBlocker::mBehaviorPref[NUMBER_OF_TYPES];
 
 NS_IMPL_ISUPPORTS(nsContentBlocker, 
                   nsIContentPolicy,
                   nsIObserver,
                   nsISupportsWeakReference)
--- a/netwerk/mime/nsMimeTypes.h
+++ b/netwerk/mime/nsMimeTypes.h
@@ -59,16 +59,17 @@
 #define APPLICATION_PKCS7_MIME              "application/pkcs7-mime"
 #define APPLICATION_XPKCS7_SIGNATURE        "application/x-pkcs7-signature"
 #define APPLICATION_PKCS7_SIGNATURE         "application/pkcs7-signature"
 #define APPLICATION_WWW_FORM_URLENCODED     "application/x-www-form-urlencoded"
 #define APPLICATION_OLEOBJECT               "application/oleobject"
 #define APPLICATION_OLEOBJECT2              "application/x-oleobject"
 #define APPLICATION_JAVAARCHIVE             "application/java-archive"
 #define APPLICATION_MARIMBA                 "application/marimba"
+#define APPLICATION_WEB_MANIFEST            "application/manifest+json"
 #define APPLICATION_XMARIMBA                "application/x-marimba"
 #define APPLICATION_XPINSTALL               "application/x-xpinstall"
 #define APPLICATION_XML                     "application/xml"
 #define APPLICATION_XHTML_XML               "application/xhtml+xml"
 #define APPLICATION_XSLT_XML                "application/xslt+xml"
 #define APPLICATION_MATHML_XML              "application/mathml+xml"
 #define APPLICATION_RDF_XML                 "application/rdf+xml"