Bug 1025476 - Part 1: Add compulsory Init functions to AutoJSAPI. r=bholley
authorBob Owen <bobowencode@gmail.com>
Thu, 19 Jun 2014 08:21:14 +0100
changeset 210792 8d6cff589e8fcbdfcd96c460003aa1bfcf95008e
parent 210791 ac067ab7fd61d6bdd72e57649bb5fbc97e30f0e2
child 210793 20c915f8f7620777462ab02e9740899e6c0f2b8b
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1025476
milestone33.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 1025476 - Part 1: Add compulsory Init functions to AutoJSAPI. r=bholley
content/base/src/EventSource.cpp
content/base/src/WebSocket.cpp
content/base/src/nsContentUtils.cpp
content/base/src/nsDOMDataChannel.cpp
content/base/src/nsScriptLoader.cpp
content/media/webaudio/AudioContext.cpp
content/media/webaudio/AudioDestinationNode.cpp
content/media/webaudio/AudioProcessingEvent.cpp
content/media/webaudio/MediaBufferDecoder.cpp
content/media/webaudio/ScriptProcessorNode.cpp
dom/archivereader/ArchiveRequest.cpp
dom/base/Console.cpp
dom/base/MessagePort.cpp
dom/base/ScriptSettings.cpp
dom/base/ScriptSettings.h
dom/base/nsGlobalWindow.cpp
dom/base/nsJSEnvironment.cpp
dom/devicestorage/nsDeviceStorage.cpp
dom/events/EventListenerManager.cpp
dom/icc/src/Icc.cpp
dom/workers/WorkerPrivate.cpp
js/src/jsapi.cpp
js/src/jsapi.h
--- a/content/base/src/EventSource.cpp
+++ b/content/base/src/EventSource.cpp
@@ -1231,25 +1231,21 @@ EventSource::DispatchAllMessageEvents()
 
   mGoingToDispatchAllMessages = false;
 
   nsresult rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     return;
   }
 
-  // We need a parent object so that we can enter its compartment.
-  nsCOMPtr<nsIGlobalObject> parentObject = do_QueryInterface(GetParentObject());
-  if (NS_WARN_IF(!parentObject)) {
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.InitUsingWin(GetOwner()))) {
     return;
   }
-
-  AutoJSAPI jsapi;
   JSContext* cx = jsapi.cx();
-  JSAutoCompartment ac(cx, parentObject->GetGlobalJSObject());
 
   while (mMessagesToDispatch.GetSize() > 0) {
     nsAutoPtr<Message>
       message(static_cast<Message*>(mMessagesToDispatch.PopFront()));
 
     // Now we can turn our string into a jsval
     JS::Rooted<JS::Value> jsData(cx);
     {
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -860,24 +860,21 @@ WebSocket::CreateAndDispatchMessageEvent
                                          bool isBinary)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
   nsresult rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv))
     return NS_OK;
 
-  nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(GetOwner());
-  if (NS_WARN_IF(!globalObject)) {
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.InitUsingWin(GetOwner()))) {
     return NS_ERROR_FAILURE;
   }
-
-  AutoJSAPI jsapi;
   JSContext* cx = jsapi.cx();
-  JSAutoCompartment ac(cx, globalObject->GetGlobalJSObject());
 
   // Create appropriate JS object for message
   JS::Rooted<JS::Value> jsData(cx);
   if (isBinary) {
     if (mBinaryType == dom::BinaryType::Blob) {
       rv = nsContentUtils::CreateBlobBuffer(cx, aData, &jsData);
       NS_ENSURE_SUCCESS(rv, rv);
     } else if (mBinaryType == dom::BinaryType::Arraybuffer) {
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -6406,25 +6406,22 @@ nsContentUtils::GetContentSecurityPolicy
 }
 
 // static
 bool
 nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
                                   nsIDocument* aDocument)
 {
   NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
-  nsCOMPtr<nsIGlobalObject> globalObject =
-    do_QueryInterface(aDocument->GetWindow());
-  if (NS_WARN_IF(!globalObject)) {
-    return true;
-  }
 
   AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.InitUsingWin(aDocument->GetWindow()))) {
+    return true;
+  }
   JSContext* cx = jsapi.cx();
