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 114374 f4e6abb6160f644546f14533640e81d3c54fc24d
parent 114373 4489b74f83994c5d8d8bea9e2b71594cc7193b8a
child 114375 7de3b05cd7d81e1a5205613dd13d226e4f4f4f81
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs768029
milestone18.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
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.