Bug 687086 - change nsIContentSecurityPolicy to return two values, shouldBlock and shouldReportViolation. r=bz
authorSid Stamm <sstamm@mozilla.com>
Mon, 15 Oct 2012 13:54:58 -0700
changeset 126653 c29279c952385f983ff592287303fe31003e66bd
parent 126652 354fb47d8205b827f61605a796fcc68c7af539f0
child 126654 6b50cbf374d6fbda41ce7279c0701c234659db46
push id25573
push usersstamm@mozilla.com
push dateFri, 29 Mar 2013 00:05:59 +0000
treeherdermozilla-inbound@c29279c95238 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs687086
milestone22.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 687086 - change nsIContentSecurityPolicy to return two values, shouldBlock and shouldReportViolation. r=bz
caps/src/nsScriptSecurityManager.cpp
content/base/public/nsIContentSecurityPolicy.idl
content/base/src/contentSecurityPolicy.js
content/base/src/nsScriptLoader.cpp
content/base/test/file_CSP_evalscript_main.html^headers^
content/base/test/file_CSP_evalscript_main.js
content/base/test/unit/test_cspreports.js
content/events/src/nsEventListenerManager.cpp
dom/base/nsJSTimeoutHandler.cpp
dom/src/jsurl/nsJSProtocolHandler.cpp
dom/workers/RuntimeService.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -477,38 +477,36 @@ nsScriptSecurityManager::ContentSecurity
     rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
     NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal.");
 
     // don't do anything unless there's a CSP
     if (!csp)
         return JS_TRUE;
 
     bool evalOK = true;
-    rv = csp->GetAllowsEval(&evalOK);
+    bool reportViolation = false;
+    rv = csp->GetAllowsEval(&reportViolation, &evalOK);
 
     if (NS_FAILED(rv))
     {
         NS_WARNING("CSP: failed to get allowsEval");
         return JS_TRUE; // fail open to not break sites.
     }
 
-    if (!evalOK) {
-        // get the script filename, script sample, and line number
-        // to log with the violation
+    if (reportViolation) {
         nsAutoString fileName;
         unsigned lineNum = 0;
         NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP");
 
         JSScript *script;
         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);
     }
 
     return evalOK;
 }
--- a/content/base/public/nsIContentSecurityPolicy.idl
+++ b/content/base/public/nsIContentSecurityPolicy.idl
@@ -4,21 +4,21 @@
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsIHttpChannel;
 interface nsIDocShell;
 
 /**
- * nsIContentSecurityPolicy  
+ * nsIContentSecurityPolicy
  * Describes an XPCOM component used to model an enforce CSPs.
  */
 
-[scriptable, uuid(d1680bb4-1ac0-4772-9437-1188375e44f2)]
+[scriptable, uuid(91E1F257-914C-4D4F-902C-F67F772839AB)]
 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.
    */
@@ -32,24 +32,40 @@ interface nsIContentSecurityPolicy : nsI
 
   /**
    * A read-only string version of the policy for debugging.
    */
   readonly attribute AString policy;
 
   /**
    * Whether this policy allows in-page script.
+   * @param shouldReportViolation
+   *     Whether or not the use of inline script 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 the effects of the inline script should be allowed
+   *     (block the compilation if false).
    */
-  readonly attribute boolean allowsInlineScript;
+  boolean getAllowsInlineScript(out boolean shouldReportViolation);
 
   /**
    * whether this policy allows eval and eval-like functions
    * such as setTimeout("code string", time).
+   * @param shouldReportViolation
+   *     Whether or not the use of eval 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 the effects of the eval call should be allowed
+   *     (block the call if false).
    */