-  JSAutoCompartment ac(cx, globalObject->GetGlobalJSObject());
 
   // The pattern has to match the entire value.
   aPattern.Insert(NS_LITERAL_STRING("^(?:"), 0);
   aPattern.AppendLiteral(")$");
 
   JS::Rooted<JSObject*> re(cx,
     JS_NewUCRegExpObjectNoStatics(cx,
                                   static_cast<jschar*>(aPattern.BeginWriting()),
--- a/content/base/src/nsDOMDataChannel.cpp
+++ b/content/base/src/nsDOMDataChannel.cpp
@@ -378,24 +378,21 @@ nsDOMDataChannel::DoOnMessageAvailable(c
 
   LOG(("DoOnMessageAvailable%s\n",aBinary ? ((mBinaryType == DC_BINARY_TYPE_BLOB) ? " (blob)" : " (binary)") : ""));
 
   nsresult rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(GetOwner());
-  if (NS_WARN_IF(!globalObject)) {
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.InitUsingWin(GetOwner()))) {
     return NS_ERROR_FAILURE;
   }
-
-  AutoJSAPI jsapi;
   JSContext* cx = jsapi.cx();
-  JSAutoCompartment ac(cx, globalObject->GetGlobalJSObject());
 
   JS::Rooted<JS::Value> jsData(cx);
 
   if (aBinary) {
     if (mBinaryType == DC_BINARY_TYPE_BLOB) {
       rv = nsContentUtils::CreateBlobBuffer(cx, aData, &jsData);
       NS_ENSURE_SUCCESS(rv, rv);
     } else if (mBinaryType == DC_BINARY_TYPE_ARRAYBUFFER) {
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -882,26 +882,23 @@ nsScriptLoader::AttemptAsyncScriptParse(
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
   if (!globalObject) {
     return NS_ERROR_FAILURE;
   }
 
-  nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext();
-  if (!context) {
+  AutoJSAPI jsapi;
+  if (!jsapi.InitWithLegacyErrorReporting(globalObject)) {
     return NS_ERROR_FAILURE;
   }
 
-  AutoJSAPIWithErrorsReportedToWindow jsapi(context);
   JSContext* cx = jsapi.cx();
   JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
-  JSAutoCompartment ac(cx, global);
-
   JS::CompileOptions options(cx);
   FillCompileOptionsForRequest(aRequest, global, &options);
 
   if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptTextLength)) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<NotifyOffThreadScriptLoadCompletedRunnable> runnable =
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -432,16 +432,17 @@ AudioContext::Listener()
 }
 
 void
 AudioContext::DecodeAudioData(const ArrayBuffer& aBuffer,
                               DecodeSuccessCallback& aSuccessCallback,
                               const Optional<OwningNonNull<DecodeErrorCallback> >& aFailureCallback)
 {
   AutoJSAPI jsapi;
+  jsapi.Init();
   JSContext* cx = jsapi.cx();
   JSAutoCompartment ac(cx, aBuffer.Obj());
 
   aBuffer.ComputeLengthAndData();
 
   // Neuter the array buffer
   size_t length = aBuffer.Length();
   JS::RootedObject obj(cx, aBuffer.Obj());
--- a/content/media/webaudio/AudioDestinationNode.cpp
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -121,25 +121,21 @@ public:
   void FireOfflineCompletionEvent(AudioDestinationNode* aNode)
   {
     AudioContext* context = aNode->Context();
     context->Shutdown();
     // Shutdown drops self reference, but the context is still referenced by aNode,
     // which is strongly referenced by the runnable that called
     // AudioDestinationNode::FireOfflineCompletionEvent.
 
-    // We need the global for the context so that we can enter its compartment.
-    JSObject* global = context->GetGlobalJSObject();
-    if (NS_WARN_IF(!global)) {
+    AutoJSAPI jsapi;
+    if (NS_WARN_IF(!jsapi.InitUsingWin(aNode->GetOwner()))) {
       return;
     }
-
-    AutoJSAPI jsapi;
     JSContext* cx = jsapi.cx();
-    JSAutoCompartment ac(cx, global);
 
     // Create the input buffer
     ErrorResult rv;
     nsRefPtr<AudioBuffer> renderedBuffer =
       AudioBuffer::Create(context, mInputChannels.Length(),
                           mLength, mSampleRate, cx, rv);
     if (rv.Failed()) {
       return;
--- a/content/media/webaudio/AudioProcessingEvent.cpp
+++ b/content/media/webaudio/AudioProcessingEvent.cpp
@@ -36,26 +36,22 @@ AudioProcessingEvent::WrapObject(JSConte
 {
   return AudioProcessingEventBinding::Wrap(aCx, this);
 }
 
 already_AddRefed<AudioBuffer>
 AudioProcessingEvent::LazilyCreateBuffer(uint32_t aNumberOfChannels,
                                          ErrorResult& aRv)
 {
-  // We need the global for the context so that we can enter its compartment.
-  JSObject* global = mNode->Context()->GetGlobalJSObject();
-  if (NS_WARN_IF(!global)) {
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.InitUsingWin(mNode->GetOwner()))) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
-
-  AutoJSAPI jsapi;
   JSContext* cx = jsapi.cx();
-  JSAutoCompartment ac(cx, global);
 
   nsRefPtr<AudioBuffer> buffer =
     AudioBuffer::Create(mNode->Context(), aNumberOfChannels,
                         mNode->BufferSize(),
                         mNode->Context()->SampleRate(), cx, aRv);
   MOZ_ASSERT(buffer || aRv.ErrorCode() == NS_ERROR_OUT_OF_MEMORY);
   return buffer.forget();
 }
--- a/content/media/webaudio/MediaBufferDecoder.cpp
+++ b/content/media/webaudio/MediaBufferDecoder.cpp
@@ -408,25 +408,21 @@ MediaDecodeTask::CallbackTheResult()
 }
 
 bool
 WebAudioDecodeJob::AllocateBuffer()
 {
   MOZ_ASSERT(!mOutput);
   MOZ_ASSERT(NS_IsMainThread());
 
-  // We need the global for the context so that we can enter its compartment.
-  JSObject* global = mContext->GetGlobalJSObject();
-  if (NS_WARN_IF(!global)) {
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.InitUsingWin(mContext->GetOwner()))) {
     return false;
   }
-
-  AutoJSAPI jsapi;
   JSContext* cx = jsapi.cx();
-  JSAutoCompartment ac(cx, global);
 
   // Now create the AudioBuffer
   ErrorResult rv;
   mOutput = AudioBuffer::Create(mContext, mChannelBuffers.Length(),
                                 mWriteIndex, mContext->SampleRate(), cx, rv);
   if (rv.Failed()) {
     return false;
   }
--- a/content/media/webaudio/ScriptProcessorNode.cpp
+++ b/content/media/webaudio/ScriptProcessorNode.cpp
@@ -397,25 +397,21 @@ private:
           // this function.
           MutexAutoLock lock(mStream->Engine()->NodeMutex());
           node = static_cast<ScriptProcessorNode*>(mStream->Engine()->Node());
         }
         if (!node || !node->Context()) {
           return NS_OK;
         }
 
-        // Get the global for the context so that we can enter its compartment.
-        JSObject* global = node->Context()->GetGlobalJSObject();
-        if (NS_WARN_IF(!global)) {
+        AutoJSAPI jsapi;
+        if (NS_WARN_IF(!jsapi.InitUsingWin(node->GetOwner()))) {
           return NS_OK;
         }
-
-        AutoJSAPI jsapi;
         JSContext* cx = jsapi.cx();
-        JSAutoCompartment ac(cx, global);
 
         // Create the input buffer
         nsRefPtr<AudioBuffer> inputBuffer;
         if (!mNullInput) {
           ErrorResult rv;
           inputBuffer =
             AudioBuffer::Create(node->Context(), mInputChannels.Length(),
                                 node->BufferSize(),
--- a/dom/archivereader/ArchiveRequest.cpp
+++ b/dom/archivereader/ArchiveRequest.cpp
@@ -126,29 +126,22 @@ ArchiveRequest::ReaderReady(nsTArray<nsC
 {
   if (NS_FAILED(aStatus)) {
     FireError(aStatus);
     return NS_OK;
   }
 
   nsresult rv;
 
-  nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(GetOwner());
-  if (NS_WARN_IF(!globalObject)) {
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.InitUsingWin(GetOwner()))) {
     return NS_ERROR_UNEXPECTED;
   }
-
-  AutoJSAPI jsapi;
   JSContext* cx = jsapi.cx();
 
-  JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
-  NS_ASSERTION(global, "Failed to get global object!");
-
-  JSAutoCompartment ac(cx, global);
-
   JS::Rooted<JS::Value> result(cx);
   switch (mOperation) {
     case GetFilenames:
       rv = GetFilenamesResult(cx, result.address(), aFileList);
       break;
 
     case GetFile:
       rv = GetFileResult(cx, &result, aFileList);
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -319,27 +319,28 @@ private:
   RunConsole() MOZ_OVERRIDE
   {
     // Walk up to our containing page
     WorkerPrivate* wp = mWorkerPrivate;
     while (wp->GetParent()) {
       wp = wp->GetParent();
     }
 
-    AutoJSAPI jsapi;
-    JSContext* cx = jsapi.cx();
-    ClearException ce(cx);
-
     nsPIDOMWindow* window = wp->GetWindow();
     NS_ENSURE_TRUE_VOID(window);
 
     nsRefPtr<nsGlobalWindow> win = static_cast<nsGlobalWindow*>(window);
     NS_ENSURE_TRUE_VOID(win);
 
-    JSAutoCompartment ac(cx, win->GetWrapperPreserveColor());
+    AutoJSAPI jsapi;
+    if (NS_WARN_IF(!jsapi.Init(win))) {
+      return;
+    }
+    JSContext* cx = jsapi.cx();
+    ClearException ce(cx);
 
     ErrorResult error;
     nsRefPtr<Console> console = win->GetConsole(error);
     if (error.Failed()) {
       NS_WARNING("Failed to get console from the window.");
       return;
     }
 
@@ -431,27 +432,28 @@ private:
   RunConsole() MOZ_OVERRIDE
   {
     // Walk up to our containing page
     WorkerPrivate* wp = mWorkerPrivate;
     while (wp->GetParent()) {
       wp = wp->GetParent();
     }
 
-    AutoJSAPI jsapi;
-    JSContext* cx = jsapi.cx();
-    ClearException ce(cx);
-
     nsPIDOMWindow* window = wp->GetWindow();
     NS_ENSURE_TRUE_VOID(window);
 
     nsRefPtr<nsGlobalWindow> win = static_cast<nsGlobalWindow*>(window);
     NS_ENSURE_TRUE_VOID(win);
 
-    JSAutoCompartment ac(cx, win->GetWrapperPreserveColor());
+    AutoJSAPI jsapi;
+    if (NS_WARN_IF(!jsapi.Init(win))) {
+      return;
+    }
+    JSContext* cx = jsapi.cx();
+    ClearException ce(cx);
 
     ErrorResult error;
     nsRefPtr<Console> console = win->GetConsole(error);
     if (error.Failed()) {
       NS_WARNING("Failed to get console from the window.");
       return;
     }
 
--- a/dom/base/MessagePort.cpp
+++ b/dom/base/MessagePort.cpp
@@ -267,24 +267,21 @@ PopulateMessagePortList(MessagePortBase*
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 PostMessageRunnable::Run()
 {
   MOZ_ASSERT(mPort);
 
-  nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(mPort->GetOwner());
-  if (NS_WARN_IF(!globalObject)) {
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.Init(mPort->GetParentObject()))) {
     return NS_ERROR_UNEXPECTED;
   }
-
-  AutoJSAPI jsapi;
   JSContext* cx = jsapi.cx();
-  JSAutoCompartment ac(cx, globalObject->GetGlobalJSObject());
 
   // Deserialize the structured clone data
   JS::Rooted<JS::Value> messageData(cx);
   StructuredCloneInfo scInfo;
   scInfo.mEvent = this;
   scInfo.mPort = mPort;
 
   if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -9,16 +9,18 @@
 #include "mozilla/Assertions.h"
 
 #include "jsapi.h"
 #include "xpcpublic.h"
 #include "nsIGlobalObject.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsContentUtils.h"
+#include "nsGlobalWindow.h"
+#include "nsPIDOMWindow.h"
 #include "nsTArray.h"
 #include "nsJSUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 static mozilla::ThreadLocal<ScriptSettingsStackEntry*> sScriptSettingsTLS;
 
@@ -216,57 +218,110 @@ FindJSContext(nsIGlobalObject* aGlobalOb
   }
   if (!cx) {
     cx = nsContentUtils::GetSafeJSContext();
   }
   return cx;
 }
 
 AutoJSAPI::AutoJSAPI()
-  : mCx(nsContentUtils::GetDefaultJSContextForThread())
+  : mCx(nullptr)
 {
-  if (NS_IsMainThread()) {
-    mCxPusher.construct(mCx);
-  }
-
-  // Leave the cx in a null compartment.
-  mNullAc.construct(mCx);
 }
 
-AutoJSAPI::AutoJSAPI(JSContext *aCx, bool aIsMainThread, bool aSkipNullAc)
-  : mCx(aCx)
+void
+AutoJSAPI::InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread)
 {
-  MOZ_ASSERT_IF(aIsMainThread, NS_IsMainThread());
+  mCx = aCx;
   if (aIsMainThread) {
     mCxPusher.construct(mCx);
   }
 
-  // In general we want to leave the cx in a null compartment, but we let
-  // subclasses skip this if they plan to immediately enter a compartment.
-  if (!aSkipNullAc) {
-    mNullAc.construct(mCx);
-  }
+  mAutoNullableCompartment.construct(mCx, aGlobal);
+}
+
+AutoJSAPI::AutoJSAPI(nsIGlobalObject* aGlobalObject,
+                     bool aIsMainThread,
+                     JSContext* aCx)
+{
+  MOZ_ASSERT(aGlobalObject);
+  MOZ_ASSERT(aGlobalObject->GetGlobalJSObject(), "Must have a JS global");
+  MOZ_ASSERT(aCx);
+  MOZ_ASSERT_IF(aIsMainThread, NS_IsMainThread());
+
+  InitInternal(aGlobalObject->GetGlobalJSObject(), aCx, aIsMainThread);
+}
+
+void
+AutoJSAPI::Init()
+{
+  MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once");
+
+  InitInternal(/* aGlobal */ nullptr,
+               nsContentUtils::GetDefaultJSContextForThread(),
+               NS_IsMainThread());
 }
 
-AutoJSAPIWithErrorsReportedToWindow::AutoJSAPIWithErrorsReportedToWindow(nsIScriptContext* aScx)
-  : AutoJSAPI(aScx->GetNativeContext(), /* aIsMainThread = */ true)
+bool
+AutoJSAPI::Init(nsIGlobalObject* aGlobalObject, JSContext* aCx)
 {
+  MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once");
+  MOZ_ASSERT(aCx);
+
+  if (NS_WARN_IF(!aGlobalObject)) {
+    return false;
+  }
+
+  JSObject* global = aGlobalObject->GetGlobalJSObject();
+  if (NS_WARN_IF(!global)) {
+    return false;
+  }
+
+  InitInternal(global, aCx, NS_IsMainThread());
+  return true;
+}
+
+bool
+AutoJSAPI::Init(nsIGlobalObject* aGlobalObject)
+{
+  return Init(aGlobalObject, nsContentUtils::GetDefaultJSContextForThread());
 }
 
-AutoJSAPIWithErrorsReportedToWindow::AutoJSAPIWithErrorsReportedToWindow(nsIGlobalObject* aGlobalObject)
-  : AutoJSAPI(FindJSContext(aGlobalObject), /* aIsMainThread = */ true)
+bool
+AutoJSAPI::InitWithLegacyErrorReporting(nsIGlobalObject* aGlobalObject)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  return Init(aGlobalObject, FindJSContext(aGlobalObject));
+}
+
+bool
+AutoJSAPI::InitUsingWin(nsPIDOMWindow* aWindow, JSContext* aCx)
 {
+  return Init(static_cast<nsGlobalWindow*>(aWindow), aCx);
+}
+
+bool
+AutoJSAPI::InitUsingWin(nsPIDOMWindow* aWindow)
+{
+  return Init(static_cast<nsGlobalWindow*>(aWindow));
+}
+
+bool
+AutoJSAPI::InitWithLegacyErrorReportingUsingWin(nsPIDOMWindow* aWindow)
+{
+  return InitWithLegacyErrorReporting(static_cast<nsGlobalWindow*>(aWindow));
 }
 
 AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
                                  bool aIsMainThread,
                                  JSContext* aCx)
-  : AutoJSAPI(aCx ? aCx : FindJSContext(aGlobalObject), aIsMainThread, /* aSkipNullAc = */ true)
+  : AutoJSAPI(aGlobalObject, aIsMainThread,
+              aCx ? aCx : FindJSContext(aGlobalObject))
   , ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ true)
-  , mAc(cx(), aGlobalObject->GetGlobalJSObject())
   , mWebIDLCallerPrincipal(nullptr)
 {
   MOZ_ASSERT(aGlobalObject);
   MOZ_ASSERT_IF(!aCx, aIsMainThread); // cx is mandatory off-main-thread.
   MOZ_ASSERT_IF(aCx && aIsMainThread, aCx == FindJSContext(aGlobalObject));
 }
 
 AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject)
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -11,17 +11,17 @@
 
 #include "nsCxPusher.h"
 #include "MainThreadUtils.h"
 #include "nsIGlobalObject.h"
 #include "nsIPrincipal.h"
 
 #include "mozilla/Maybe.h"
 
-class nsIGlobalObject;
+class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
 /*
  * System-wide setup/teardown routines. Init and Destroy should be invoked
  * once each, at startup and shutdown (respectively).
  */
@@ -87,22 +87,24 @@ private:
   ScriptSettingsStackEntry *mOlder;
 };
 
 /*
  * For any interaction with JSAPI, an AutoJSAPI (or one of its subclasses)
  * must be on the stack.
  *
  * This base class should be instantiated as-is when the caller wants to use
- * JSAPI but doesn't expect to run script. Its current duties are as-follows:
+ * JSAPI but doesn't expect to run script. The caller must then call one of its
+ * Init functions before being able to access the JSContext through cx().
+ * Its current duties are as-follows (see individual Init comments for details):
  *
  * * Grabbing an appropriate JSContext, and, on the main thread, pushing it onto
  *   the JSContext stack.
- * * Entering a null compartment, so that the consumer is forced to select a
- *   compartment to enter before manipulating objects.
+ * * Entering an initial (possibly null) compartment, to ensure that the
+ *   previously entered compartment for that JSContext is not used by mistake.
  *
  * Additionally, the following duties are planned, but not yet implemented:
  *
  * * De-poisoning the JSRuntime to allow manipulation of JSAPI. We can't
  *   actually implement this poisoning until all the JSContext pushing in the
  *   system goes through AutoJSAPI (see bug 951991). For now, this de-poisoning
  *   effectively corresponds to having a non-null cx on the stack.
  * * Reporting any exceptions left on the JSRuntime, unless the caller steals
@@ -116,47 +118,74 @@ private:
  * should be used, which does additional manipulation of the script settings
  * stack. In bug 991758, we'll add hard invariants to SpiderMonkey, such that
  * any attempt to run script without an AutoEntryScript on the stack will
  * fail. This prevents system code from accidentally triggering script
  * execution at inopportune moments via surreptitious getters and proxies.
  */
 class AutoJSAPI {
 public:
-  // Public constructor for use when the base class is constructed as-is. It
-  // uses the SafeJSContext (or worker equivalent), and enters a null
-  // compartment.
+  // Trivial constructor. One of the Init functions must be called before
+  // accessing the JSContext through cx().
   AutoJSAPI();
-  JSContext* cx() const { return mCx; }
+
+  // This uses the SafeJSContext (or worker equivalent), and enters a null
+  // compartment, so that the consumer is forced to select a compartment to
+  // enter before manipulating objects.
+  void Init();
+
+  // This uses the SafeJSContext (or worker equivalent), and enters the
+  // compartment of aGlobalObject.
+  // If aGlobalObject or its associated JS global are null then it returns
+  // false and use of cx() will cause an assertion.
+  bool Init(nsIGlobalObject* aGlobalObject);
+
+  // Unsurprisingly, this uses aCx and enters the compartment of aGlobalObject.
+  // If aGlobalObject or its associated JS global are null then it returns
+  // false and use of cx() will cause an assertion.
+  // If aCx is null it will cause an assertion.
+  bool Init(nsIGlobalObject* aGlobalObject, JSContext* aCx);
+
+  // This may only be used on the main thread.
+  // This attempts to use the JSContext associated with aGlobalObject, otherwise
+  // it uses the SafeJSContext. It then enters the compartment of aGlobalObject.
+  // This means that existing error reporting mechanisms that use the JSContext
+  // to find the JSErrorReporter should still work as before.
+  // We should be able to remove this around bug 981198.
+  // If aGlobalObject or its associated JS global are null then it returns
+  // false and use of cx() will cause an assertion.
+  bool InitWithLegacyErrorReporting(nsIGlobalObject* aGlobalObject);
+
+  // Convenience functions to take an nsPIDOMWindow*, when it is more easily
+  // available than an nsIGlobalObject.
+  bool InitUsingWin(nsPIDOMWindow* aWindow);
+  bool InitUsingWin(nsPIDOMWindow* aWindow, JSContext* aCx);
+  bool InitWithLegacyErrorReportingUsingWin(nsPIDOMWindow* aWindow);
+
+  JSContext* cx() const {
+    MOZ_ASSERT(mCx, "Must call Init before using an AutoJSAPI");
+    return mCx;
+  }
 
   bool CxPusherIsStackTop() { return mCxPusher.ref().IsStackTop(); }
 
 protected:
   // Protected constructor, allowing subclasses to specify a particular cx to
-  // be used.
-  AutoJSAPI(JSContext *aCx, bool aIsMainThread, bool aSkipNullAC = false);
+  // be used. This constructor initialises the AutoJSAPI, so Init must NOT be
+  // called on subclasses that use this.
+  // If aGlobalObject, its associated JS global or aCx are null this will cause
+  // an assertion, as will setting aIsMainThread incorrectly.
+  AutoJSAPI(nsIGlobalObject* aGlobalObject, bool aIsMainThread, JSContext* aCx);
 
 private:
   mozilla::Maybe<AutoCxPusher> mCxPusher;
-  mozilla::Maybe<JSAutoNullCompartment> mNullAc;
+  mozilla::Maybe<JSAutoNullableCompartment> mAutoNullableCompartment;
   JSContext *mCx;
-};
 
-// Note - the ideal way to implement this is with an accessor on AutoJSAPI
-// that lets us select the error reporting target. But at present,
-// implementing it that way would require us to destroy and reconstruct
-// mCxPusher, which is pretty wasteful. So we do this for now, since it should
-// be pretty easy to switch things over later.
-//
-// This should only be used on the main thread.
-class AutoJSAPIWithErrorsReportedToWindow : public AutoJSAPI {
-  public:
-    AutoJSAPIWithErrorsReportedToWindow(nsIScriptContext* aScx);
-    // Equivalent to AutoJSAPI if aGlobal is not a Window.
-    AutoJSAPIWithErrorsReportedToWindow(nsIGlobalObject* aGlobalObject);
+  void InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread);
 };
 
 /*
  * A class that represents a new script entry point.
  */
 class AutoEntryScript : public AutoJSAPI,
                         protected ScriptSettingsStackEntry {
 public:
@@ -165,17 +194,16 @@ public:
                   // Note: aCx is mandatory off-main-thread.
                   JSContext* aCx = nullptr);
 
   void SetWebIDLCallerPrincipal(nsIPrincipal *aPrincipal) {
     mWebIDLCallerPrincipal = aPrincipal;
   }
 
 private:
-  JSAutoCompartment mAc;
   // It's safe to make this a weak pointer, since it's the subject principal
   // when we go on the stack, so can't go away until after we're gone.  In
   // particular, this is only used from the CallSetup constructor, and only in
   // the aIsJSImplementedWebIDL case.  And in that case, the subject principal
   // is the principal of the callee function that is part of the CallArgs just a
   // bit up the stack, and which will outlive us.  So we know the principal
   // can't go away until then either.
   nsIPrincipal* mWebIDLCallerPrincipal;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2354,16 +2354,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
   // clear smartcard events, our document has gone away.
   if (mCrypto && XRE_GetProcessType() != GeckoProcessType_Content) {
     nsresult rv = mCrypto->SetEnableSmartCardEvents(false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 #endif
 
   AutoJSAPI jsapi;
+  jsapi.Init();
   JSContext *cx = jsapi.cx();
 
   if (!mDoc) {
     // First document load.
 
     // Get our private root. If it is equal to us, then we need to
     // attach our global key bindings that handles browser scrolling
     // and other browser commands.
@@ -8000,16 +8001,17 @@ NS_IMETHODIMP
 PostMessageEvent::Run()
 {
   NS_ABORT_IF_FALSE(mTargetWindow->IsOuterWindow(),
                     "should have been passed an outer window!");
   NS_ABORT_IF_FALSE(!mSource || mSource->IsOuterWindow(),
                     "should have been passed an outer window!");
 
   AutoJSAPI jsapi;
+  jsapi.Init();
   JSContext* cx = jsapi.cx();
 
   // If we bailed before this point we're going to leak mMessage, but
   // that's probably better than crashing.
 
   nsRefPtr<nsGlobalWindow> targetWindow;
   if (mTargetWindow->IsClosedOrClosing() ||
       !(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1590,16 +1590,17 @@ static const JSFunctionSpec JProfFunctio
 
 #endif /* defined(MOZ_JPROF) */
 
 nsresult
 nsJSContext::InitClasses(JS::Handle<JSObject*> aGlobalObj)
 {
   JSOptionChangedCallback(js_options_dot_str, this);
   AutoJSAPI jsapi;
+  jsapi.Init();
   JSContext* cx = jsapi.cx();
   JSAutoCompartment ac(cx, aGlobalObj);
 
   // Attempt to initialize profiling functions
   ::JS_DefineProfilingFunctions(cx, aGlobalObj);
 
 #ifdef NS_TRACE_MALLOC
   if (nsContentUtils::IsCallerChrome()) {
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -1719,25 +1719,21 @@ nsIFileToJsval(nsPIDOMWindow* aWindow, D
   return InterfaceToJsval(aWindow, blob, &NS_GET_IID(nsIDOMBlob));
 }
 
 JS::Value StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
 
-  JSObject* global =
-    static_cast<nsGlobalWindow*>(aWindow)->GetWrapperPreserveColor();
-  if (NS_WARN_IF(!global)) {
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.InitUsingWin(aWindow))) {
     return JSVAL_NULL;
   }
-
-  AutoJSAPI jsapi;
   JSContext* cx = jsapi.cx();
-  JSAutoCompartment ac(cx, global);
 
   JS::Rooted<JS::Value> result(cx);
   if (!xpc::StringToJsval(cx, aString, &result)) {
     return JSVAL_NULL;
   }
 
   return result;
 }
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -770,22 +770,22 @@ EventListenerManager::CompileEventHandle
              "What is there to compile?");
 
   nsresult result = NS_OK;
   nsCOMPtr<nsIDocument> doc;
   nsCOMPtr<nsIScriptGlobalObject> global =
     GetScriptGlobalAndDocument(getter_AddRefs(doc));
   NS_ENSURE_STATE(global);
 
-  nsIScriptContext* context = global->GetScriptContext();
-  NS_ENSURE_STATE(context);
-
   // Activate JSAPI, and make sure that exceptions are reported on the right
   // Window.
-  AutoJSAPIWithErrorsReportedToWindow jsapi(context);
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(global))) {
+    return NS_ERROR_UNEXPECTED;
+  }
   JSContext* cx = jsapi.cx();
 
   nsCOMPtr<nsIAtom> typeAtom = aListener->mTypeAtom;
   nsIAtom* attrName = typeAtom;
 
   // Flag us as not a string so we don't keep trying to compile strings which
   // can't be compiled.
   aListener->mHandlerIsString = false;
@@ -846,17 +846,17 @@ EventListenerManager::CompileEventHandle
   JSAddonId *addonId = MapURIToAddonID(uri);
 
   // Wrap the event target, so that we can use it as the scope for the event
   // handler. Note that mTarget is different from aElement in the <body> case,
   // where mTarget is a Window.
   //
   // The wrapScope doesn't really matter here, because the target will create
   // its reflector in the proper scope, and then we'll enter that compartment.
-  JS::Rooted<JSObject*> wrapScope(cx, context->GetWindowProxy());
+  JS::Rooted<JSObject*> wrapScope(cx, global->GetGlobalJSObject());
   JS::Rooted<JS::Value> v(cx);
   {
     JSAutoCompartment ac(cx, wrapScope);
     nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v,
                                              /* aAllowWrapping = */ false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
--- a/dom/icc/src/Icc.cpp
+++ b/dom/icc/src/Icc.cpp
@@ -43,23 +43,21 @@ nsresult
 Icc::NotifyEvent(const nsAString& aName)
 {
   return DispatchTrustedEvent(aName);
 }
 
 nsresult
 Icc::NotifyStkEvent(const nsAString& aName, const nsAString& aMessage)
 {
-  nsresult rv;
-  nsIScriptContext* sc = GetContextForEventHandlers(&rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  AutoJSAPIWithErrorsReportedToWindow jsapi(sc);
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReportingUsingWin(GetOwner()))) {
+    return NS_ERROR_UNEXPECTED;
+  }
   JSContext* cx = jsapi.cx();
-  JSAutoCompartment ac(cx, sc->GetWindowProxyPreserveColor());
   JS::Rooted<JS::Value> value(cx);
 
   if (!aMessage.IsEmpty()) {
     nsCOMPtr<nsIJSON> json(new nsJSON());
     nsresult rv = json->DecodeToJSVal(aMessage, cx, &value);
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
     value = JS::NullValue();
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -3130,20 +3130,21 @@ WorkerPrivateParent<Derived>::BroadcastE
   for (size_t index = 0; index < sharedWorkers.Length(); index++) {
     nsRefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
 
     // May be null.
     nsPIDOMWindow* window = sharedWorker->GetOwner();
 
     size_t actionsIndex = windowActions.LastIndexOf(WindowAction(window));
 
-    nsIGlobalObject* global = sharedWorker->GetParentObject();
-    AutoJSAPIWithErrorsReportedToWindow jsapi(global);
+    AutoJSAPI jsapi;
+    if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReportingUsingWin(sharedWorker->GetOwner()))) {
+      continue;
+    }
     JSContext* cx = jsapi.cx();
-    JSAutoCompartment ac(cx, global->GetGlobalJSObject());
 
     RootedDictionary<ErrorEventInit> errorInit(aCx);
     errorInit.mBubbles = false;
     errorInit.mCancelable = true;
     errorInit.mMessage = aMessage;
     errorInit.mFilename = aFilename;
     errorInit.mLineno = aLineNumber;
     errorInit.mColno = aColumnNumber;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -977,25 +977,30 @@ JSAutoCompartment::JSAutoCompartment(JSC
     cx_->enterCompartment(target->compartment());
 }
 
 JSAutoCompartment::~JSAutoCompartment()
 {
     cx_->leaveCompartment(oldCompartment_);
 }
 
-JSAutoNullCompartment::JSAutoNullCompartment(JSContext *cx)
+JSAutoNullableCompartment::JSAutoNullableCompartment(JSContext *cx,
+                                                     JSObject *targetOrNull)
   : cx_(cx),
     oldCompartment_(cx->compartment())
 {
     AssertHeapIsIdleOrIterating(cx_);
-    cx_->enterNullCompartment();
-}
-
-JSAutoNullCompartment::~JSAutoNullCompartment()
+    if (targetOrNull) {
+        cx_->enterCompartment(targetOrNull->compartment());
+    } else {
+        cx_->enterNullCompartment();
+    }
+}
+
+JSAutoNullableCompartment::~JSAutoNullableCompartment()
 {
     cx_->leaveCompartment(oldCompartment_);
 }
 
 JS_PUBLIC_API(void)
 JS_SetCompartmentPrivate(JSCompartment *compartment, void *data)
 {
     compartment->data = data;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1694,23 +1694,23 @@ class JS_PUBLIC_API(JSAutoCompartment)
     JSContext *cx_;
     JSCompartment *oldCompartment_;
   public:
     JSAutoCompartment(JSContext *cx, JSObject *target);
     JSAutoCompartment(JSContext *cx, JSScript *target);
     ~JSAutoCompartment();
 };
 
-class JS_PUBLIC_API(JSAutoNullCompartment)
+class JS_PUBLIC_API(JSAutoNullableCompartment)
 {
     JSContext *cx_;
     JSCompartment *oldCompartment_;
   public:
-    explicit JSAutoNullCompartment(JSContext *cx);
-    ~JSAutoNullCompartment();
+    explicit JSAutoNullableCompartment(JSContext *cx, JSObject *targetOrNull);
+    ~JSAutoNullableCompartment();
 };
 
 /* NB: This API is infallible; a nullptr return value does not indicate error. */
 extern JS_PUBLIC_API(JSCompartment *)
 JS_EnterCompartment(JSContext *cx, JSObject *target);
 
 extern JS_PUBLIC_API(void)
 JS_LeaveCompartment(JSContext *cx, JSCompartment *oldCompartment);