Backout Bug 768029 because that breaks Gaia (see https://github.com/mozilla-b2g/gaia/issues/5177)
authorFabrice Desré <fabrice@mozilla.com>
Tue, 25 Sep 2012 15:32:20 -0700
changeset 108185 f4e6abb6160f644546f14533640e81d3c54fc24d
parent 108184 4489b74f83994c5d8d8bea9e2b71594cc7193b8a
child 108186 7de3b05cd7d81e1a5205613dd13d226e4f4f4f81
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
bugs768029
milestone18.0a1
Backout Bug 768029 because that breaks Gaia (see https://github.com/mozilla-b2g/gaia/issues/5177)
b2g/app/b2g.js
build/automation.py.in
caps/idl/nsIPrincipal.idl
caps/include/nsPrincipal.h
caps/src/nsNullPrincipal.cpp
caps/src/nsPrincipal.cpp
caps/src/nsSystemPrincipal.cpp
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/test/Makefile.in
content/base/test/chrome/Makefile.in
content/base/test/chrome/test_csp_bug768029.html
content/base/test/file_csp_bug768029.html
content/base/test/file_csp_bug768029.sjs
dom/locales/en-US/chrome/dom/dom.properties
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -349,20 +349,16 @@ pref("browser.dom.window.dump.enabled", 
 
 
 
 // Temporarily relax file:// origin checks so that we can use <img>s
 // from other dirs as webgl textures and more.  Remove me when we have
 // installable apps or wifi support.
 pref("security.fileuri.strict_origin_policy", false);
 
-// Default Content Security Policy to apply to privileged and certified apps
-pref("security.apps.privileged.CSP.default", "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'");
-pref("security.apps.certified.CSP.default", "default-src *; script-src 'self'; object-src 'none'; style-src 'self'");
-
 // Temporarily force-enable GL compositing.  This is default-disabled
 // deep within the bowels of the widgetry system.  Remove me when GL
 // compositing isn't default disabled in widget/android.
 pref("layers.acceleration.force-enabled", true);
 
 // handle links targeting new windows
 // 1=current window/tab, 2=new window, 3=new tab in most recent window
 pref("browser.link.open_newwindow", 3);
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -24,22 +24,16 @@ SCRIPT_DIR = os.path.abspath(os.path.rea
 sys.path.insert(0, SCRIPT_DIR)
 import automationutils
 
 _DEFAULT_WEB_SERVER = "127.0.0.1"
 _DEFAULT_HTTP_PORT = 8888
 _DEFAULT_SSL_PORT = 4443
 _DEFAULT_WEBSOCKET_PORT = 9988
 
-# from nsIPrincipal.idl
-_APP_STATUS_NOT_INSTALLED = 0
-_APP_STATUS_INSTALLED     = 1
-_APP_STATUS_PRIVILEGED    = 2
-_APP_STATUS_CERTIFIED     = 3
-
 #expand _DIST_BIN = __XPC_BIN_PATH__
 #expand _IS_WIN32 = len("__WIN32__") != 0
 #expand _IS_MAC = __IS_MAC__ != 0
 #expand _IS_LINUX = __IS_LINUX__ != 0
 #ifdef IS_CYGWIN
 #expand _IS_CYGWIN = __IS_CYGWIN__ == 1
 #else
 _IS_CYGWIN = False
@@ -298,18 +292,17 @@ class Automation(object):
 
   def setupTestApps(self, profileDir, apps):
     webappJSONTemplate = Template(""""$name": {
 "origin": "$origin",
 "installOrigin": "$origin",
 "receipt": null,
 "installTime": 132333986000,
 "manifestURL": "$manifestURL",
-"localId": $localId,
-"appStatus": $appStatus
+"localId": $localId
 }""")
 
     manifestTemplate = Template("""{
   "name": "$name",
   "description": "$description",
   "launch_path": "/",
   "developer": {
     "name": "Mozilla",
@@ -522,60 +515,41 @@ user_pref("camino.use_system_proxy_setti
     prefsFile.write("".join(prefs))
     prefsFile.close()
 
     apps = [
       {
         'name': 'http_example_org',
         'origin': 'http://example.org',
         'manifestURL': 'http://example.org/manifest.webapp',
-        'description': 'http://example.org App',
-        'appStatus': _APP_STATUS_INSTALLED
+        'description': 'http://example.org App'
       },
       {
         'name': 'https_example_com',
         'origin': 'https://example.com',
         'manifestURL': 'https://example.com/manifest.webapp',
-        'description': 'https://example.com App',
-        'appStatus': _APP_STATUS_INSTALLED
+        'description': 'https://example.com App'
       },
       {
         'name': 'http_test1_example_org',
         'origin': 'http://test1.example.org',
         'manifestURL': 'http://test1.example.org/manifest.webapp',
-        'description': 'http://test1.example.org App',
-        'appStatus': _APP_STATUS_INSTALLED
+        'description': 'http://test1.example.org App'
       },
       {
         'name': 'http_test1_example_org_8000',
         'origin': 'http://test1.example.org:8000',
         'manifestURL': 'http://test1.example.org:8000/manifest.webapp',
-        'description': 'http://test1.example.org:8000 App',
-        'appStatus': _APP_STATUS_INSTALLED
+        'description': 'http://test1.example.org:8000 App'
       },
       {
         'name': 'http_sub1_test1_example_org',
         'origin': 'http://sub1.test1.example.org',
         'manifestURL': 'http://sub1.test1.example.org/manifest.webapp',
-        'description': 'http://sub1.test1.example.org App',
-        'appStatus': _APP_STATUS_INSTALLED
-      },
-      {
-        'name': 'https_example_com_privileged',
-        'origin': 'https://example.com',
-        'manifestURL': 'https://example.com/manifest_priv.webapp',
-        'description': 'https://example.com Privileged App',
-        'appStatus': _APP_STATUS_PRIVILEGED
-      },
-      {
-        'name': 'https_example_com_certified',
-        'origin': 'https://example.com',
-        'manifestURL': 'https://example.com/manifest_cert.webapp',
-        'description': 'https://example.com Certified App',
-        'appStatus': _APP_STATUS_CERTIFIED
+        'description': 'http://sub1.test1.example.org App'
       },
     ];
     self.setupTestApps(profileDir, apps)
 
   def addCommonOptions(self, parser):
     "Adds command-line options which are common to mochitest and reftest."
 
     parser.add_option("--setpref",
--- a/caps/idl/nsIPrincipal.idl
+++ b/caps/idl/nsIPrincipal.idl
@@ -16,17 +16,17 @@ struct JSPrincipals;
 
 interface nsIURI;
 interface nsIContentSecurityPolicy;
 
 [ptr] native JSContext(JSContext);
 [ptr] native JSPrincipals(JSPrincipals);
 [ptr] native PrincipalArray(nsTArray<nsCOMPtr<nsIPrincipal> >);
 
-[scriptable, uuid(115d1081-373e-4837-8d12-d0f4874f3467)]
+[scriptable, uuid(825ffce8-962d-11e1-aef3-8f2b6188709b)]
 interface nsIPrincipal : nsISerializable
 {
     /**
      * Values of capabilities for each principal. Order is
      * significant: if an operation is performed on a set
      * of capabilities, the minimum is computed.
      */
     const short ENABLE_DENIED                = 1;
@@ -268,23 +268,16 @@ interface nsIPrincipal : nsISerializable
      * app.
      */
     readonly attribute unsigned long appId;
 
     /**
      * Returns true iif the principal is inside a browser element.
      */
     readonly attribute boolean isInBrowserElement;
-
-    /**
-     * Returns true if this principal has an unknown appId. This shouldn't
-     * generally be used. We only expose it due to not providing the correct
-     * appId everywhere where we construct principals.
-     */
-    readonly attribute boolean unknownAppId;
 };
 
 /**
  * If nsSystemPrincipal is too risky to use, but we want a principal to access 
  * more than one origin, nsExpandedPrincipals letting us define an array of 
  * principals it subsumes. So script with an nsExpandedPrincipals will gain
  * same origin access when at least one of its principals it contains gained 
  * sameorigin acccess. An nsExpandedPrincipal will be subsumed by the system
--- a/caps/include/nsPrincipal.h
+++ b/caps/include/nsPrincipal.h
@@ -126,17 +126,16 @@ public:
   NS_IMETHOD GetOrigin(char** aOrigin);
   NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD SubsumesIgnoringDomain(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal);
   NS_IMETHOD GetExtendedOrigin(nsACString& aExtendedOrigin);
   NS_IMETHOD GetAppStatus(uint16_t* aAppStatus);
   NS_IMETHOD GetAppId(uint32_t* aAppStatus);
   NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement);
-  NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId);
 #ifdef DEBUG
   virtual void dumpImpl();
 #endif
 
   nsPrincipal();
 
   // Either Init() or InitFromPersistent() must be called before
   // the principal is in a usable state.
@@ -223,17 +222,16 @@ public:
   NS_IMETHOD GetOrigin(char** aOrigin);
   NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD SubsumesIgnoringDomain(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal);
   NS_IMETHOD GetExtendedOrigin(nsACString& aExtendedOrigin);
   NS_IMETHOD GetAppStatus(uint16_t* aAppStatus);
   NS_IMETHOD GetAppId(uint32_t* aAppStatus);
   NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement);
-  NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId);
 #ifdef DEBUG
   virtual void dumpImpl();
 #endif
   
   virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE;
 
 private:
   nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals;
--- a/caps/src/nsNullPrincipal.cpp
+++ b/caps/src/nsNullPrincipal.cpp
@@ -355,23 +355,16 @@ nsNullPrincipal::GetAppId(uint32_t* aApp
 
 NS_IMETHODIMP
 nsNullPrincipal::GetIsInBrowserElement(bool* aIsInBrowserElement)
 {
   *aIsInBrowserElement = false;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsNullPrincipal::GetUnknownAppId(bool* aUnknownAppId)
-{
-  *aUnknownAppId = false;
-  return NS_OK;
-}
-
 /**
  * nsISerializable implementation
  */
 NS_IMETHODIMP
 nsNullPrincipal::Read(nsIObjectInputStream* aStream)
 {
   // no-op: CID is sufficient to create a useful nsNullPrincipal, since the URI
   // is not really relevant.
--- a/caps/src/nsPrincipal.cpp
+++ b/caps/src/nsPrincipal.cpp
@@ -1107,23 +1107,16 @@ nsPrincipal::GetAppId(uint32_t* aAppId)
 NS_IMETHODIMP
 nsPrincipal::GetIsInBrowserElement(bool* aIsInBrowserElement)
 {
   *aIsInBrowserElement = mInMozBrowser;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsPrincipal::GetUnknownAppId(bool* aUnknownAppId)
-{
-  *aUnknownAppId = mAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsPrincipal::Read(nsIObjectInputStream* aStream)
 {
   bool hasCapabilities;
   nsresult rv = aStream->ReadBoolean(&hasCapabilities);
   if (NS_SUCCEEDED(rv) && hasCapabilities) {
     mCapabilities = new nsHashtable(aStream, ReadAnnotationEntry,
                                     FreeAnnotationEntry, &rv);
     NS_ENSURE_TRUE(mCapabilities, NS_ERROR_OUT_OF_MEMORY);
@@ -1537,23 +1530,16 @@ nsExpandedPrincipal::GetAppId(uint32_t* 
 }
 
 NS_IMETHODIMP
 nsExpandedPrincipal::GetIsInBrowserElement(bool* aIsInBrowserElement)
 {
   return NS_ERROR_NOT_AVAILABLE;
 }
 
-NS_IMETHODIMP
-nsExpandedPrincipal::GetUnknownAppId(bool* aUnknownAppId)
-{
-  *aUnknownAppId = false;
-  return NS_OK;
-}
-
 void
 nsExpandedPrincipal::GetScriptLocation(nsACString& aStr)
 {
   if (mCert) {
     aStr.Assign(mCert->fingerprint);
   } else {
     // Is that a good idea to list it's principals?
     aStr.Assign(EXPANDED_PRINCIPAL_SPEC);
--- a/caps/src/nsSystemPrincipal.cpp
+++ b/caps/src/nsSystemPrincipal.cpp
@@ -260,23 +260,16 @@ nsSystemPrincipal::GetAppId(uint32_t* aA
 
 NS_IMETHODIMP
 nsSystemPrincipal::GetIsInBrowserElement(bool* aIsInBrowserElement)
 {
   *aIsInBrowserElement = false;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsSystemPrincipal::GetUnknownAppId(bool* aUnknownAppId)
-{
-  *aUnknownAppId = false;
-  return NS_OK;
-}
-
 //////////////////////////////////////////
 // Methods implementing nsISerializable //
 //////////////////////////////////////////
 
 NS_IMETHODIMP
 nsSystemPrincipal::Read(nsIObjectInputStream* aStream)
 {
     // no-op: CID is sufficient to identify the mSystemPrincipal singleton
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -2389,16 +2389,19 @@ nsDocument::StartDocumentLoad(const char
   } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
     // Allow CSS, but not scripts
     ScriptLoader()->SetEnabled(false);
   }
 
   mMayStartLayout = false;
 
   mHaveInputEncoding = true;
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  nsresult rv = InitCSP(aChannel, getter_AddRefs(csp));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   if (aReset) {
     Reset(aChannel, aLoadGroup);
   }
 
   nsAutoCString contentType;
   if (NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
     // XXX this is only necessary for viewsource:
@@ -2418,202 +2421,150 @@ nsDocument::StartDocumentLoad(const char
   // to the document. These are immutable after being set here.
   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
 
   if (docShell) {
     nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  nsresult rv = InitCSP(aChannel);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (csp) {
+    // Copy into principal
+    nsIPrincipal* principal = GetPrincipal();
+    principal->SetCsp(csp);
+#ifdef PR_LOGGING
+    PR_LOG(gCspPRLog, PR_LOG_DEBUG,
+           ("Inserted CSP into principal %p", principal));
+#endif
+  }
 
   return NS_OK;
 }
 
 nsresult
-nsDocument::InitCSP(nsIChannel* aChannel)
-{
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  if (!CSPService::sCSPEnabled) {
-#ifdef PR_LOGGING
-    PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-           ("CSP is disabled, skipping CSP init for document %p", this));
-#endif
-    return NS_OK;
-  }
-
-  nsAutoCString tCspHeaderValue, tCspROHeaderValue;
-  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
-  if (httpChannel) {
+nsDocument::InitCSP(nsIChannel* aChannel, nsIContentSecurityPolicy **aCSP)
+{
+  *aCSP = nullptr;
+  if (CSPService::sCSPEnabled) {
+    nsAutoCString tCspHeaderValue, tCspROHeaderValue;
+    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+    if (!httpChannel) {
+      // no CSP for non http channels
+      return NS_OK;
+    }
     httpChannel->GetResponseHeader(
         NS_LITERAL_CSTRING("x-content-security-policy"),
         tCspHeaderValue);
 
     httpChannel->GetResponseHeader(
         NS_LITERAL_CSTRING("x-content-security-policy-report-only"),
         tCspROHeaderValue);
-  }
-  NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
-  NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
-
-  // ----- Figure out if we need to apply an app default CSP
-  bool applyAppDefaultCSP = false;
-  nsIPrincipal* principal = NodePrincipal();
-  PRUint16 appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
-  bool unknownAppId;
-  if (NS_SUCCEEDED(principal->GetUnknownAppId(&unknownAppId)) &&
-      !unknownAppId &&
-      NS_SUCCEEDED(principal->GetAppStatus(&appStatus))) {
-    applyAppDefaultCSP = ( appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED ||
-                           appStatus == nsIPrincipal::APP_STATUS_CERTIFIED);
-  }
-#ifdef PR_LOGGING
-  else
-    PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to get app status from principal"));
-#endif
-
-  // If there's no CSP to apply go ahead and return early
-  if (!applyAppDefaultCSP &&
-      cspHeaderValue.IsEmpty() &&
-      cspROHeaderValue.IsEmpty()) {
-#ifdef PR_LOGGING
-    nsCOMPtr<nsIURI> chanURI;
-    aChannel->GetURI(getter_AddRefs(chanURI));
-    nsAutoCString aspec;
-    chanURI->GetAsciiSpec(aspec);
-    PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-           ("no CSP for document, %s, %s",
-            aspec.get(),
-            applyAppDefaultCSP ? "is app" : "not an app"));
-#endif
-    return NS_OK;
-  }
+    NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
+    NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
+
+    if (cspHeaderValue.IsEmpty() && cspROHeaderValue.IsEmpty()) {
+      // no CSP header present, stop processing
+      return NS_OK;
+    }
 
 #ifdef PR_LOGGING
-  PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Document is an app or CSP header specified %p", this));
+    PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP header specified for document %p", this));
 #endif
 
-  nsresult rv;
-  csp = do_CreateInstance("@mozilla.org/contentsecuritypolicy;1", &rv);
-
-  if (NS_FAILED(rv)) {
+    nsresult rv;
+    nsCOMPtr<nsIContentSecurityPolicy> csp;
+    csp = do_CreateInstance("@mozilla.org/contentsecuritypolicy;1", &rv);
+
+    if (NS_FAILED(rv)) {
 #ifdef PR_LOGGING
-    PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to create CSP object: %x", rv));
+      PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to create CSP object: %x", rv));
 #endif
-    return rv;
-  }
-
-  // used as a "self" identifier for the CSP.
-  nsCOMPtr<nsIURI> chanURI;
-  aChannel->GetURI(getter_AddRefs(chanURI));
-
-  // Store the request context for violation reports
-  csp->ScanRequestData(httpChannel);
-
-  // ----- process the app default policy, if necessary
-  if (applyAppDefaultCSP) {
-    nsAdoptingString appCSP;
-    if (appStatus ==  nsIPrincipal::APP_STATUS_PRIVILEGED) {
-      appCSP = Preferences::GetString("security.apps.privileged.CSP.default");
-      NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.privileged.CSP.default");
-    } else if (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED) {
-      appCSP = Preferences::GetString("security.apps.certified.CSP.default");
-      NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.certified.CSP.default");
-    }
-
-    if (appCSP)
-      csp->RefinePolicy(appCSP, chanURI);
-  }
-
-  // ----- if there's a full-strength CSP header, apply it.
-  if (!cspHeaderValue.IsEmpty()) {
-    // Need to tokenize the header value since multiple headers could be
-    // concatenated into one comma-separated list of policies.
-    // See RFC2616 section 4.2 (last paragraph)
-    nsCharSeparatedTokenizer tokenizer(cspHeaderValue, ',');
-    while (tokenizer.hasMoreTokens()) {
-        const nsSubstring& policy = tokenizer.nextToken();
-        csp->RefinePolicy(policy, chanURI);
+      return rv;
+    }
+
+    // Store the request context for violation reports
+    csp->ScanRequestData(httpChannel);
+
+    // Start parsing the policy
+    nsCOMPtr<nsIURI> chanURI;
+    aChannel->GetURI(getter_AddRefs(chanURI));
+
 #ifdef PR_LOGGING
-        {
-          PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-                  ("CSP refined with policy: \"%s\"",
-                    NS_ConvertUTF16toUTF8(policy).get()));
-        }
+    PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP Loaded"));
 #endif
-    }
-  }
-
-  // ----- if there's a report-only CSP header, apply it
-  if (!cspROHeaderValue.IsEmpty()) {
-    // post a warning and skip report-only CSP when both read only and regular
-    // CSP policies are present since CSP only allows one policy and it can't
-    // be partially report-only.
-    if (applyAppDefaultCSP || !cspHeaderValue.IsEmpty()) {
-      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
-                                      "CSP", this,
-                                      nsContentUtils::eDOM_PROPERTIES,
-                                      "ReportOnlyCSPIgnored");
-#ifdef PR_LOGGING
-      PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-              ("Skipped report-only CSP init for document %p because another, enforced policy is set", this));
-#endif
-    } else {
-      // we can apply the report-only policy because there's no other CSP
-      // applied.
+
+    // ReportOnly mode is enabled *only* if there are no regular-strength CSP
+    // headers present.  If there are, then we ignore the ReportOnly mode and
+    // toss a warning into the error console, proceeding with enforcing the
+    // regular-strength CSP.
+    if (cspHeaderValue.IsEmpty()) {
       csp->SetReportOnlyMode(true);
 
       // Need to tokenize the header value since multiple headers could be
       // concatenated into one comma-separated list of policies.
       // See RFC2616 section 4.2 (last paragraph)
       nsCharSeparatedTokenizer tokenizer(cspROHeaderValue, ',');
       while (tokenizer.hasMoreTokens()) {
         const nsSubstring& policy = tokenizer.nextToken();
         csp->RefinePolicy(policy, chanURI);
 #ifdef PR_LOGGING
         {
           PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-                  ("CSP (report-only) refined with policy: \"%s\"",
+                  ("CSP (report only) refined with policy: \"%s\"",
                     NS_ConvertUTF16toUTF8(policy).get()));
         }
 #endif
       }
-    }
-  }
-
-  // ----- Enforce frame-ancestor policy on any applied policies
-  nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocumentContainer);
-  if (docShell) {
-    bool safeAncestry = false;
-
-    // PermitsAncestry sends violation reports when necessary
-    rv = csp->PermitsAncestry(docShell, &safeAncestry);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!safeAncestry) {
+    } else {
+      //XXX(sstamm): maybe we should post a warning when both read only and regular
+      // CSP headers are present.
+
+      // Need to tokenize the header value since multiple headers could be
+      // concatenated into one comma-separated list of policies.
+      // See RFC2616 section 4.2 (last paragraph)
+      nsCharSeparatedTokenizer tokenizer(cspHeaderValue, ',');
+      while (tokenizer.hasMoreTokens()) {
+        const nsSubstring& policy = tokenizer.nextToken();
+        csp->RefinePolicy(policy, chanURI);
 #ifdef PR_LOGGING
-      PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-              ("CSP doesn't like frame's ancestry, not loading."));
+        {
+          PR_LOG(gCspPRLog, PR_LOG_DEBUG,
+                ("CSP refined with policy: \"%s\"",
+                  NS_ConvertUTF16toUTF8(policy).get()));
+        }
 #endif
-      // stop!  ERROR page!
-      aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
-    }
-  }
-
-  if (csp) {
-    // Copy into principal
-    nsIPrincipal* principal = GetPrincipal();
-    principal->SetCsp(csp);
+      }
+    }
+
+    // Check for frame-ancestor violation
+    nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocumentContainer);
+    if (docShell) {
+        bool safeAncestry = false;
+
+        // PermitsAncestry sends violation reports when necessary
+        rv = csp->PermitsAncestry(docShell, &safeAncestry);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (!safeAncestry) {
 #ifdef PR_LOGGING
-    PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-           ("Inserted CSP into principal %p", principal));
+            PR_LOG(gCspPRLog, PR_LOG_DEBUG,
+                   ("CSP doesn't like frame's ancestry, not loading."));
 #endif
-  }
-
+            // stop!  ERROR page!
+            aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
+        }
+    }
+    csp.forget(aCSP);
+  }
+#ifdef PR_LOGGING
+  else { //CSP was not enabled!
+    PR_LOG(gCspPRLog, PR_LOG_DEBUG,
+           ("CSP is disabled, skipping CSP init for document %p", this));
+  }
+#endif
   return NS_OK;
 }
 
 void
 nsDocument::StopDocumentLoad()
 {
   if (mParser) {
     mParserAborted = true;
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -1269,17 +1269,17 @@ private:
   };
   // Recomputes the visibility state but doesn't set the new value.
   VisibilityState GetVisibilityState() const;
 
   void PostUnblockOnloadEvent();
   void DoUnblockOnload();
 
   nsresult CheckFrameOptions();
-  nsresult InitCSP(nsIChannel* aChannel);
+  nsresult InitCSP(nsIChannel* aChannel, nsIContentSecurityPolicy **aCSP);
 
   // Sets aElement to be the pending pointer lock element. Once this document's
   // node principal's URI is granted the "fullscreen" permission, the pointer
   // lock request will be processed. At any one time there can be only one
   // pending pointer lock request; calling this clears the previous pending
   // request.
   static nsresult SetPendingPointerLockRequest(Element* aElement);
 
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -362,18 +362,16 @@ MOCHITEST_FILES_B = \
 		file_CSP_frameancestors_main.js \
 		test_CSP_inlinescript.html \
 		file_CSP_inlinescript_main.html \
 		file_CSP_inlinescript_main.html^headers^ \
 		test_CSP_evalscript.html \
 		file_CSP_evalscript_main.html \
 		file_CSP_evalscript_main.html^headers^ \
 		file_CSP_evalscript_main.js \
-		file_csp_bug768029.html \
-		file_csp_bug768029.sjs \
 		test_bug540854.html \
 		bug540854.sjs \
 		test_bug548463.html \
 		test_bug545644.html \
 		test_bug545644.xhtml \
 		test_bug553896.xhtml \
 		test_bug515401.html \
 		test_bug541937.html \
--- a/content/base/test/chrome/Makefile.in
+++ b/content/base/test/chrome/Makefile.in
@@ -41,12 +41,11 @@ MOCHITEST_CHROME_FILES = \
     test_bug650776.html \
     test_bug650784.html \
     test_bug750096.html \
     test_bug752226-3.xul \
     test_bug752226-4.xul \
     test_bug682305.html \
     test_bug780199.xul \
     test_bug780529.xul \
-    test_csp_bug768029.html \
     $(NULL)
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/content/base/test/chrome/test_csp_bug768029.html
+++ /dev/null
@@ -1,221 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-  https://bugzilla.mozilla.org/show_bug.cgi?id=768029
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for CSP on trusted/certified apps -- bug 768029</title>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=768029">Mozilla Bug 768029</a>
-<p id="display"></p>
-<div id="content">
-
-</div>
-<pre id="test">
-<script type="application/javascript;version=1.7">
-
-Components.utils.import("resource://gre/modules/Services.jsm");
-
-/** Test for Bug 768029 **/
-
-// Note: we don't have to inspect all the different operations of CSP,
-// we're just looking for specific differences in behavior that indicate
-// a default CSP got applied.
-const DEFAULT_CSP_PRIV = "default-src *; script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'none'";
-const DEFAULT_CSP_CERT = "default-src *; script-src 'self'; style-src 'self'; object-src 'none'";
-
-SimpleTest.waitForExplicitFinish();
-
-var gData = [
-  {
-    app: "https://example.com/manifest.webapp",
-    appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_INSTALLED,
-    origin: "https://example.com",
-    uri: "https://example.com/tests/content/base/test/file_csp_bug768029.html",
-    statusString: "installed app",
-    expectedTestResults: {
-      max_tests: 7, /* number of bools below plus one for the status check */
-      cross_origin: { img: true,  script: true,  style: true },
-      same_origin:  { img: true,  script: true,  style: true  },
-    },
-  },
-
-  {
-    app: "https://example.com/manifest_priv.webapp",
-    appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_PRIVILEGED,
-    origin: "https://example.com",
-    uri: "https://example.com/tests/content/base/test/file_csp_bug768029.html",
-    statusString: "privileged app",
-    expectedTestResults: {
-      max_tests: 7, /* number of bools below plus one for the status check */
-      cross_origin: { img: true,  script: false, style: false },
-      same_origin:  { img: true,  script: true,  style: true  },
-    },
-  },
-
-  {
-    app: "https://example.com/manifest_cert.webapp",
-    appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_CERTIFIED,
-    origin: "https://example.com",
-    uri: "https://example.com/tests/content/base/test/file_csp_bug768029.html",
-    statusString: "certified app",
-    expectedTestResults: {
-      max_tests: 7, /* number of bools below plus one for the status check */
-      cross_origin: { img: true,  script: false, style: false },
-      same_origin:  { img: true,  script: true,  style: true  },
-    },
-  },
-];
-
-// Observer for watching allowed loads and blocked attempts
-function ThingyListener(app, iframe) {
-  Services.obs.addObserver(this, "csp-on-violate-policy", false);
-  Services.obs.addObserver(this, "http-on-modify-request", false);
-  dump("added observers\n");
-  // keep track of which app ID this test is monitoring.
-  this._testData = app;
-  this._expectedResults = app.expectedTestResults;
-  this._resultsRecorded = { cross_origin: {}, same_origin: {}};
-  this._iframe = iframe;
-  this._countedTests = 0;
-}
-ThingyListener.prototype = {
-
-  observe: function(subject, topic, data) {
-    // make sure to only observe app-generated calls to the helper for this test.
-    var testpat = new RegExp("file_csp_bug768029\\.sjs");
-
-    // used to extract which kind of load this is (img, script, etc).
-    var typepat = new RegExp("type=([\\_a-z0-9]+)");
-
-    // used to identify whether it's cross-origin or same-origin loads
-    // (the applied CSP allows same-origin loads).
-    var originpat = new RegExp("origin=([\\_a-z0-9]+)");
-
-    if (topic === "http-on-modify-request") {
-      // Matching requests on this topic were allowed by the csp
-      var chan = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
-      var uri = chan.URI;
-      // ignore irrelevent URIs
-      if (!testpat.test(uri.asciiSpec)) return;
-
-      var loadType = typepat.exec(uri.asciiSpec)[1];
-      var originType = originpat.exec(uri.asciiSpec)[1];
-
-      // skip duplicate hits to this topic (potentially document loads
-      // may generate duplicate loads.
-      if (this._resultsRecorded[originType] &&
-          this._resultsRecorded[originType][loadType]) {
-        return;
-      }
-      var message = originType + " : " + loadType + " should be " +
-                    (this._expectedResults[originType][loadType] ? "allowed" : "blocked");
-      ok(this._expectedResults[originType][loadType] == true, message);
-      this._resultsRecorded[originType][loadType] = true;
-      this._countedTests++;
-    }
-    else if (topic === "csp-on-violate-policy") {
-      // Matching hits on this topic were blocked by the csp
-      var uri = subject.QueryInterface(Components.interfaces.nsIURI);
-      // ignore irrelevent URIs
-      if (!testpat.test(uri.asciiSpec)) return;
-
-      var loadType = typepat.exec(uri.asciiSpec)[1];
-      var originType = originpat.exec(uri.asciiSpec)[1];
-
-      // skip duplicate hits to this topic (potentially document loads
-      // may generate duplicate loads.
-      if (this._resultsRecorded[originType] &&
-          this._resultsRecorded[originType][loadType]) {
-        return;
-      }
-
-      var message = originType + " : " + loadType + " should be " +
-                    (this._expectedResults[originType][loadType] ? "allowed" : "blocked");
-      ok(this._expectedResults[originType][loadType] == false, message);
-      this._resultsRecorded[originType][loadType] = true;
-      this._countedTests++;
-    }
-    else {
-      // wrong topic!  Nothing to do.
-      return;
-    }
-
-    this._checkForFinish();
-  },
-
-  _checkForFinish: function() {
-    // check to see if there are load tests still pending.
-    // (All requests triggered by the app should hit one of the
-    // two observer topics.)
-    if (this._countedTests == this._expectedResults.max_tests) {
-      Services.obs.removeObserver(this, "csp-on-violate-policy");
-      Services.obs.removeObserver(this, "http-on-modify-request");
-      dump("removed observers\n");
-      checkedCount++;
-      if (checkedCount == checksTodo) {
-        SpecialPowers.removePermission("browser", "https://example.com");
-        SimpleTest.finish();
-      } else {
-        gTestRunner.next();
-      }
-    }
-  },
-
-  // verify the status of the app
-  checkAppStatus: function() {
-    var principal = this._iframe.contentDocument.nodePrincipal;
-    if (this._testData.app) {
-      is(principal.appStatus, this._testData.appStatus,
-         "iframe principal's app status doesn't match the expected app status.");
-      this._countedTests++;
-      this._checkForFinish();
-    }
-  }
-}
-
-var content = document.getElementById('content');
-var checkedCount = 0; // number of apps checked
-var checksTodo = gData.length;
-
-// quick check to make sure we can test apps:
-is('appStatus' in document.nodePrincipal, true,
-   'appStatus should be present in nsIPrincipal, if not the rest of this test will fail');
-
-function runTest() {
-  for (var i = 0; i < gData.length; i++) {
-    let data = gData[i];
-    var iframe = document.createElement('iframe');
-
-    // watch for successes and failures
-    var examiner = new ThingyListener(data, iframe);
-
-    iframe.setAttribute('mozapp', data.app);
-    iframe.setAttribute('mozbrowser', 'true');
-    iframe.addEventListener('load', examiner.checkAppStatus.bind(examiner));
-    iframe.src = data.uri;
-
-    content.appendChild(iframe);
-
-    yield;
-  }
-}
-
-var gTestRunner = runTest();
-
-// load the default CSP and pref it on
-SpecialPowers.addPermission("browser", true, "https://example.com");
-SpecialPowers.pushPrefEnv({'set': [["dom.mozBrowserFramesEnabled", true],
-                                   ["security.apps.privileged.CSP.default", DEFAULT_CSP_PRIV],
-                                   ["security.apps.certified.CSP.default", DEFAULT_CSP_CERT]]},
-                          function() {  gTestRunner.next(); });
-
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/content/base/test/file_csp_bug768029.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-  https://bugzilla.mozilla.org/show_bug.cgi?id=768029
--->
-<head>
-  <meta charset="utf-8">
-    <title>This is an app for testing</title>
-
-    <link rel="stylesheet" type="text/css"
-          href="file_csp_bug768029.sjs?type=style&origin=same_origin" />
-    <link rel="stylesheet" type="text/css"
-          href="http://example.com/tests/content/base/test/file_csp_bug768029.sjs?type=style&origin=cross_origin" />
-  </head>
-  <body>
-
-    <script src="file_csp_bug768029.sjs?type=script&origin=same_origin"></script>
-    <script src="http://example.com/tests/content/base/test/file_csp_bug768029.sjs?type=script&origin=cross_origin"></script>
-    <img src="file_csp_bug768029.sjs?type=img&origin=same_origin" />
-    <img src="http://example.com/tests/content/base/test/file_csp_bug768029.sjs?type=img&origin=cross_origin" />
-
-    Test for CSP applied to (simulated) app.
-
-  </body>
-</html>
deleted file mode 100644
--- a/content/base/test/file_csp_bug768029.sjs
+++ /dev/null
@@ -1,29 +0,0 @@
-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('<pre>script loaded\\n</pre>');\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!");
-}
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -100,17 +100,16 @@ FullScreenDeniedLostWindow=Request for f
 FullScreenDeniedSubDocFullScreen=Request for full-screen was denied because a subdocument of the document requesting full-screen is already full-screen.
 FullScreenDeniedNotDescendant=Request for full-screen was denied because requesting element is not a descendant of the current full-screen element.
 FullScreenDeniedNotFocusedTab=Request for full-screen was denied because requesting element is not in the currently focused tab.
 RemovedFullScreenElement=Exited full-screen because full-screen element was removed from document.
 FocusedWindowedPluginWhileFullScreen=Exited full-screen because windowed plugin was focused.
 HTMLMultipartXHRWarning=HTML parsing in XMLHttpRequest is not supported for multipart responses.
 HTMLSyncXHRWarning=HTML parsing in XMLHttpRequest is not supported in the synchronous mode.
 InvalidRedirectChannelWarning=Unable to redirect to %S because the channel doesn't implement nsIWritablePropertyBag2.
-ReportOnlyCSPIgnored=Report-only CSP policy will be ignored because there are other non-report-only CSP policies applied.
 ResponseTypeSyncXHRWarning=Use of XMLHttpRequest's responseType attribute is no longer supported in the synchronous mode in window context.
 WithCredentialsSyncXHRWarning=Use of XMLHttpRequest's withCredentials attribute is no longer supported in the synchronous mode in window context.
 TimeoutSyncXHRWarning=Use of XMLHttpRequest's timeout attribute is not supported in the synchronous mode in window context.
 JSONCharsetWarning=An attempt was made to declare a non-UTF-8 encoding for JSON retrieved using XMLHttpRequest. Only UTF-8 is supported for decoding JSON.
 MediaLoadExhaustedCandidates=All candidate resources failed to load. Media load paused.
 MediaLoadSourceMissingSrc=<source> element has no "src" attribute. Media resource load failed.
 # LOCALIZATION NOTE: %1$S is the Http error code the server returned (e.g. 404, 500, etc), %2$S is the URL of the media resource which failed to load.
 MediaLoadHttpError=HTTP load failed with status %1$S. Load of media resource %2$S failed.