-  readonly attribute boolean allowsEval;
+  boolean getAllowsEval(out boolean shouldReportViolation);
 
   /**
    * Log policy violation on the Error Console and send a report if a report-uri
    * is present in the policy
    *
    * @param violationType
    *     one of the VIOLATION_TYPE_* constants, e.g. inline-script or eval
    * @param sourceFile
@@ -73,17 +89,17 @@ interface nsIContentSecurityPolicy : nsI
    * @param blockedURI
    *     the URI that violated the policy
    * @param violatedDirective
    *     the directive that was violated.
    * @param scriptSample
    *     a sample of the violating inline script
    * @param lineNum
    *     source line number of the violation (if available)
-   * @return 
+   * @return
    *     nothing.
    */
   void sendReports(in AString blockedURI,
                    in AString violatedDirective,
                    in AString scriptSample,
                    in int32_t lineNum);
 
   /**
--- a/content/base/src/contentSecurityPolicy.js
+++ b/content/base/src/contentSecurityPolicy.js
@@ -123,21 +123,29 @@ ContentSecurityPolicy.prototype = {
   set isInitialized (foo) {
     this._isInitialized = foo;
   },
 
   get policy () {
     return this._policy.toString();
   },
 
-  get allowsInlineScript() {
+  getAllowsInlineScript: function(shouldReportViolation) {
+    // report it?
+    shouldReportViolation.value = !this._policy.allowsInlineScripts;
+
+    // allow it to execute?
     return this._reportOnlyMode || this._policy.allowsInlineScripts;
   },
 
-  get allowsEval() {
+  getAllowsEval: function(shouldReportViolation) {
+    // report it?
+    shouldReportViolation.value = !this._policy.allowsEvalInScripts;
+
+    // allow it to execute?
     return this._reportOnlyMode || this._policy.allowsEvalInScripts;
   },
 
   /**
    * Log policy violation on the Error Console and send a report if a report-uri
    * is present in the policy
    *
    * @param aViolationType
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -608,22 +608,22 @@ nsScriptLoader::ProcessScriptElement(nsI
   }
 
   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;
-    rv = csp->GetAllowsInlineScript(&inlineOK);
+    bool inlineOK = true;
+    bool reportViolations = false;
+    rv = csp->GetAllowsInlineScript(&reportViolations, &inlineOK);
     NS_ENSURE_SUCCESS(rv, false);
 
-    if (!inlineOK) {
-      PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP blocked inline scripts (2)"));
+    if (reportViolations) {
       // 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
@@ -631,16 +631,20 @@ nsScriptLoader::ProcessScriptElement(nsI
         scriptText.Truncate(40);
         scriptText.Append(NS_LITERAL_STRING("..."));
       }
 
       csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT,
                                NS_ConvertUTF8toUTF16(asciiSpec),
                                scriptText,
                                aElement->GetScriptLineNumber());
+    }
+
+    if (!inlineOK) {
+      PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP blocked inline scripts (2)"));
       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/test/file_CSP_evalscript_main.html^headers^
+++ b/content/base/test/file_CSP_evalscript_main.html^headers^
@@ -1,2 +1,2 @@
 Cache-Control: no-cache
-X-Content-Security-Policy: allow 'self'
+X-Content-Security-Policy: default-src 'self'
--- a/content/base/test/file_CSP_evalscript_main.js
+++ b/content/base/test/file_CSP_evalscript_main.js
@@ -3,88 +3,103 @@
 function logResult(str, passed) {
   var elt = document.createElement('div');
   var color = passed ? "#cfc;" : "#fcc";
   elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
   elt.innerHTML = str;
   document.body.appendChild(elt);
 }
 
+window._testResults = {};
 
 // callback for when stuff is allowed by CSP
 var onevalexecuted = (function(window) {
     return function(shouldrun, what, data) {
+      window._testResults[what] = "ran";
       window.parent.scriptRan(shouldrun, what, data);
       logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
     };})(window);
 
 // callback for when stuff is blocked
 var onevalblocked = (function(window) {
     return function(shouldrun, what, data) {
+      window._testResults[what] = "blocked";
       window.parent.scriptBlocked(shouldrun, what, data);
       logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
     };})(window);
 
 
 // Defer until document is loaded so that we can write the pretty result boxes
 // out.
 addEventListener('load', function() {
 
-  // setTimeout(String) test  -- should pass
-  try {
-    setTimeout('onevalexecuted(false, "setTimeout(String)", "setTimeout with a string was enabled.");', 10);
-  } catch (e) {
-    onevalblocked(false, "setTimeout(String)",
-                  "setTimeout with a string was blocked");
+  // setTimeout(String) test -- mutate something in the window._testResults
+  // obj, then check it.
+  {
+    var str_setTimeoutWithStringRan = 'onevalexecuted(false, "setTimeout(String)", "setTimeout with a string was enabled.");';
+    function fcn_setTimeoutWithStringCheck() {
+      if (this._testResults["setTimeout(String)"] !== "ran") {
+        onevalblocked(false, "setTimeout(String)",
+                      "setTimeout with a string was blocked");
+      }
+    }
+    setTimeout(fcn_setTimeoutWithStringCheck.bind(window), 10);
+    setTimeout(str_setTimeoutWithStringRan, 10);
   }
 
-  // setTimeout(function) test  -- should pass
-  try {
-    setTimeout(function() {
-          onevalexecuted(true, "setTimeout(function)",
-                        "setTimeout with a function was enabled.")
-        }, 10);
-  } catch (e) {
-    onevalblocked(true, "setTimeout(function)",
-                  "setTimeout with a function was blocked");
+  // setTimeout(function) test -- mutate something in the window._testResults
+  // obj, then check it.
+  {
+    function fcn_setTimeoutWithFunctionRan() {
+      onevalexecuted(true, "setTimeout(function)",
+                    "setTimeout with a function was enabled.")
+    }
+    function fcn_setTimeoutWithFunctionCheck() {
+      if (this._testResults["setTimeout(function)"] !== "ran") {
+        onevalblocked(true, "setTimeout(function)",
+                      "setTimeout with a function was blocked");
+      }
+    }
+    setTimeout(fcn_setTimeoutWithFunctionRan.bind(window), 10);
+    setTimeout(fcn_setTimeoutWithFunctionCheck.bind(window), 10);
   }
 
-  // eval() test
+  // eval() test -- should throw exception as per spec
   try {
     eval('onevalexecuted(false, "eval(String)", "eval() was enabled.");');
   } catch (e) {
     onevalblocked(false, "eval(String)",
                   "eval() was blocked");
   }
 
-  // eval(foo,bar) test
+  // eval(foo,bar) test -- should throw exception as per spec
   try {
     eval('onevalexecuted(false, "eval(String,scope)", "eval() was enabled.");',1);
   } catch (e) {
     onevalblocked(false, "eval(String,object)",
                   "eval() with scope was blocked");
   }
 
-  // [foo,bar].sort(eval) test
+  // [foo,bar].sort(eval) test -- should throw exception as per spec
   try {
     ['onevalexecuted(false, "[String, obj].sort(eval)", "eval() was enabled.");',1].sort(eval);
   } catch (e) {
     onevalblocked(false, "[String, obj].sort(eval)",
                   "eval() with scope via sort was blocked");
   }
 
-  // [].sort.call([foo,bar], eval) test
+  // [].sort.call([foo,bar], eval) test -- should throw exception as per spec
   try {
     [].sort.call(['onevalexecuted(false, "[String, obj].sort(eval)", "eval() was enabled.");',1], eval);
   } catch (e) {
     onevalblocked(false, "[].sort.call([String, obj], eval)",
                   "eval() with scope via sort/call was blocked");
   }
 
-  // new Function() test
+  // new Function() test -- should throw exception as per spec
   try {
     var fcn = new Function('onevalexecuted(false, "new Function(String)", "new Function(String) was enabled.");');
     fcn();
   } catch (e) {
     onevalblocked(false, "new Function(String)",
                   "new Function(String) was blocked.");
   }
 
--- a/content/base/test/unit/test_cspreports.js
+++ b/content/base/test/unit/test_cspreports.js
@@ -40,27 +40,31 @@ function makeReportHandler(testpath, mes
 
     dump("GOT REPORT:\n" + JSON.stringify(reportObj) + "\n");
     dump("TESTPATH:    " + testpath + "\n");
     dump("EXPECTED:  \n" + JSON.stringify(expectedJSON) + "\n\n");
 
     for (var i in expectedJSON)
       do_check_eq(expectedJSON[i], reportObj['csp-report'][i]);
 
-    // self-destroy
     testsToFinish--;
     httpServer.registerPathHandler(testpath, null);
     if (testsToFinish < 1)
       httpServer.stop(do_test_finished);
     else
       do_test_finished();
   };
 }
 
-function makeTest(id, expectedJSON, callback) {
+/**
+ * Everything created by this assumes it will cause a report.  If you want to
+ * add a test here that will *not* cause a report to go out, you're gonna have
+ * to make sure the test cleans up after itself.
+ */
+function makeTest(id, expectedJSON, useReportOnlyPolicy, callback) {
   testsToFinish++;
   do_test_pending();
 
   // set up a new CSP instance for each test.
   var csp = Cc["@mozilla.org/contentsecuritypolicy;1"]
               .createInstance(Ci.nsIContentSecurityPolicy);
   var policy = "allow 'none'; " +
                "report-uri " + REPORT_SERVER_URI +
@@ -74,16 +78,19 @@ function makeTest(id, expectedJSON, call
   dump("Created test " + id + " : " + policy + "\n\n");
 
   // make the reports seem authentic by "binding" them to a channel.
   csp.scanRequestData(selfchan);
 
   // Load up the policy
   csp.refinePolicy(policy, selfuri, false);
 
+  // set as report-only if that's the case
+  if (useReportOnlyPolicy) csp.reportOnlyMode = true;
+
   // prime the report server
   var handler = makeReportHandler("/test" + id, "Test " + id, expectedJSON);
   httpServer.registerPathHandler("/test" + id, handler);
 
   //trigger the violation
   callback(csp);
 }
 
@@ -91,37 +98,87 @@ function run_test() {
   var selfuri = NetUtil.newURI(REPORT_SERVER_URI +
                                ":" + REPORT_SERVER_PORT +
                                "/foo/self");
 
   httpServer = new HttpServer();
   httpServer.start(REPORT_SERVER_PORT);
 
   // test that inline script violations cause a report.
-  makeTest(0, {"blocked-uri": "self"},
+  makeTest(0, {"blocked-uri": "self"}, false,
       function(csp) {
-        if(!csp.allowsInlineScript) {
-          // force the logging, since the getter doesn't.
-          csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
-                                  selfuri.asciiSpec,
-                                  "script sample",
-                                  0);
-        }
+        let inlineOK = true, oReportViolation = {};
+        inlineOK = csp.getAllowsInlineScript(oReportViolation);
+
+        // this is not a report only policy, so it better block inline scripts
+        do_check_false(inlineOK);
+        // ... and cause reports to go out
+        do_check_true(oReportViolation.value);
+
+        // force the logging, since the getter doesn't.
+        csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
+                                selfuri.asciiSpec,
+                                "script sample",
+                                0);
       });
 
-  makeTest(1, {"blocked-uri": "self"},
+  // test that eval violations cause a report.
+  makeTest(1, {"blocked-uri": "self"}, false,
       function(csp) {
-        if(!csp.allowsEval) {
-          // force the logging, since the getter doesn't.
-          csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
-                                  selfuri.asciiSpec,
-                                  "script sample",
-                                  1);
-        }
+        let evalOK = true, oReportViolation = {};
+        evalOK = csp.getAllowsEval(oReportViolation);
+
+        // this is not a report only policy, so it better block eval
+        do_check_false(evalOK);
+        // ... and cause reports to go out
+        do_check_true(oReportViolation.value);
+
+        // force the logging, since the getter doesn't.
+        csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
+                                selfuri.asciiSpec,
+                                "script sample",
+                                1);
       });
 
