Bug 1181619 - Make sure we've entered a microtask before we call nsJSUtils::EvaluateString, and put those microtasks outside the relevant AutoEntryScripts so we report any possible exceptions before doing the microtask checkpoint. r=bholley, a=lizzard
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 09 Jul 2015 02:41:43 -0400
changeset 269052 cc5082dd3f73f5adf18a4e41764b061dbccfa771
parent 269051 a5973ed7f8538dfa1234d6c09332269ecff983cc
child 269053 81288e78a1d4cb2020d1232c7c137410c63a652d
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-esr52@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley, lizzard
bugs1181619
milestone41.0a2
Bug 1181619 - Make sure we've entered a microtask before we call nsJSUtils::EvaluateString, and put those microtasks outside the relevant AutoEntryScripts so we report any possible exceptions before doing the microtask checkpoint. r=bholley, a=lizzard
dom/base/crashtests/1181619.html
dom/base/crashtests/crashtests.list
dom/base/nsGlobalWindow.cpp
dom/base/nsJSUtils.cpp
dom/base/nsScriptLoader.cpp
dom/jsurl/nsJSProtocolHandler.cpp
dom/plugins/base/nsNPAPIPlugin.cpp
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/1181619.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<body>
+  <script>
+    var obs = new MutationObserver(function() {
+      // Just need something here to assert exception is not pending.  Any
+      // binding method will do.
+      console.log("hello");
+    });
+    obs.observe(document.body, { childList: true });
+  </script>
+  <script>
+    noSuchMethodYo();
+  </script>
+</body>
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -198,8 +198,9 @@ pref(dom.webcomponents.enabled,true) loa
 load 1026714.html
 pref(dom.webcomponents.enabled,true) load 1029710.html
 HTTP(..) load xhr_abortinprogress.html
 load xhr_empty_datauri.html
 load xhr_html_nullresponse.html
 load structured_clone_container_throws.html
 load 1154598.xhtml
 load 1157995.html
+load 1181619.html
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -12045,16 +12045,17 @@ nsGlobalWindow::RunTimeoutHandler(nsTime
     NS_ASSERTION(script, "timeout has no script nor handler text!");
 
     const char* filename = nullptr;
     uint32_t lineNo = 0;
     handler->GetLocation(&filename, &lineNo);
 
     // New script entry point required, due to the "Create a script" sub-step of
     // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
+    nsAutoMicroTask mt;
     AutoEntryScript entryScript(this, reason, true, aScx->GetNativeContext());
     entryScript.TakeOwnershipOfErrorReporting();
     JS::CompileOptions options(entryScript.cx());
     options.setFileAndLine(filename, lineNo)
            .setVersion(JSVERSION_DEFAULT);
     JS::Rooted<JSObject*> global(entryScript.cx(), FastGetGlobalJSObject());
     nsJSUtils::EvaluateString(entryScript.cx(), nsDependentString(script),
                               global, options);
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -169,26 +169,27 @@ nsJSUtils::EvaluateString(JSContext* aCx
   MOZ_ASSERT_IF(aCompileOptions.versionSet,
                 aCompileOptions.version != JSVERSION_UNKNOWN);
   MOZ_ASSERT_IF(aEvaluateOptions.coerceToString, !aCompileOptions.noScriptRval);
   MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
   MOZ_ASSERT(aSrcBuf.get());
   MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aEvaluationGlobal) ==
              aEvaluationGlobal);
   MOZ_ASSERT_IF(aOffThreadToken, aCompileOptions.noScriptRval);
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(nsContentUtils::IsInMicroTask());
 
   // Unfortunately, the JS engine actually compiles scripts with a return value
   // in a different, less efficient way.  Furthermore, it can't JIT them in many
   // cases.  So we need to be explicitly told whether the caller cares about the
   // return value.  Callers can do this by calling the other overload of
   // EvaluateString() which calls this function with
   // aCompileOptions.noScriptRval set to true.
   aRetValue.setUndefined();
 
-  nsAutoMicroTask mt;
   nsresult rv = NS_OK;
 
   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   NS_ENSURE_TRUE(ssm->ScriptAllowed(aEvaluationGlobal), NS_OK);
 
   bool ok = true;
   // Scope the JSAutoCompartment so that we can later wrap the return value
   // into the caller's cx.
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -1108,16 +1108,17 @@ nsScriptLoader::EvaluateScript(nsScriptL
 
   JSVersion version = JSVersion(aRequest->mJSVersion);
   if (version == JSVERSION_UNKNOWN) {
     return NS_OK;
   }
 
   // New script entry point required, due to the "Create a script" sub-step of
   // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
+  nsAutoMicroTask mt;
   AutoEntryScript entryScript(globalObject, "<script> element", true,
                               context->GetNativeContext());
   entryScript.TakeOwnershipOfErrorReporting();
   JS::Rooted<JSObject*> global(entryScript.cx(),
                                globalObject->GetGlobalJSObject());
 
   bool oldProcessingScriptTag = context->GetProcessingScriptTag();
   context->SetProcessingScriptTag(true);
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -244,16 +244,17 @@ nsresult nsJSThunk::EvaluateScript(nsICh
 
     nsCOMPtr<nsIScriptSecurityManager> securityManager;
     securityManager = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
     if (NS_FAILED(rv))
         return rv;
 
     // New script entry point required, due to the "Create a script" step of
     // http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol
+    nsAutoMicroTask mt;
     AutoEntryScript entryScript(innerGlobal, "javascript: URI", true,
                                 scriptContext->GetNativeContext());
     // We want to make sure we report any exceptions that happen before we
     // return, since whatever happens inside our execution shouldn't affect any
     // other scripts that might happen to be running.
     entryScript.TakeOwnershipOfErrorReporting();
     JSContext* cx = entryScript.cx();
     JS::Rooted<JSObject*> globalJSObject(cx, innerGlobal->GetGlobalJSObject());
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -1477,16 +1477,17 @@ bool
   nsIDocument *doc = GetDocumentFromNPP(npp);
   NS_ENSURE_TRUE(doc, false);
 
   nsGlobalWindow* win = static_cast<nsGlobalWindow*>(doc->GetInnerWindow());
   if (NS_WARN_IF(!win || !win->FastGetGlobalJSObject())) {
     return false;
   }
 
+  nsAutoMicroTask mt;
   dom::AutoEntryScript aes(win, "NPAPI NPN_evaluate");
   aes.TakeOwnershipOfErrorReporting();
   JSContext* cx = aes.cx();
 
   JS::Rooted<JSObject*> obj(cx, nsNPObjWrapper::GetNewOrUsed(npp, cx, npobj));
 
   if (!obj) {
     return false;