Bug 855326 - CSP 1.1 nonce-source for scripts and styles. r=mrbkap r=dholbert r=geekboy
☠☠ backed out by c45039455165 ☠ ☠
authorGarrett Robinson <grobinson@mozilla.com>
Fri, 08 Nov 2013 09:20:43 -0800
changeset 168768 57213b64023b7ad16014b296c5817d39a29c3907
parent 168767 1a92a46ff6be75e4fce09aeec66d44145793e685
child 168769 14366dd910b6041b8fe857d96888e94aeef0a3e9
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap, dholbert, geekboy
bugs855326
milestone28.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 855326 - CSP 1.1 nonce-source for scripts and styles. r=mrbkap r=dholbert r=geekboy
caps/src/nsScriptSecurityManager.cpp
content/base/public/nsIContentSecurityPolicy.idl
content/base/src/CSPUtils.jsm
content/base/src/contentSecurityPolicy.js
content/base/src/nsGkAtomList.h
content/base/src/nsScriptLoader.cpp
content/base/src/nsStyleLinkElement.cpp
content/base/src/nsStyledElement.cpp
content/base/test/csp/file_nonce_source.html
content/base/test/csp/file_nonce_source.html^headers^
content/base/test/csp/mochitest.ini
content/base/test/csp/test_nonce_source.html
content/events/src/nsEventListenerManager.cpp
content/smil/nsSMILCSSValueType.cpp
dom/base/nsJSTimeoutHandler.cpp
dom/src/jsurl/nsJSProtocolHandler.cpp
dom/workers/RuntimeService.cpp
layout/style/nsStyleUtil.cpp
layout/style/nsStyleUtil.h
modules/libpref/src/init/all.js
security/manager/ssl/src/nsCrypto.cpp
testing/mochitest/b2g.json
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -484,17 +484,18 @@ nsScriptSecurityManager::ContentSecurity
         if (JS_DescribeScriptedCaller(cx, &script, &lineNum)) {
             if (const char *file = JS_GetScriptFilename(cx, script)) {
                 CopyUTF8toUTF16(nsDependentCString(file), fileName);
             }
         }
         csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
                                  fileName,
                                  scriptSample,