-  makeTest(2, {"blocked-uri": "http://blocked.test/foo.js"},
+  makeTest(2, {"blocked-uri": "http://blocked.test/foo.js"}, false,
       function(csp) {
+        // shouldLoad creates and sends out the report here.
         csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT,
                       NetUtil.newURI("http://blocked.test/foo.js"),
                       null, null, null, null);
       });
+
+  // test that inline script violations cause a report in report-only policy
+  makeTest(3, {"blocked-uri": "self"}, true,
+      function(csp) {
+        let inlineOK = true, oReportViolation = {};
+        inlineOK = csp.getAllowsInlineScript(oReportViolation);
+
+        // this is a report only policy, so it better allow inline scripts
+        do_check_true(inlineOK);
+        // ... but still cause reports to go out
+        do_check_true(oReportViolation.value);
+
+        // force the logging, since the getter doesn't.
+        csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
+                                selfuri.asciiSpec,
+                                "script sample",
+                                3);
+      });
+
+  // test that eval violations cause a report in report-only policy
+  makeTest(4, {"blocked-uri": "self"}, true,
+      function(csp) {
+        let evalOK = true, oReportViolation = {};
+        evalOK = csp.getAllowsEval(oReportViolation);
+
+        // this is a report only policy, so it better allow eval
+        do_check_true(evalOK);
+        // ... but still cause reports to go out
+        do_check_true(oReportViolation.value);
+
+        // force the logging, since the getter doesn't.
+        csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
+                                selfuri.asciiSpec,
+                                "script sample",
+                                4);
+      });
 }
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -664,21 +664,22 @@ nsEventListenerManager::SetEventHandler(
       return NS_ERROR_DOM_SECURITY_ERR;
     }
 
     nsCOMPtr<nsIContentSecurityPolicy> csp;
     rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (csp) {
-      bool inlineOK;
-      rv = csp->GetAllowsInlineScript(&inlineOK);
+      bool inlineOK = true;
+      bool reportViolations = false;
+      rv = csp->GetAllowsInlineScript(&reportViolations, &inlineOK);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      if ( !inlineOK ) {
+      if (reportViolations) {
         // gather information to log with violation report
         nsIURI* uri = doc->GetDocumentURI();
         nsAutoCString asciiSpec;
         if (uri)
           uri->GetAsciiSpec(asciiSpec);
         nsAutoString scriptSample, attr, tagName(NS_LITERAL_STRING("UNKNOWN"));
         aName->ToString(attr);
         nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mTarget));
@@ -688,16 +689,20 @@ nsEventListenerManager::SetEventHandler(
         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);
+      }
+
+      // return early if CSP wants us to block inline scripts
+      if (!inlineOK) {
         return NS_OK;
       }
     }
   }
 
   // This might be the first reference to this language in the global
   // We must init the language before we attempt to fetch its context.
   if (NS_FAILED(global->EnsureScriptEnvironment())) {
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -240,26 +240,42 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(aWindow->GetExtantDocument());
 
     if (doc) {
       nsCOMPtr<nsIContentSecurityPolicy> csp;
       nsresult rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
       NS_ENSURE_SUCCESS(rv, rv);
 
       if (csp) {
-        bool allowsEval;
-        // this call will send violation reports as warranted (and return true if
-        // reportOnly is set).
-        rv = csp->GetAllowsEval(&allowsEval);
+        bool allowsEval = true;
+        bool reportViolation = false;
+        rv = csp->GetAllowsEval(&reportViolation, &allowsEval);
         NS_ENSURE_SUCCESS(rv, rv);
 
+        if (reportViolation) {
+          // TODO : FIX DATA in violation report.
+          NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP");
+
+          // Get the calling location.
+          uint32_t lineNum = 0;
+          const char *fileName;
+          nsAutoCString aFileName;
+          if (nsJSUtils::GetCallingLocation(cx, &fileName, &lineNum)) {
+            aFileName.Assign(fileName);
+          } else {
+            aFileName.Assign("unknown");
+          }
+
+          csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
+                                  NS_ConvertUTF8toUTF16(aFileName),
+                                  scriptSample,
+                                  lineNum);
+        }
+
         if (!allowsEval) {
-          ::JS_ReportError(cx, "call to %s blocked by CSP",
-                            *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
-
           // Note: Our only caller knows to turn NS_ERROR_DOM_TYPE_ERR into NS_OK.
           return NS_ERROR_DOM_TYPE_ERR;
         }
       }
     } // if there's no document, we don't have to do anything.
 
     NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
 
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -163,32 +163,37 @@ nsresult nsJSThunk::EvaluateScript(nsICh
     nsresult rv;
 
     // CSP check: javascript: URIs disabled unless "inline" scripts are
     // allowed.
     nsCOMPtr<nsIContentSecurityPolicy> csp;
     rv = principal->GetCsp(getter_AddRefs(csp));
     NS_ENSURE_SUCCESS(rv, rv);
     if (csp) {
-		bool allowsInline;
-		rv = csp->GetAllowsInlineScript(&allowsInline);
-		NS_ENSURE_SUCCESS(rv, rv);
+        bool allowsInline = true;
+        bool reportViolations = false;
+        rv = csp->GetAllowsInlineScript(&reportViolations, &allowsInline);
+        NS_ENSURE_SUCCESS(rv, rv);
 
-      if (!allowsInline) {
-          // 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);
+        if (reportViolations) {
+            // 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);
+        }
+
+        //return early if inline scripts are not allowed
+        if (!allowsInline) {
           return NS_ERROR_DOM_RETVAL_UNDEFINED;
-      }
+        }
     }
 
     // Get the global object we should be running on.
     nsIScriptGlobalObject* global = GetGlobalObject(aChannel);
     if (!global) {
         return NS_ERROR_FAILURE;
     }
 
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -335,17 +335,17 @@ public:
   {
     AssertIsOnMainThread();
 
     nsIContentSecurityPolicy* csp = mWorkerPrivate->GetCSP();
     if (csp) {
       NS_NAMED_LITERAL_STRING(scriptSample,
          "Call to eval() or related function blocked by CSP.");
       csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
-                                mFileName, scriptSample, mLineNum);
+                               mFileName, scriptSample, mLineNum);
     }
 
     nsRefPtr<LogViolationDetailsResponseRunnable> response =
         new LogViolationDetailsResponseRunnable(mWorkerPrivate, mSyncQueueKey);
     if (!response->Dispatch(nullptr)) {
       NS_WARNING("Failed to dispatch response!");
     }
 
@@ -354,40 +354,38 @@ public:
 };
 
 JSBool
 ContentSecurityPolicyAllows(JSContext* aCx)
 {
   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
   worker->AssertIsOnWorkerThread();
 
-  if (worker->IsEvalAllowed()) {
-    return true;
+  if (worker->GetReportCSPViolations()) {
+    nsString fileName;
+    uint32_t lineNum = 0;
+
+    JSScript* script;
+    const char* file;
+    if (JS_DescribeScriptedCaller(aCx, &script, &lineNum) &&
+        (file = JS_GetScriptFilename(aCx, script))) {
+      fileName.AssignASCII(file);
+    } else {
+      JS_ReportPendingException(aCx);
+    }
+
+    nsRefPtr<LogViolationDetailsRunnable> runnable =
+        new LogViolationDetailsRunnable(worker, fileName, lineNum);
+
+    if (!runnable->Dispatch(aCx)) {
+      JS_ReportPendingException(aCx);
+    }
   }
 
-  nsString fileName;
-  uint32_t lineNum = 0;
-
-  JSScript* script;
-  const char* file;
-  if (JS_DescribeScriptedCaller(aCx, &script, &lineNum) &&
-      (file = JS_GetScriptFilename(aCx, script))) {
-    fileName.AssignASCII(file);
-  } else {
-    JS_ReportPendingException(aCx);
-  }
-
-  nsRefPtr<LogViolationDetailsRunnable> runnable =
-      new LogViolationDetailsRunnable(worker, fileName, lineNum);
-
-  if (!runnable->Dispatch(aCx)) {
-    JS_ReportPendingException(aCx);
-  }
-
-  return false;
+  return worker->IsEvalAllowed();
 }
 
 void
 CTypesActivityCallback(JSContext* aCx,
                        js::CTypesActivityType aType)
 {
   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
   worker->AssertIsOnWorkerThread();
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1833,27 +1833,29 @@ WorkerPrivateParent<Derived>::WorkerPriv
                                      bool aIsChromeWorker,
                                      const nsACString& aDomain,
                                      nsCOMPtr<nsPIDOMWindow>& aWindow,
                                      nsCOMPtr<nsIScriptContext>& aScriptContext,
                                      nsCOMPtr<nsIURI>& aBaseURI,
                                      nsCOMPtr<nsIPrincipal>& aPrincipal,
                                      nsCOMPtr<nsIChannel>& aChannel,
                                      nsCOMPtr<nsIContentSecurityPolicy>& aCSP,
-                                     bool aEvalAllowed)
+                                     bool aEvalAllowed,
+                                     bool aReportCSPViolations)
 : EventTarget(aParent ? aCx : NULL), mMutex("WorkerPrivateParent Mutex"),
   mCondVar(mMutex, "WorkerPrivateParent CondVar"),
   mMemoryReportCondVar(mMutex, "WorkerPrivateParent Memory Report CondVar"),
   mJSObject(aObject), mParent(aParent), mParentJSContext(aParentJSContext),
   mScriptURL(aScriptURL), mDomain(aDomain), mBusyCount(0),
   mParentStatus(Pending), mJSContextOptions(0),
   mJSRuntimeHeapSize(0), mJSWorkerAllocationThreshold(3),
   mGCZeal(0), mJSObjectRooted(false), mParentSuspended(false),
   mIsChromeWorker(aIsChromeWorker), mPrincipalIsSystem(false),
-  mMainThreadObjectsForgotten(false), mEvalAllowed(aEvalAllowed)
+  mMainThreadObjectsForgotten(false), mEvalAllowed(aEvalAllowed),
+  mReportCSPViolations(aReportCSPViolations)
 {
   MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivateParent);
 
   if (aWindow) {
     NS_ASSERTION(aWindow->IsInnerWindow(), "Should have inner window here!");
   }
 
   mWindow.swap(aWindow);
@@ -2421,21 +2423,23 @@ WorkerPrivate::WorkerPrivate(JSContext* 
                              const nsACString& aDomain,
                              nsCOMPtr<nsPIDOMWindow>& aWindow,
                              nsCOMPtr<nsIScriptContext>& aParentScriptContext,
                              nsCOMPtr<nsIURI>& aBaseURI,
                              nsCOMPtr<nsIPrincipal>& aPrincipal,
                              nsCOMPtr<nsIChannel>& aChannel,
                              nsCOMPtr<nsIContentSecurityPolicy>& aCSP,
                              bool aEvalAllowed,
+                             bool aReportCSPViolations,
                              bool aXHRParamsAllowed)
 : WorkerPrivateParent<WorkerPrivate>(aCx, aObject, aParent, aParentJSContext,
                                      aScriptURL, aIsChromeWorker, aDomain,
                                      aWindow, aParentScriptContext, aBaseURI,
-                                     aPrincipal, aChannel, aCSP, aEvalAllowed),
+                                     aPrincipal, aChannel, aCSP, aEvalAllowed,
+                                     aReportCSPViolations),
   mJSContext(nullptr), mErrorHandlerRecursionCount(0), mNextTimeoutId(1),
   mStatus(Pending), mSuspended(false), mTimerRunning(false),
   mRunningExpiredTimeouts(false), mCloseHandlerStarted(false),
   mCloseHandlerFinished(false), mMemoryReporterRunning(false),
   mBlockedForMemoryReporter(false), mXHRParamsAllowed(aXHRParamsAllowed)
 {
   MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate);
 }