-                                 lineNum);
+                                 lineNum,
+                                 EmptyString());
     }
 
     return evalOK;
 }
 
 
 bool
 nsScriptSecurityManager::CheckObjectAccess(JSContext *cx, JS::Handle<JSObject*> obj,
--- a/content/base/public/nsIContentSecurityPolicy.idl
+++ b/content/base/public/nsIContentSecurityPolicy.idl
@@ -10,17 +10,17 @@ interface nsIDocShell;
 
 /**
  * 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.
  */
 
-[scriptable, uuid(e5020ec3-1437-46f5-b4eb-8b60766d02c0)]
+[scriptable, uuid(781b6511-f1fa-4e2c-8eff-1739d091eb2f)]
 interface nsIContentSecurityPolicy : nsISupports
 {
 
   /**
    * Set to true when the CSP has been read in and parsed and is ready to
    * enforce.  This is a barrier for the nsDocument so it doesn't load any
    * sub-content until either it knows that a CSP is ready or will not be used.
    */
@@ -100,37 +100,62 @@ interface nsIContentSecurityPolicy : nsI
    *     inline style should be permitted.
    * @return
    *     Whether or not the effects of the inline style should be allowed
    *     (block the rules if false).
    */
   boolean getAllowsInlineStyle(out boolean shouldReportViolations);
 
   /**
+   * Whether this policy accepts the given nonce
+   * @param aNonce
+   *     The nonce string to check against the policy
+   * @param aContentType
+   *     The type of element on which we encountered this nonce
+   * @param shouldReportViolation
+   *     Whether or not the use of an incorrect nonce should be reported.
+   *     This function always returns "true" for report-only policies, but when
+   *     the report-only policy is violated, shouldReportViolation is true as
+   *     well.
+   * @return
+   *     Whether or not this nonce is valid
+   */
+   boolean getAllowsNonce(in AString aNonce,
+                          in unsigned long aContentType,
+                          out boolean shouldReportViolation);
+
+  /**
    * For each violated policy (of type violationType), log policy violation on
    * the Error Console and send a report to report-uris present in the violated
    * policies.
    *
    * @param violationType
    *     one of the VIOLATION_TYPE_* constants, e.g. inline-script or eval
    * @param sourceFile
    *     name of the source file containing the violation (if available)
    * @param contentSample
    *     sample of the violating content (to aid debugging)
    * @param lineNum
    *     source line number of the violation (if available)
+   * @param aNonce
+   *     (optional) If this is a nonce violation, include the nonce so we can
+   *     recheck to determine which policies were violated and send the
+   *     appropriate reports.
    */
   void logViolationDetails(in unsigned short violationType,
                            in AString sourceFile,
                            in AString scriptSample,
-                           in int32_t lineNum);
+                           in int32_t lineNum,
+                           [optional] in AString nonce);
 
   const unsigned short VIOLATION_TYPE_INLINE_SCRIPT = 1;
-  const unsigned short VIOLATION_TYPE_EVAL = 2;
-  const unsigned short VIOLATION_TYPE_INLINE_STYLE = 3;
+  const unsigned short VIOLATION_TYPE_EVAL          = 2;
+  const unsigned short VIOLATION_TYPE_INLINE_STYLE  = 3;
+  const unsigned short VIOLATION_TYPE_NONCE_SCRIPT  = 4;
+  const unsigned short VIOLATION_TYPE_NONCE_STYLE   = 5;
 
   /**
    * Called after the CSP object is created to fill in the appropriate request
    * and request header information needed in case a report needs to be sent.
    */
   void scanRequestData(in nsIHttpChannel aChannel);
 
   /**
--- a/content/base/src/CSPUtils.jsm
+++ b/content/base/src/CSPUtils.jsm
@@ -19,17 +19,18 @@ const ERROR_FLAG = Ci.nsIScriptError.ERR
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
                                   "resource://gre/modules/Services.jsm");
 
 // Module stuff
 this.EXPORTED_SYMBOLS = ["CSPRep", "CSPSourceList", "CSPSource", "CSPHost",
-                         "CSPdebug", "CSPViolationReportListener", "CSPLocalizer"];
+                         "CSPdebug", "CSPViolationReportListener", "CSPLocalizer",
+                         "CSPPrefObserver"];
 
 var STRINGS_URI = "chrome://global/locale/security/csp.properties";
 
 // these are not exported
 var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
                  .getService(Ci.nsIIOService);
 
 var gETLDService = Components.classes["@mozilla.org/network/effective-tld-service;1"]
@@ -60,51 +61,65 @@ const R_HOSTSRC    = new RegExp ("^((" +
 
 // ext-host-source = host-source "/" *( <VCHAR except ";" and ","> )
 //                 ; ext-host-source is reserved for future use.
 const R_EXTHOSTSRC = new RegExp ("^" + R_HOSTSRC.source + "\\/[:print:]+$", 'i');
 
 // keyword-source  = "'self'" / "'unsafe-inline'" / "'unsafe-eval'"
 const R_KEYWORDSRC = new RegExp ("^('self'|'unsafe-inline'|'unsafe-eval')$", 'i');
 
+// nonce-source      = "'nonce-" nonce-value "'"
+// nonce-value       = 1*( ALPHA / DIGIT / "+" / "/" )
+const R_NONCESRC = new RegExp ("^'nonce-([a-zA-Z0-9\+\/]+)'$", 'i');
+
 // source-exp      = scheme-source / host-source / keyword-source
 const R_SOURCEEXP  = new RegExp (R_SCHEMESRC.source + "|" +
                                    R_HOSTSRC.source + "|" +
-                                R_KEYWORDSRC.source,  'i');
+                                R_KEYWORDSRC.source + "|" +
+                                  R_NONCESRC.source,  'i');
 
 
-var gPrefObserver = {
+this.CSPPrefObserver = {
   get debugEnabled () {
     if (!this._branch)
       this._initialize();
     return this._debugEnabled;
   },
 
+  get experimentalEnabled () {
+    if (!this._branch)
+      this._initialize();
+    return this._experimentalEnabled;
+  },
+
   _initialize: function() {
     var prefSvc = Components.classes["@mozilla.org/preferences-service;1"]
                     .getService(Ci.nsIPrefService);
     this._branch = prefSvc.getBranch("security.csp.");
     this._branch.addObserver("", this, false);
     this._debugEnabled = this._branch.getBoolPref("debug");
+    this._experimentalEnabled = this._branch.getBoolPref("experimentalEnabled");
   },
 
   unregister: function() {
     if (!this._branch) return;
     this._branch.removeObserver("", this);
   },
 
   observe: function(aSubject, aTopic, aData) {
     if (aTopic != "nsPref:changed") return;
     if (aData === "debug")
       this._debugEnabled = this._branch.getBoolPref("debug");
+    if (aData === "experimentalEnabled")
+      this._experimentalEnabled = this._branch.getBoolPref("experimentalEnabled");
   },
 };
 
 this.CSPdebug = function CSPdebug(aMsg) {
-  if (!gPrefObserver.debugEnabled) return;
+  if (!CSPPrefObserver.debugEnabled) return;
 
   aMsg = 'CSP debug: ' + aMsg + "\n";
   Components.classes["@mozilla.org/consoleservice;1"]
                     .getService(Ci.nsIConsoleService)
                     .logStringMessage(aMsg);
 }
 
 // Callback to resume a request once the policy-uri has been fetched
@@ -788,66 +803,80 @@ CSPRep.prototype = {
         dirs.push(i + " " + this._directives[i].toString());
       }
     }
     return dirs.join("; ");
   },
 
   /**
    * Determines if this policy accepts a URI.
+   * @param aURI
+   *        URI of the requested resource
+   * @param aDirective
+   *        one of the SRC_DIRECTIVES defined above
    * @param aContext
-   *        one of the SRC_DIRECTIVES defined above
+   *        Context of the resource being requested. This is a type inheriting
+   *        from nsIDOMHTMLElement if this is called from shouldLoad to check
+   *        an external resource load, and refers to the HTML element that is
+   *        causing the resource load. Otherwise, it is a string containing
+   *        a nonce from a nonce="" attribute if it is called from
+   *        getAllowsNonce.
    * @returns
    *        true if the policy permits the URI in given context.
    */
   permits:
-  function csp_permits(aURI, aContext) {
-    if (!aURI) return false;
+  function csp_permits(aURI, aDirective, aContext) {
+    // In the case where permits is called from getAllowsNonce (for an inline
+    // element), aURI is null and aContext has a specific value. Otherwise,
+    // calling permits without aURI is invalid.
+    let checking_nonce = aContext instanceof Ci.nsIDOMHTMLElement ||
+                         typeof aContext === 'string';
+    if (!aURI && !checking_nonce) return false;
 
     // GLOBALLY ALLOW "about:" SCHEME
     if (aURI instanceof String && aURI.substring(0,6) === "about:")
       return true;
     if (aURI instanceof Ci.nsIURI && aURI.scheme === "about")
       return true;
 
     // make sure the right directive set is used
     let DIRS = this._specCompliant ? CSPRep.SRC_DIRECTIVES_NEW : CSPRep.SRC_DIRECTIVES_OLD;
 
-    let contextIsSrcDir = false;
+    let directiveInPolicy = false;
     for (var i in DIRS) {
-      if (DIRS[i] === aContext) {
+      if (DIRS[i] === aDirective) {
         // for catching calls with invalid contexts (below)
-        contextIsSrcDir = true;
-        if (this._directives.hasOwnProperty(aContext)) {
-          return this._directives[aContext].permits(aURI);
+        directiveInPolicy = true;
+        if (this._directives.hasOwnProperty(aDirective)) {
+          return this._directives[aDirective].permits(aURI, aContext);
         }
         //found matching dir, can stop looking
         break;
       }
     }
 
     // frame-ancestors is a special case; it doesn't fall back to default-src.
-    if (aContext === DIRS.FRAME_ANCESTORS)
+    if (aDirective === DIRS.FRAME_ANCESTORS)
       return true;
 
     // All directives that don't fall back to default-src should have an escape
     // hatch above (like frame-ancestors).
-    if (!contextIsSrcDir) {
+    if (!directiveInPolicy) {
       // if this code runs, there's probably something calling permits() that
       // shouldn't be calling permits().
-      CSPdebug("permits called with invalid load type: " + aContext);
+      CSPdebug("permits called with invalid load type: " + aDirective);
       return false;
     }
 
     // no directives specifically matched, fall back to default-src.
     // (default-src may not be present for CSP 1.0-compliant policies, and
     // indicates no relevant directives were present and the load should be
     // permitted).
     if (this._directives.hasOwnProperty(DIRS.DEFAULT_SRC)) {
-      return this._directives[DIRS.DEFAULT_SRC].permits(aURI);
+      return this._directives[DIRS.DEFAULT_SRC].permits(aURI, aContext);
     }
 
     // no relevant directives present -- this means for CSP 1.0 that the load
     // should be permitted (and for the old CSP, to block it).
     return this._specCompliant;
   },
 
   /**
@@ -1076,37 +1105,38 @@ CSPSourceList.prototype = {
   /**
    * Determines if this directive accepts a URI.
    * @param aURI
    *        the URI in question
    * @returns
    *        true if the URI matches a source in this source list.
    */
   permits:
-  function cspsd_permits(aURI) {
+  function cspsd_permits(aURI, aContext) {
     if (this.isNone())    return false;
     if (this.isAll())     return true;
 
     for (var i in this._sources) {
-      if (this._sources[i].permits(aURI)) {
+      if (this._sources[i].permits(aURI, aContext)) {
         return true;
       }
     }
     return false;
   }
 }
 
 //////////////////////////////////////////////////////////////////////
 /**
  * Class to model a source (scheme, host, port)
  */
 this.CSPSource = function CSPSource() {
   this._scheme = undefined;
   this._port = undefined;
   this._host = undefined;
+  this._nonce = undefined;
 
   //when set to true, this allows all source
   this._permitAll = false;
 
   // when set to true, this source represents 'self'
   this._isSelf = false;
 
   // when set to true, this source allows inline scripts or styles
@@ -1341,16 +1371,29 @@ CSPSource.fromString = function(aStr, aC
     }
     else {
       // strip the ':' from the port
       sObj._port = portMatch[0].substr(1);
     }
     return sObj;
   }
 
+  // check for a nonce-source match
+  if (R_NONCESRC.test(aStr)) {
+    // We can't put this check outside of the regex test because R_NONCESRC is
+    // included in R_SOURCEEXP, which is const. By testing here, we can
+    // explicitly return null for nonces if experimental is not enabled,
+    // instead of letting it fall through and assuming it won't accidentally
+    // match something later in this function.
+    if (!CSPPrefObserver.experimentalEnabled) return null;
+    var nonceSrcMatch = R_NONCESRC.exec(aStr);
+    sObj._nonce = nonceSrcMatch[1];
+    return sObj;
+  }
+
   // check for 'self' (case insensitive)
   if (aStr.toUpperCase() === "'SELF'") {
     if (!self) {
       cspError(aCSPRep, CSPLocalizer.getStr("selfKeywordNoSelfData"));
       return null;
     }
     sObj._self = self.clone();
     sObj._isSelf = true;
@@ -1445,45 +1488,61 @@ CSPSource.prototype = {
 
     var s = "";
     if (this.scheme)
       s = s + this.scheme + "://";
     if (this._host)
       s = s + this._host;
     if (this.port)
       s = s + ":" + this.port;
+    if (this._nonce)
+      s = s + "'nonce-" + this._nonce + "'";
     return s;
   },
 
   /**
    * Makes a new deep copy of this object.
    * @returns
    *      a new CSPSource
    */
   clone:
   function() {
     var aClone = new CSPSource();
     aClone._self = this._self ? this._self.clone() : undefined;
     aClone._scheme = this._scheme;
     aClone._port = this._port;
     aClone._host = this._host ? this._host.clone() : undefined;
+    aClone._nonce = this._nonce;
     aClone._isSelf = this._isSelf;
     aClone._CSPRep = this._CSPRep;
     return aClone;
   },
 
   /**
    * Determines if this Source accepts a URI.
    * @param aSource
    *        the URI, or CSPSource in question
+   * @param aContext
+   *        the context of the resource being loaded
    * @returns
    *        true if the URI matches a source in this source list.
    */
   permits:
-  function(aSource) {
+  function(aSource, aContext) {
+    if (this._nonce && CSPPrefObserver.experimentalEnabled) {
+      if (aContext instanceof Ci.nsIDOMHTMLElement) {
+        return this._nonce === aContext.getAttribute('nonce');
+      } else if (typeof aContext === 'string') {
+        return this._nonce === aContext;
+      }
+    }
+    // We only use aContext for nonce checks. If it's otherwise provided,
+    // ignore it.
+    if (!CSPPrefObserver.experimentalEnabled && aContext) return false;
+
     if (!aSource) return false;
 
     if (!(aSource instanceof CSPSource))
       aSource = CSPSource.create(aSource, this._CSPRep);
 
     // verify scheme
     if (this.scheme != aSource.scheme)
       return false;
--- a/content/base/src/contentSecurityPolicy.js
+++ b/content/base/src/contentSecurityPolicy.js
@@ -26,16 +26,18 @@ const CSP_TYPE_XMLHTTPREQUEST_SPEC_COMPL
 const CSP_TYPE_WEBSOCKET_SPEC_COMPLIANT = "csp_type_websocket_spec_compliant";
 
 const WARN_FLAG = Ci.nsIScriptError.warningFlag;
 const ERROR_FLAG = Ci.nsIScriptError.ERROR_FLAG;
 
 const INLINE_STYLE_VIOLATION_OBSERVER_SUBJECT = 'violated base restriction: Inline Stylesheets will not apply';
 const INLINE_SCRIPT_VIOLATION_OBSERVER_SUBJECT = 'violated base restriction: Inline Scripts will not execute';
 const EVAL_VIOLATION_OBSERVER_SUBJECT = 'violated base restriction: Code will not be created from strings';
+const SCRIPT_NONCE_VIOLATION_OBSERVER_SUBJECT = 'Inline Script had invalid nonce'
+const STYLE_NONCE_VIOLATION_OBSERVER_SUBJECT = 'Inline Style had invalid nonce'
 
 // The cutoff length of content location in creating CSP cache key.
 const CSP_CACHE_URI_CUTOFF_SIZE = 512;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/CSPUtils.jsm");
 
@@ -184,31 +186,57 @@ ContentSecurityPolicy.prototype = {
     shouldReportViolations.value = this._policies.some(function(a) { return !a.allowsInlineStyles; });
 
     // allow it to execute?  (Do all the policies allow it to execute)?
     return this._policies.every(function(a) {
       return a._reportOnlyMode || a.allowsInlineStyles;
     });
   },
 
+  getAllowsNonce: function(aNonce, aContentType, shouldReportViolation) {
+    if (!CSPPrefObserver.experimentalEnabled)
+      return false;
+
+    if (!(aContentType == Ci.nsIContentPolicy.TYPE_SCRIPT ||
+          aContentType == Ci.nsIContentPolicy.TYPE_STYLESHEET)) {
+      CSPdebug("Nonce check requested for an invalid content type (not script or style): " + aContentType);
+      return false;
+    }
+    let ct = ContentSecurityPolicy._MAPPINGS[aContentType];
+
+    // allow it to execute?
+    let policyAllowsNonce = [ policy.permits(null, ct, aNonce) for (policy of this._policies) ];
+
+    shouldReportViolation.value = policyAllowsNonce.some(function(a) { return !a; });
+
+    // allow it to execute?  (Do all the policies allow it to execute)?
+    return this._policies.every(function(policy, i) {
+      return policy._reportOnlyMode || policyAllowsNonce[i];
+    });
+  },
+
   /**
    * For each policy, log any violation on the Error Console and send a report
    * if a report-uri is present in the policy
    *
    * @param aViolationType
    *     one of the VIOLATION_TYPE_* constants, e.g. inline-script or eval
    * @param aSourceFile
    *     name of the source file containing the violation (if available)
    * @param aContentSample
    *     sample of the violating content (to aid debugging)
    * @param aLineNum
    *     source line number of the violation (if available)
+   * @param aNonce
+   *     (optional) If this is a nonce violation, include the nonce should we
+   *     can recheck to determine which policies were violated and send the
+   *     appropriate reports.
    */
   logViolationDetails:
-  function(aViolationType, aSourceFile, aScriptSample, aLineNum, violatedPolicyIndex) {
+  function(aViolationType, aSourceFile, aScriptSample, aLineNum, aNonce) {
     for (let policyIndex=0; policyIndex < this._policies.length; policyIndex++) {
       let policy = this._policies[policyIndex];
 
       // call-sites to the eval/inline checks recieve two return values: allows
       // and violates.  Policies that are report-only allow the
       // loads/compilations but violations should still be reported.  Not all
       // policies in this nsIContentSecurityPolicy instance will be violated,
       // which is why we must check again here.
@@ -232,16 +260,34 @@ ContentSecurityPolicy.prototype = {
       case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL:
         if (!policy.allowsEvalInScripts) {
           var violatedDirective = this._buildViolatedDirectiveString('SCRIPT_SRC', policy);
           this._asyncReportViolation('self', null, violatedDirective, policyIndex,
                                     EVAL_VIOLATION_OBSERVER_SUBJECT,
                                     aSourceFile, aScriptSample, aLineNum);
         }
         break;
+      case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_NONCE_SCRIPT:
+        let scriptType = ContentSecurityPolicy._MAPPINGS[Ci.nsIContentPolicy.TYPE_SCRIPT];
+        if (!policy.permits(null, scriptType, aNonce)) {
+          var violatedDirective = this._buildViolatedDirectiveString('SCRIPT_SRC', policy);
+          this._asyncReportViolation('self', null, violatedDirective, policyIndex,
+                                     SCRIPT_NONCE_VIOLATION_OBSERVER_SUBJECT,
+                                     aSourceFile, aScriptSample, aLineNum);
+        }
+        break;
+      case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_NONCE_STYLE:
+        let styleType = ContentSecurityPolicy._MAPPINGS[Ci.nsIContentPolicy.TYPE_STYLE];
+        if (!policy.permits(null, styleType, aNonce)) {
+          var violatedDirective = this._buildViolatedDirectiveString('STYLE_SRC', policy);
+          this._asyncReportViolation('self', null, violatedDirective, policyIndex,
+                                     STYLE_NONCE_VIOLATION_OBSERVER_SUBJECT,
+                                     aSourceFile, aScriptSample, aLineNum);
+        }
+        break;
       }
     }
   },
 
   /**
    * Given an nsIHttpChannel, fill out the appropriate data.
    */
   scanRequestData:
@@ -623,16 +669,24 @@ ContentSecurityPolicy.prototype = {
 #endif
 
     // The mapping for XHR and websockets is different between our original
     // implementation and the 1.0 spec, we handle this here.
     var cspContext;
 
     let cp = Ci.nsIContentPolicy;
 
+    // Infer if this is a preload for elements that use nonce-source. Since,
+    // for preloads, aContext is the document and not the element associated
+    // with the resource, we cannot determine the nonce. See Bug 612921 and
+    // Bug 855326.
+    var possiblePreloadNonceConflict =
+      (aContentType == cp.TYPE_SCRIPT || aContentType == cp.TYPE_STYLESHEET) &&
+      aContext instanceof Ci.nsIDOMHTMLDocument;
+
     // iterate through all the _policies and send reports where a policy is
     // violated.  After the check, determine the overall effect (blocked or
     // loaded?) and cache it.
     let policyAllowsLoadArray = [];
     for (let policyIndex=0; policyIndex < this._policies.length; policyIndex++) {
       let policy = this._policies[policyIndex];
 
 #ifndef MOZ_B2G
@@ -656,25 +710,28 @@ ContentSecurityPolicy.prototype = {
 
       // if the mapping is null, there's no policy, let it through.
       if (!cspContext) {
         return Ci.nsIContentPolicy.ACCEPT;
       }
 
       // otherwise, honor the translation
       // var source = aContentLocation.scheme + "://" + aContentLocation.hostPort;
-      var res = policy.permits(aContentLocation, cspContext)
-                ? cp.ACCEPT : cp.REJECT_SERVER;
+      let context = CSPPrefObserver.experimentalEnabled ? aContext : null;
+      var res = policy.permits(aContentLocation, cspContext, context) ?
+                cp.ACCEPT : cp.REJECT_SERVER;
       // record whether the thing should be blocked or just reported.
       policyAllowsLoadArray.push(res == cp.ACCEPT || policy._reportOnlyMode);
 
       // frame-ancestors is taken care of early on (as this document is loaded)
 
       // If the result is *NOT* ACCEPT, then send report
-      if (res != Ci.nsIContentPolicy.ACCEPT) {
+      // Do not send report if this is a nonce-source preload - the decision may
+      // be wrong and will incorrectly fail the unit tests.
+      if (res != Ci.nsIContentPolicy.ACCEPT && !possiblePreloadNonceConflict) {
         CSPdebug("blocking request for " + aContentLocation.asciiSpec);
         try {
           let directive = "unknown directive",
               violatedPolicy = "unknown policy";
 
           // The policy might not explicitly declare each source directive (so
           // the cspContext may be implicit).  If so, we have to report
           // violations as appropriate: specific or the default-src directive.
@@ -699,17 +756,18 @@ ContentSecurityPolicy.prototype = {
     } // end for-each loop over policies
 
     // the ultimate decision is based on whether any policies want to reject
     // the load.  The array keeps track of whether the policies allowed the
     // loads. If any doesn't, we'll reject the load (and cache the result).
     let ret = (policyAllowsLoadArray.some(function(a,b) { return !a; }) ?
                cp.REJECT_SERVER : cp.ACCEPT);
 
-    if (key) {
+    // Do not cache the result if this is a nonce-source preload
+    if (key && !possiblePreloadNonceConflict) {
       this._cache[key] = ret;
     }
     return ret;
   },
 
   shouldProcess:
   function csp_shouldProcess(aContentType,
                              aContentLocation,
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -603,16 +603,17 @@ GK_ATOM(no, "no")
 GK_ATOM(noautohide, "noautohide")
 GK_ATOM(nobr, "nobr")
 GK_ATOM(node, "node")
 GK_ATOM(nodefaultsrc, "nodefaultsrc")
 GK_ATOM(nodeSet, "node-set")
 GK_ATOM(noembed, "noembed")
 GK_ATOM(noframes, "noframes")
 GK_ATOM(nohref, "nohref")
+GK_ATOM(nonce, "nonce")
 GK_ATOM(none, "none")
 GK_ATOM(noresize, "noresize")
 GK_ATOM(normal, "normal")
 GK_ATOM(normalizeSpace, "normalize-space")
 GK_ATOM(noscript, "noscript")
 GK_ATOM(noshade, "noshade")
 GK_ATOM(novalidate, "novalidate")
 GK_ATOM(_not, "not")
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -619,44 +619,66 @@ nsScriptLoader::ProcessScriptElement(nsI
     return false;
   }
 
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
   NS_ENSURE_SUCCESS(rv, false);
 
   if (csp) {
-    PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("New ScriptLoader i ****with CSP****"));
-    bool inlineOK = true;
-    bool reportViolations = false;
-    rv = csp->GetAllowsInlineScript(&reportViolations, &inlineOK);
+    PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("New ScriptLoader ****with CSP****"));
+
+    bool reportViolation = false;
+    bool allowInlineScript = true;
+    rv = csp->GetAllowsInlineScript(&reportViolation, &allowInlineScript);
     NS_ENSURE_SUCCESS(rv, false);
 
-    if (reportViolations) {
+    bool foundNonce = false;
+    nsAutoString nonce;
+    if (!allowInlineScript) {
+      foundNonce = scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::nonce, nonce);
+      if (foundNonce) {
+        // We can overwrite the outparams from GetAllowsInlineScript because
+        // if the nonce is correct, then we don't want to report the original
+        // inline violation (it has been whitelisted by the nonce), and if
+        // the nonce is incorrect, then we want to return just the specific
+        // "nonce violation" rather than both a "nonce violation" and
+        // a generic "inline violation".
+        rv = csp->GetAllowsNonce(nonce, nsIContentPolicy::TYPE_SCRIPT,
+                                 &reportViolation, &allowInlineScript);
+        NS_ENSURE_SUCCESS(rv, false);
+      }
+    }
+
+    if (reportViolation) {
       // gather information to log with violation report
       nsIURI* uri = mDocument->GetDocumentURI();
       nsAutoCString asciiSpec;
       uri->GetAsciiSpec(asciiSpec);
       nsAutoString scriptText;
       aElement->GetScriptText(scriptText);
 
       // cap the length of the script sample at 40 chars
       if (scriptText.Length() > 40) {
         scriptText.Truncate(40);
-        scriptText.Append(NS_LITERAL_STRING("..."));
+        scriptText.AppendLiteral("...");
       }
 
-      csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT,
-                               NS_ConvertUTF8toUTF16(asciiSpec),
-                               scriptText,
-                               aElement->GetScriptLineNumber());
+      // The type of violation to report is determined by whether there was
+      // a nonce present.
+      unsigned short violationType = foundNonce ?
+        nsIContentSecurityPolicy::VIOLATION_TYPE_NONCE_SCRIPT :
+        nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT;
+      csp->LogViolationDetails(violationType, NS_ConvertUTF8toUTF16(asciiSpec),
+                               scriptText, aElement->GetScriptLineNumber(), nonce);
     }
 
-    if (!inlineOK) {
-      PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP blocked inline scripts (2)"));
+    if (!allowInlineScript) {
+      NS_ASSERTION(reportViolation,
+          "CSP blocked inline script but is not reporting a violation");
       return false;
     }
   }
 
   // Inline scripts ignore ther CORS mode and are always CORS_NONE
   request = new nsScriptLoadRequest(aElement, version, CORS_NONE);
   request->mJSVersion = version;
   request->mLoading = false;
--- a/content/base/src/nsStyleLinkElement.cpp
+++ b/content/base/src/nsStyleLinkElement.cpp
@@ -355,17 +355,20 @@ nsStyleLinkElement::DoUpdateStyleSheet(n
   }
 
   bool doneLoading = false;
   nsresult rv = NS_OK;
   if (isInline) {
     nsAutoString text;
     nsContentUtils::GetNodeTextContent(thisContent, false, text);
 
-    if (!nsStyleUtil::CSPAllowsInlineStyle(thisContent->NodePrincipal(),
+    MOZ_ASSERT(Tag() != nsGkAtoms::link,
+               "<link> is not 'inline', and needs different CSP checks");
+    if (!nsStyleUtil::CSPAllowsInlineStyle(thisContent,
+                                           thisContent->NodePrincipal(),
                                            doc->GetDocumentURI(),
                                            mLineNumber, text, &rv))
       return rv;
 
     // Parse the style sheet.
     rv = doc->CSSLoader()->
       LoadInlineStyle(thisContent, text, mLineNumber, title, media,
                       scopeElement, aObserver, &doneLoading, &isAlternate);
--- a/content/base/src/nsStyledElement.cpp
+++ b/content/base/src/nsStyledElement.cpp
@@ -231,17 +231,17 @@ nsStyledElementNotElementCSSInlineStyle:
 
 void
 nsStyledElementNotElementCSSInlineStyle::ParseStyleAttribute(const nsAString& aValue,
                                                              nsAttrValue& aResult,
                                                              bool aForceInDataDoc)
 {
   nsIDocument* doc = OwnerDoc();
 
-  if (!nsStyleUtil::CSPAllowsInlineStyle(NodePrincipal(),
+  if (!nsStyleUtil::CSPAllowsInlineStyle(nullptr, NodePrincipal(),
                                          doc->GetDocumentURI(), 0, aValue,
                                          nullptr))
     return;
 
   if (aForceInDataDoc ||
       !doc->IsLoadedAsData() ||
       doc->IsStaticDocument()) {
     bool isCSS = true; // assume CSS until proven otherwise
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_nonce_source.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<html>
+  <head>
+    <!-- external styles -->
+    <link rel='stylesheet' nonce="correctstylenonce" href="file_CSP.sjs?testid=external_style_correct_nonce_good&type=text/css" />
+    <link rel='stylesheet' nonce="incorrectstylenonce" href="file_CSP.sjs?testid=external_style_incorrect_nonce_bad&type=text/css" />
+    <link rel='stylesheet' nonce="correctscriptnonce" href="file_CSP.sjs?testid=external_style_correct_script_nonce_bad&type=text/css" />
+    <link rel='stylesheet' href="file_CSP.sjs?testid=external_style_no_nonce_bad&type=text/css" />
+  </head>
+  <body>
+    <!-- inline scripts -->
+    <script nonce="correctscriptnonce">
+      window.parent.inlineScriptTestResult("allowed", "allowed", "This script has a correct nonce for scripts");
+    </script>
+    <script nonce="incorrectscriptnonce">
+      window.parent.inlineScriptTestResult("allowed", "blocked", "This script has an incorrect nonce for scripts");
+    </script>
+    <script nonce="correctstylenonce">
+      window.parent.inlineScriptTestResult("allowed", "blocked", "This script has a correct nonce for styles (but not for scripts)");
+    </script>
+    <script>
+      window.parent.inlineScriptTestResult("allowed", "blocked", "This script has no nonce");
+    </script>
+
+    <!-- external scripts -->
+    <script nonce="correctscriptnonce" src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=external_script_correct_nonce_good&type=text/javascript"></script>
+    <script nonce="anothercorrectscriptnonce" src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=external_script_another_correct_nonce_good&type=text/javascript"></script>
+    <script nonce="incorrectscriptnonce" src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=external_script_incorrect_nonce_bad&type=text/javascript"></script>
+    <script nonce="correctstylenonce" src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=external_script_correct_style_nonce_bad&type=text/javascript"></script>
+    <script src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=external_script_no_nonce_bad&type=text/javascript"></script>
+
+    <!-- This external script has the correct nonce and comes from a whitelisted URI. It should be allowed. -->
+    <script nonce="correctscriptnonce" src="file_CSP.sjs?testid=external_script_correct_nonce_correct_uri_good&type=text/javascript"></script>
+    <!-- This external script has an incorrect nonce, but comes from a whitelisted URI. It should be allowed. -->
+    <script nonce="incorrectscriptnonce" src="file_CSP.sjs?testid=external_script_incorrect_nonce_correct_uri_good&type=text/javascript"></script>
+    <!-- This external script has no nonce and comes from a whitelisted URI. It should be allowed. -->
+    <script src="file_CSP.sjs?testid=external_script_no_nonce_correct_uri_good&type=text/javascript"></script>
+
+    <!-- inline styles -->
+    <ol>
+      <li id=inline-style-correct-nonce>
+        (inline style with correct nonce) This text should be green
+      </li>
+      <li id=inline-style-incorrect-nonce>
+        (inline style with incorrect nonce) This text should be black
+      </li>
+      <li id=inline-style-correct-script-nonce>
+        (inline style with correct script, not style, nonce) This text should be black
+      </li>
+      <li id=inline-style-no-nonce>
+        (inline style with no nonce) This text should be black
+      </li>
+    </ol>
+    <style nonce=correctstylenonce>
+      li#inline-style-correct-nonce { color: green; }
+    </style>
+    <style nonce=incorrectstylenonce>
+      li#inline-style-incorrect-nonce { color: red; }
+    </style>
+    <style nonce=correctscriptnonce>
+      li#inline-style-correct-script-nonce { color: red; }
+    </style>
+    <style>
+      li#inline-style-no-nonce { color: red; }
+    </style>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_nonce_source.html^headers^
@@ -0,0 +1,2 @@
+Content-Security-Policy: script-src 'self' 'nonce-correctscriptnonce' 'nonce-anothercorrectscriptnonce'; style-src 'nonce-correctstylenonce';
+Cache-Control: no-cache
--- a/content/base/test/csp/mochitest.ini
+++ b/content/base/test/csp/mochitest.ini
@@ -80,16 +80,18 @@ support-files =
   file_CSP_bug910139.xsl
   file_CSP_bug909029_star.html
   file_CSP_bug909029_star.html^headers^
   file_CSP_bug909029_none.html
   file_CSP_bug909029_none.html^headers^
   file_policyuri_regression_from_multipolicy.html
   file_policyuri_regression_from_multipolicy.html^headers^
   file_policyuri_regression_from_multipolicy_policy
+  file_nonce_source.html
+  file_nonce_source.html^headers^
 
 [test_CSP.html]
 [test_CSP_bug663567.html]
 [test_CSP_bug802872.html]
 [test_CSP_bug885433.html]
 [test_CSP_bug888172.html]
 [test_CSP_bug916446.html]
 [test_CSP_evalscript.html]
@@ -98,8 +100,9 @@ support-files =
 [test_CSP_inlinescript.html]
 [test_CSP_inlinestyle.html]
 [test_bothCSPheaders.html]
 [test_bug836922_npolicies.html]
 [test_csp_redirects.html]
 [test_CSP_bug910139.html]
 [test_CSP_bug909029.html]
 [test_policyuri_regression_from_multipolicy.html]
+[test_nonce_source.html]
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/test_nonce_source.html
@@ -0,0 +1,149 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test CSP 1.1 nonce-source for scripts and styles</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="visibility:hidden">
+  <iframe style="width:100%;" id='cspframe'></iframe>
+</div>
+<script class="testbody" type="text/javascript">
+
+var testsRun = 0;
+var totalTests = 20;
+
+var inlineScriptTestsRun = 0;
+var totalInlineScriptTests = 4;
+
+var scriptNonceViolations = 0;
+var expectedScriptNonceViolations = 2;
+var scriptInlineViolations = 0;
+var expectedScriptInlineViolations = 1;
+
+// This is used to watch the blocked data bounce off CSP
+function examiner() {
+  SpecialPowers.addObserver(this, "http-on-modify-request", false);
+  SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+}
+
+examiner.prototype = {
+  observe: function(subject, topic, data) {
+    if (!SpecialPowers.can_QI(subject))
+      return;
+
+    var testid_re = new RegExp("testid=([a-z0-9_]+)");
+
+    //_good things better be allowed!
+    //_bad things better be blocked!
+
+    if (topic === "http-on-modify-request") {
+      // these things were allowed by CSP
+      var allowed_uri = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
+      if (!testid_re.test(allowed_uri)) return;
+      var testid = testid_re.exec(allowed_uri)[1];
+      ok(/_good/.test(testid), "Allowed URI with testid " + testid);
+      ranTests(1);
+    }
+
+    if (topic === "csp-on-violate-policy") {
+      try {
+        // if it is an blocked external load, subject will be the URI of the resource
+        var blocked_uri = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+        if (!testid_re.test(blocked_uri)) return;
+        var testid = testid_re.exec(blocked_uri)[1];
+        ok(/_bad/.test(testid), "Blocked URI with testid " + testid);
+        ranTests(1);
+      } catch (e) {
+        // if the subject is blocked inline, data will be a violation msg (defined at the top of contentSecurityPolicy.js)
+        var violation_msg = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsISupportsCString"), "data");
+        if (/Inline Script/.test(violation_msg)) {
+          if (/Inline Script had invalid nonce/.test(violation_msg))
+            scriptNonceViolations++;
+          if (/Inline Scripts will not execute/.test(violation_msg))
+            scriptInlineViolations++;
+          window.inlineScriptTestResult("blocked", "blocked",
+                                        "Blocked because " + violation_msg);
+        }
+      }
+    }
+  },
+  // must eventually call this to remove the listener, or mochitests might get borked.
+  remove: function() {
+    SpecialPowers.removeObserver(this, "http-on-modify-request");
+    SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+  }
+}
+
+var inlineScriptTestResult = function(testIs, testShouldBe, description) {
+  if (testIs !== testShouldBe) {
+    ok(false, description);
+  } else {
+    ok(true, description);
+  }
+  ranTests(1)
+
+  inlineScriptTestsRun++;
+  if (inlineScriptTestsRun == totalInlineScriptTests) {
+    if (scriptNonceViolations != expectedScriptNonceViolations)
+      ok(false, "The number of reported script nonce violations does not match expected; got " + scriptNonceViolations + ", expected " + expectedScriptNonceViolations);
+    if (scriptInlineViolations != expectedScriptInlineViolations)
+      ok(false, "The number of reported inline script  violations does not match expected; got " + scriptInlineViolations + ", expected " + expectedScriptInlineViolations);
+    ranTests(2);
+  }
+}
+
+function cleanup() {
+  // remove the observer so we don't bork other tests
+  window.examiner.remove();
+  // finish the tests
+  SimpleTest.finish();
+}
+
+function ranTests(num) {
+  testsRun += num;
+  if (testsRun < totalTests) {
+    return;
+  }
+  cleanup();
+}
+
+function checkStyles () {
+  var cspframe = document.getElementById('cspframe');
+  var getElementColorById = function (id) {
+    return window.getComputedStyle(cspframe.contentDocument.getElementById(id), null).color;
+  };
+  // Inline style tries to change an element's color to green. If blocked, the
+  // element's color will be the default black.
+  var green = "rgb(0, 128, 0)";
+  var black = "rgb(0, 0, 0)";
+
+  is(getElementColorById('inline-style-correct-nonce'), green, "Inline style with correct nonce allowed");
+  is(getElementColorById('inline-style-incorrect-nonce'), black, "Inline style with incorrect nonce blocked");
+  is(getElementColorById('inline-style-correct-script-nonce'), black, "Inline style with correct nonce for scripts (but incorrect nonce for styles) blocked");
+  is(getElementColorById('inline-style-no-nonce'), black, "Inline style with no nonce blocked");
+
+  ranTests(4);
+}
+
+//////////////////////////////////////////////////////////////////////
+// set up and go
+window.examiner = new examiner();
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv(
+  {'set':[["security.csp.speccompliant", true],
+          ["security.csp.experimentalEnabled", true]]},
+  function() {
+    // save this for last so that our listeners are registered.
+    // ... this loads the testbed of good and bad requests.
+    document.getElementById('cspframe').src = 'file_nonce_source.html';
+    document.getElementById('cspframe').addEventListener('load', checkStyles, false);
+  });
+</script>
+</pre>
+</body>
+</html>
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -723,17 +723,18 @@ nsEventListenerManager::SetEventHandler(
         // build a "script sample" based on what we know about this element
         scriptSample.Assign(attr);
         scriptSample.AppendLiteral(" attribute on ");
         scriptSample.Append(tagName);
         scriptSample.AppendLiteral(" element");
         csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT,
                                  NS_ConvertUTF8toUTF16(asciiSpec),
                                  scriptSample,
-                                 0);
+                                 0,
+                                 EmptyString());
       }
 
       // return early if CSP wants us to block inline scripts
       if (!inlineOK) {
         return NS_OK;
       }
     }
   }
--- a/content/smil/nsSMILCSSValueType.cpp
+++ b/content/smil/nsSMILCSSValueType.cpp
@@ -389,17 +389,18 @@ nsSMILCSSValueType::ValueFromString(nsCS
   NS_ABORT_IF_FALSE(aValue.IsNull(), "Outparam should be null-typed");
   nsPresContext* presContext = GetPresContextForElement(aTargetElement);
   if (!presContext) {
     NS_WARNING("Not parsing animation value; unable to get PresContext");
     return;
   }
 
   nsIDocument* doc = aTargetElement->GetCurrentDoc();
-  if (doc && !nsStyleUtil::CSPAllowsInlineStyle(doc->NodePrincipal(),
+  if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr,
+                                                doc->NodePrincipal(),
                                                 doc->GetDocumentURI(),
                                                 0, aString, nullptr)) {
     return;
   }
 
   nsStyleAnimation::Value parsedValue;
   if (ValueFromStringHelper(aPropID, aTargetElement, presContext,
                             aString, parsedValue, aIsContextSensitive)) {
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -176,17 +176,17 @@ CheckCSPForEval(JSContext* aCx, nsGlobal
     nsAutoString fileNameString;
     if (nsJSUtils::GetCallingLocation(aCx, &fileName, &lineNum)) {
       AppendUTF8toUTF16(fileName, fileNameString);
     } else {
       fileNameString.AssignLiteral("unknown");
     }
 
     csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
-                             fileNameString, scriptSample, lineNum);
+                             fileNameString, scriptSample, lineNum, EmptyString());
   }
 
   return allowsEval;
 }
 
 nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler() :
   mLineNo(0)
 {
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -174,17 +174,18 @@ nsresult nsJSThunk::EvaluateScript(nsICh
             // gather information to log with violation report
             nsCOMPtr<nsIURI> uri;
             principal->GetURI(getter_AddRefs(uri));
             nsAutoCString asciiSpec;
             uri->GetAsciiSpec(asciiSpec);
             csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT,
                                      NS_ConvertUTF8toUTF16(asciiSpec),
                                      NS_ConvertUTF8toUTF16(mURL),
-                                     0);
+                                     0,
+                                     EmptyString());
         }
 
         //return early if inline scripts are not allowed
         if (!allowsInline) {
           return NS_ERROR_DOM_RETVAL_UNDEFINED;
         }
     }
 
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -702,17 +702,17 @@ public:
     AssertIsOnMainThread();
 
     nsIContentSecurityPolicy* csp = mWorkerPrivate->GetCSP();
     if (csp) {
       NS_NAMED_LITERAL_STRING(scriptSample,
          "Call to eval() or related function blocked by CSP.");
       if (mWorkerPrivate->GetReportCSPViolations()) {
         csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
-                                 mFileName, scriptSample, mLineNum);
+                                 mFileName, scriptSample, mLineNum, EmptyString());
       }
     }
 
     nsRefPtr<LogViolationDetailsResponseRunnable> response =
         new LogViolationDetailsResponseRunnable(mWorkerPrivate, mSyncQueueKey);
     if (!response->Dispatch(nullptr)) {
       NS_WARNING("Failed to dispatch response!");
     }
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -5,16 +5,17 @@
 
 #include "nsStyleUtil.h"
 #include "nsStyleConsts.h"
 
 #include "nsIContent.h"
 #include "nsCSSProps.h"
 #include "nsRuleNode.h"
 #include "nsROCSSPrimitiveValue.h"
+#include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIURI.h"
 
 using namespace mozilla;
 
 //------------------------------------------------------------------------------
 // Font Algorithm Code
 //------------------------------------------------------------------------------
@@ -436,65 +437,100 @@ nsStyleUtil::IsSignificantChild(nsIConte
   }
 
   return aTextIsSignificant && isText && aChild->TextLength() != 0 &&
          (aWhitespaceIsSignificant ||
           !aChild->TextIsOnlyWhitespace());
 }
 
 /* static */ bool
-nsStyleUtil::CSPAllowsInlineStyle(nsIPrincipal* aPrincipal,
+nsStyleUtil::CSPAllowsInlineStyle(nsIContent* aContent,
+                                  nsIPrincipal* aPrincipal,
                                   nsIURI* aSourceURI,
                                   uint32_t aLineNumber,
                                   const nsSubstring& aStyleText,
                                   nsresult* aRv)
 {
   nsresult rv;
 
   if (aRv) {
     *aRv = NS_OK;
   }
 
+  MOZ_ASSERT(!aContent || aContent->Tag() == nsGkAtoms::style,
+      "aContent passed to CSPAllowsInlineStyle "
+      "for an element that is not <style>");
+
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   rv = aPrincipal->GetCsp(getter_AddRefs(csp));
 
   if (NS_FAILED(rv)) {
     if (aRv)
       *aRv = rv;
     return false;
   }
 
   if (csp) {
-    bool inlineOK = true;
     bool reportViolation;
-    rv = csp->GetAllowsInlineStyle(&reportViolation, &inlineOK);
+    bool allowInlineStyle = true;
+    rv = csp->GetAllowsInlineStyle(&reportViolation, &allowInlineStyle);
     if (NS_FAILED(rv)) {
       if (aRv)
         *aRv = rv;
       return false;
     }
 
+    bool foundNonce = false;
+    nsAutoString nonce;
+    // If inline styles are allowed ('unsafe-inline'), skip the (irrelevant)
+    // nonce check
+    if (!allowInlineStyle) {
+      // We can only find a nonce if aContent is provided
+      foundNonce = !!aContent &&
+        aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::nonce, nonce);
+      if (foundNonce) {
+        // We can overwrite the outparams from GetAllowsInlineStyle because
+        // if the nonce is correct, then we don't want to report the original
+        // inline violation (it has been whitelisted by the nonce), and if
+        // the nonce is incorrect, then we want to return just the specific
+        // "nonce violation" rather than both a "nonce violation" and
+        // a generic "inline violation".
+        rv = csp->GetAllowsNonce(nonce, nsIContentPolicy::TYPE_STYLESHEET,
+                                 &reportViolation, &allowInlineStyle);
+        if (NS_FAILED(rv)) {
+          if (aRv)
+            *aRv = rv;
+          return false;
+        }
+      }
+    }
+
     if (reportViolation) {
-      // Inline styles are not allowed by CSP, so report the violation
+      // This inline style is not allowed by CSP, so report the violation
       nsAutoCString asciiSpec;
       aSourceURI->GetAsciiSpec(asciiSpec);
       nsAutoString styleText(aStyleText);
 
       // cap the length of the style sample at 40 chars.
       if (styleText.Length() > 40) {
         styleText.Truncate(40);
-        styleText.Append(NS_LITERAL_STRING("..."));
+        styleText.AppendLiteral("...");
       }
 
-      csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_STYLE,
-                               NS_ConvertUTF8toUTF16(asciiSpec),
-                               aStyleText,
-                               aLineNumber);
+      // The type of violation to report is determined by whether there was
+      // a nonce present.
+      unsigned short violationType = foundNonce ?
+        nsIContentSecurityPolicy::VIOLATION_TYPE_NONCE_STYLE :
+        nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_STYLE;
+      csp->LogViolationDetails(violationType, NS_ConvertUTF8toUTF16(asciiSpec),
+                               styleText, aLineNumber, nonce);
     }
 
-    if (!inlineOK) {
-        // The inline style should be blocked.
-        return false;
+    if (!allowInlineStyle) {
+      NS_ASSERTION(reportViolation,
+          "CSP blocked inline style but is not reporting a violation");
+      // The inline style should be blocked.
+      return false;
     }
   }
   // No CSP or a CSP that allows inline styles.
   return true;
 }
--- a/layout/style/nsStyleUtil.h
+++ b/layout/style/nsStyleUtil.h
@@ -93,25 +93,42 @@ public:
   /*
    * Does this child count as significant for selector matching?
    */
   static bool IsSignificantChild(nsIContent* aChild,
                                    bool aTextIsSignificant,
                                    bool aWhitespaceIsSignificant);
   /*
    *  Does this principal have a CSP that blocks the application of
-   *  inline styles ? Returns false if application of the style should
+   *  inline styles? Returns false if application of the style should
    *  be blocked.
    *
-   *  Note that the principal passed in here needs to be the principal
-   *  of the document, not of the style sheet. The document's principal
-   *  is where any Content Security Policy that should be used to
-   *  block or allow inline styles will be located.
+   *  @param aContent
+   *      The <style> element that the caller wants to know whether to honor.
+   *      Included to check the nonce attribute if one is provided. Allowed to
+   *      be null, if this is for something other than a <style> element (in
+   *      which case nonces won't be checked).
+   *  @param aPrincipal
+   *      The principal of the of the document (*not* of the style sheet).
+   *      The document's principal is where any Content Security Policy that
+   *      should be used to block or allow inline styles will be located.
+   *  @param aSourceURI
+   *      URI of document containing inline style (for reporting violations)
+   *  @param aLineNumber
+   *      Line number of inline style element in the containing document (for
+   *      reporting violations)
+   *  @param aStyleText
+   *      Contents of the inline style element (for reporting violations)
+   *  @param aRv
+   *      Return error code in case of failure
+   *  @return
+   *      Does CSP allow application of the specified inline style?
    */
-  static bool CSPAllowsInlineStyle(nsIPrincipal* aPrincipal,
+  static bool CSPAllowsInlineStyle(nsIContent* aContent,
+                                   nsIPrincipal* aPrincipal,
                                    nsIURI* aSourceURI,
                                    uint32_t aLineNumber,
                                    const nsSubstring& aStyleText,
                                    nsresult* aRv);
 
 };
 
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1605,16 +1605,17 @@ pref("signed.applets.codebase_principal_
 pref("security.checkloaduri", true);
 pref("security.xpconnect.plugin.unrestricted", true);
 // security-sensitive dialogs should delay button enabling. In milliseconds.
 pref("security.dialog_enable_delay", 2000);
 pref("security.notification_enable_delay", 500);
 
 pref("security.csp.enable", true);
 pref("security.csp.debug", false);
+pref("security.csp.experimentalEnabled", false);
 
 // Mixed content blocking
 pref("security.mixed_content.block_active_content", false);
 pref("security.mixed_content.block_display_content", false);
 
 // Modifier key prefs: default to Windows settings,
 // menu access key = alt, accelerator key = control.
 // Use 17 for Ctrl, 18 for Alt, 224 for Meta, 91 for Win, 0 for none. Mac settings in macprefs.js
--- a/security/manager/ssl/src/nsCrypto.cpp
+++ b/security/manager/ssl/src/nsCrypto.cpp
@@ -1921,17 +1921,18 @@ nsCrypto::GenerateCRMFRequest(JSContext*
     NS_NAMED_LITERAL_STRING(scriptSample, "window.crypto.generateCRMFRequest: call to eval() or related function blocked by CSP");
 
     const char *fileName;
     uint32_t lineNum;
     nsJSUtils::GetCallingLocation(aContext, &fileName, &lineNum);
     csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
                              NS_ConvertASCIItoUTF16(fileName),
                              scriptSample,
-                             lineNum);
+                             lineNum,
+                             EmptyString());
   }
 
   if (!evalAllowed) {
     NS_WARNING("eval() not allowed by Content Security Policy");
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
--- a/testing/mochitest/b2g.json
+++ b/testing/mochitest/b2g.json
@@ -207,16 +207,17 @@
     "content/base/test/csp/test_CSP_evalscript.html":"observer not working",
     "content/base/test/csp/test_CSP_evalscript_getCRMFRequest.html":"observer not working",
     "content/base/test/csp/test_CSP_frameancestors.html":"observer not working",
     "content/base/test/csp/test_CSP.html":"observer not working",
     "content/base/test/csp/test_bug836922_npolicies.html":"observer not working",
     "content/base/test/csp/test_CSP_bug916446.html":"observer not working",
     "content/base/test/csp/test_CSP_bug909029.html":"observer not working",
     "content/base/test/csp/test_policyuri_regression_from_multipolicy.html":"observer not working",
+    "content/base/test/csp/test_nonce_source.html":"observer not working",
 
     "content/base/test/test_CrossSiteXHR_origin.html":"https not working, bug 907770",
     "content/base/test/test_plugin_freezing.html":"",
     "content/base/test/test_bug466409.html":"",
     "content/base/test/test_bug482935.html":"",
     "content/base/test/test_bug498433.html":"",
     "content/base/test/test_bug650386_redirect_301.html":"",
     "content/base/test/test_bug650386_redirect_302.html":"",