@@ -2454,16 +2458,17 @@ WorkerPrivate::Create(JSContext* aCx, JS
   nsCOMPtr<nsIURI> baseURI;
   nsCOMPtr<nsIPrincipal> principal;
   nsCOMPtr<nsIScriptContext> scriptContext;
   nsCOMPtr<nsIDocument> document;
   nsCOMPtr<nsPIDOMWindow> window;
   nsCOMPtr<nsIContentSecurityPolicy> csp;
 
   bool evalAllowed = true;
+  bool reportEvalViolations = false;
 
   JSContext* parentContext;
 
   bool xhrParamsAllowed = false;
 
   if (aParent) {
     aParent->AssertIsOnWorkerThread();
 
@@ -2618,17 +2623,17 @@ WorkerPrivate::Create(JSContext* aCx, JS
       NS_ERROR("Must be chrome or have an domain!");
       return nullptr;
     }
 
     if (!GetContentSecurityPolicy(aCx, getter_AddRefs(csp))) {
       return nullptr;
     }
 
-    if (csp && NS_FAILED(csp->GetAllowsEval(&evalAllowed))) {
+    if (csp && NS_FAILED(csp->GetAllowsEval(&reportEvalViolations, &evalAllowed))) {
       NS_ERROR("CSP: failed to get allowsEval");
       return nullptr;
     }
   }
 
   size_t urlLength;
   const jschar* urlChars = JS_GetStringCharsZAndLength(aCx, aScriptURL,
                                                        &urlLength);
@@ -2677,17 +2682,18 @@ WorkerPrivate::Create(JSContext* aCx, JS
   if (NS_FAILED(rv)) {
     scriptloader::ReportLoadError(aCx, scriptURL, rv, !aParent);
     return nullptr;
   }
 
   nsRefPtr<WorkerPrivate> worker =
     new WorkerPrivate(aCx, aObj, aParent, parentContext, scriptURL,
                       aIsChromeWorker, domain, window, scriptContext, baseURI,
-                      principal, channel, csp, evalAllowed, xhrParamsAllowed);
+                      principal, channel, csp, evalAllowed, reportEvalViolations,
+                      xhrParamsAllowed);
 
   worker->SetIsDOMBinding();
   worker->SetWrapper(aObj);
 
   nsRefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(worker);
   if (!compiler->Dispatch(aCx)) {
     return nullptr;
   }
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -279,28 +279,30 @@ private:
   uint32_t mJSWorkerAllocationThreshold;
   uint8_t mGCZeal;
   bool mJSObjectRooted;
   bool mParentSuspended;
   bool mIsChromeWorker;
   bool mPrincipalIsSystem;
   bool mMainThreadObjectsForgotten;
   bool mEvalAllowed;
+  bool mReportCSPViolations;
 
 protected:
   WorkerPrivateParent(JSContext* aCx, JSObject* aObject, WorkerPrivate* aParent,
                       JSContext* aParentJSContext, const nsAString& aScriptURL,
                       bool aIsChromeWorker, const nsACString& aDomain,
                       nsCOMPtr<nsPIDOMWindow>& aWindow,
                       nsCOMPtr<nsIScriptContext>& aScriptContext,
                       nsCOMPtr<nsIURI>& aBaseURI,
                       nsCOMPtr<nsIPrincipal>& aPrincipal,
                       nsCOMPtr<nsIChannel>& aChannel,
                       nsCOMPtr<nsIContentSecurityPolicy>& aCSP,
-                      bool aEvalAllowed);
+                      bool aEvalAllowed,
+                      bool aReportCSPViolations);
 
   ~WorkerPrivateParent();
 
 private:
   Derived*
   ParentAsWorkerPrivate() const
   {
     return static_cast<Derived*>(const_cast<WorkerPrivateParent*>(this));
@@ -555,16 +557,22 @@ public:
   }
 
   void
   SetEvalAllowed(bool aEvalAllowed)
   {
     mEvalAllowed = aEvalAllowed;
   }
 
+  bool
+  GetReportCSPViolations() const
+  {
+    return mReportCSPViolations;
+  }
+
   LocationInfo&
   GetLocationInfo()
   {
     return mLocationInfo;
   }
 
   uint32_t
   GetJSContextOptions() const
@@ -889,17 +897,17 @@ private:
   WorkerPrivate(JSContext* aCx, JSObject* aObject, WorkerPrivate* aParent,
                 JSContext* aParentJSContext, const nsAString& aScriptURL,
                 bool aIsChromeWorker, const nsACString& aDomain,
                 nsCOMPtr<nsPIDOMWindow>& aWindow,
                 nsCOMPtr<nsIScriptContext>& aScriptContext,
                 nsCOMPtr<nsIURI>& aBaseURI, nsCOMPtr<nsIPrincipal>& aPrincipal,
                 nsCOMPtr<nsIChannel>& aChannel,
                 nsCOMPtr<nsIContentSecurityPolicy>& aCSP, bool aEvalAllowed,
-                bool aXHRParamsAllowed);
+                bool aReportCSPViolations, bool aXHRParamsAllowed);
 
   static bool
   GetContentSecurityPolicy(JSContext *aCx,
                            nsIContentSecurityPolicy** aCsp);
 
   bool
   Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue);