Bug 1107777 - Add autoJSAPIOwnsErrorReporting flag to JSContext options. AutoJSAPI sets it. r=bholley
authorNikhil Marathe <nsm.nikhil@gmail.com>
Tue, 16 Dec 2014 02:02:37 -0800
changeset 220062 7fe7d8036eac311beb8aa600a158e7fa69e5431e
parent 220061 8b751f12a3ad6154af32c98a8264310f9dd15222
child 220063 7cc47794c45d0f334322706bb3138a76c5aa97b8
push id53002
push usercbook@mozilla.com
push dateWed, 17 Dec 2014 08:34:31 +0000
treeherdermozilla-inbound@7cc47794c45d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1107777
milestone37.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 1107777 - Add autoJSAPIOwnsErrorReporting flag to JSContext options. AutoJSAPI sets it. r=bholley
dom/base/ScriptSettings.cpp
dom/base/ScriptSettings.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/xpconnect/src/XPCWrappedJSClass.cpp
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -299,25 +299,25 @@ FindJSContext(nsIGlobalObject* aGlobalOb
     cx = nsContentUtils::GetSafeJSContext();
   }
   return cx;
 }
 
 AutoJSAPI::AutoJSAPI()
   : mCx(nullptr)
   , mOwnErrorReporting(false)
-  , mOldDontReportUncaught(false)
+  , mOldAutoJSAPIOwnsErrorReporting(false)
 {
 }
 
 AutoJSAPI::~AutoJSAPI()
 {
   if (mOwnErrorReporting) {
     MOZ_ASSERT(NS_IsMainThread(), "See corresponding assertion in TakeOwnershipOfErrorReporting()");
-    JS::ContextOptionsRef(cx()).setDontReportUncaught(mOldDontReportUncaught);
+    JS::ContextOptionsRef(cx()).setAutoJSAPIOwnsErrorReporting(mOldAutoJSAPIOwnsErrorReporting);
 
     if (HasException()) {
 
       // AutoJSAPI uses a JSAutoNullableCompartment, and may be in a null
       // compartment when the destructor is called. However, the JS engine
       // requires us to be in a compartment when we fetch the pending exception.
       // In this case, we enter the privileged junk scope and don't dispatch any
       // error events.
@@ -372,17 +372,17 @@ AutoJSAPI::InitInternal(JSObject* aGloba
     JS_SetErrorReporter(rt, xpc::SystemErrorReporter);
   }
 }
 
 AutoJSAPI::AutoJSAPI(nsIGlobalObject* aGlobalObject,
                      bool aIsMainThread,
                      JSContext* aCx)
   : mOwnErrorReporting(false)
-  , mOldDontReportUncaught(false)
+  , mOldAutoJSAPIOwnsErrorReporting(false)
 {
   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);
 }
@@ -467,21 +467,22 @@ AutoJSAPI::InitWithLegacyErrorReporting(
 }
 
 bool
 AutoJSAPI::InitWithLegacyErrorReporting(nsGlobalWindow* aWindow)
 {
   return InitWithLegacyErrorReporting(static_cast<nsIGlobalObject*>(aWindow));
 }
 
-// Even with dontReportUncaught, the JS engine still sends warning reports
-// to the JSErrorReporter as soon as they are generated. These go directly to
-// the console, so we can handle them easily here.
+// Even with autoJSAPIOwnsErrorReporting, the JS engine still sends warning
+// reports to the JSErrorReporter as soon as they are generated. These go
+// directly to the console, so we can handle them easily here.
 //
-// Eventually, SpiderMonkey will have a special-purpose callback for warnings only.
+// Eventually, SpiderMonkey will have a special-purpose callback for warnings
+// only.
 void
 WarningOnlyErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aRep)
 {
   MOZ_ASSERT(JSREPORT_IS_WARNING(aRep->flags));
   nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
   nsPIDOMWindow* win = xpc::WindowGlobalOrNull(JS::CurrentGlobalOrNull(aCx));
   xpcReport->Init(aRep, aMessage, nsContentUtils::IsCallerChrome(),
                   win ? win->WindowID() : 0);
@@ -491,18 +492,18 @@ WarningOnlyErrorReporter(JSContext* aCx,
 void
 AutoJSAPI::TakeOwnershipOfErrorReporting()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Can't own error reporting off-main-thread yet");
   MOZ_ASSERT(!mOwnErrorReporting);
   mOwnErrorReporting = true;
 
   JSRuntime *rt = JS_GetRuntime(cx());
-  mOldDontReportUncaught = JS::ContextOptionsRef(cx()).dontReportUncaught();
-  JS::ContextOptionsRef(cx()).setDontReportUncaught(true);
+  mOldAutoJSAPIOwnsErrorReporting = JS::ContextOptionsRef(cx()).autoJSAPIOwnsErrorReporting();
+  JS::ContextOptionsRef(cx()).setAutoJSAPIOwnsErrorReporting(true);
   JS_SetErrorReporter(rt, WarningOnlyErrorReporter);
 }
 
 bool
 AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal)
 {
     MOZ_ASSERT(CxPusherIsStackTop());
     MOZ_ASSERT(HasException());
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -302,17 +302,17 @@ protected:
 
 private:
   mozilla::Maybe<danger::AutoCxPusher> mCxPusher;
   mozilla::Maybe<JSAutoNullableCompartment> mAutoNullableCompartment;
   JSContext *mCx;
 
   // Track state between the old and new error reporting modes.
   bool mOwnErrorReporting;
-  bool mOldDontReportUncaught;
+  bool mOldAutoJSAPIOwnsErrorReporting;
   Maybe<JSErrorReporter> mOldErrorReporter;
 
   void InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread);
 
   AutoJSAPI(const AutoJSAPI&) MOZ_DELETE;
   AutoJSAPI& operator= (const AutoJSAPI&) MOZ_DELETE;
 };
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3736,17 +3736,17 @@ struct AutoLastFrameCheck
     {
         MOZ_ASSERT(cx);
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     ~AutoLastFrameCheck() {
         if (cx->isExceptionPending() &&
             !JS_IsRunning(cx) &&
-            !cx->options().dontReportUncaught()) {
+            (!cx->options().dontReportUncaught() && !cx->options().autoJSAPIOwnsErrorReporting())) {
             js_ReportUncaughtException(cx);
         }
     }
 
   private:
     JSContext *cx;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1596,17 +1596,18 @@ RuntimeOptionsRef(JSRuntime *rt);
 
 JS_PUBLIC_API(RuntimeOptions &)
 RuntimeOptionsRef(JSContext *cx);
 
 class JS_PUBLIC_API(ContextOptions) {
   public:
     ContextOptions()
       : privateIsNSISupports_(false),
-        dontReportUncaught_(false)
+        dontReportUncaught_(false),
+        autoJSAPIOwnsErrorReporting_(false)
     {
     }
 
     bool privateIsNSISupports() const { return privateIsNSISupports_; }
     ContextOptions &setPrivateIsNSISupports(bool flag) {
         privateIsNSISupports_ = flag;
         return *this;
     }
@@ -1620,19 +1621,38 @@ class JS_PUBLIC_API(ContextOptions) {
         dontReportUncaught_ = flag;
         return *this;
     }
     ContextOptions &toggleDontReportUncaught() {
         dontReportUncaught_ = !dontReportUncaught_;
         return *this;
     }
 
+    bool autoJSAPIOwnsErrorReporting() const { return autoJSAPIOwnsErrorReporting_; }
+    ContextOptions &setAutoJSAPIOwnsErrorReporting(bool flag) {
+        autoJSAPIOwnsErrorReporting_ = flag;
+        return *this;
+    }
+    ContextOptions &toggleAutoJSAPIOwnsErrorReporting() {
+        autoJSAPIOwnsErrorReporting_ = !autoJSAPIOwnsErrorReporting_;
+        return *this;
+    }
+
+
   private:
     bool privateIsNSISupports_ : 1;
     bool dontReportUncaught_ : 1;
+    // dontReportUncaught isn't respected by all JSAPI codepaths, particularly the
+    // JS_ReportError* functions that eventually report the error even when dontReportUncaught is
+    // set, if script is not running. We want a way to indicate that the embedder will always
+    // handle any exceptions, and that SpiderMonkey should just leave them on the context. This is
+    // the way we want to do all future error handling in Gecko - stealing the exception explicitly
+    // from the context and handling it as per the situation. This will eventually become the
+    // default and these 2 flags should go away.
+    bool autoJSAPIOwnsErrorReporting_ : 1;
 };
 
 JS_PUBLIC_API(ContextOptions &)
 ContextOptionsRef(JSContext *cx);
 
 class JS_PUBLIC_API(AutoSaveContextOptions) {
   public:
     explicit AutoSaveContextOptions(JSContext *cx)
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -296,22 +296,27 @@ ReportError(JSContext *cx, const char *m
      */
     MOZ_ASSERT(reportp);
     if ((!callback || callback == js_GetErrorMessage) &&
         reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
     {
         reportp->flags |= JSREPORT_EXCEPTION;
     }
 
+    if (cx->options().autoJSAPIOwnsErrorReporting() || JS_IsRunning(cx)) {
+        if (js_ErrorToException(cx, message, reportp, callback, userRef)) {
+            return;
+        }
+    }
+
     /*
      * Call the error reporter only if an exception wasn't raised.
      */
-    if (!JS_IsRunning(cx) || !js_ErrorToException(cx, message, reportp, callback, userRef)) {
-        if (message)
-            CallErrorReporter(cx, message, reportp);
+    if (message) {
+        CallErrorReporter(cx, message, reportp);
     }
 }
 
 /*
  * The given JSErrorReport object have been zeroed and must not outlive
  * cx->fp() (otherwise owned fields may become invalid).
  */
 static void
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -272,17 +272,18 @@ nsXPCWrappedJSClass::CallQueryInterfaceO
                         rv = (nsresult)(jsexception.toInt32());
 
                     if (rv == NS_NOINTERFACE)
                         JS_ClearPendingException(cx);
                 }
             }
 
             // Don't report if reporting was disabled by someone else.
-            if (!ContextOptionsRef(cx).dontReportUncaught())
+            if (!ContextOptionsRef(cx).dontReportUncaught() &&
+                !ContextOptionsRef(cx).autoJSAPIOwnsErrorReporting())
                 JS_ReportPendingException(cx);
         } else if (!success) {
             NS_WARNING("QI hook ran OOMed - this is probably a bug!");
         }
     }
 
     if (success)
         success = JS_ValueToObject(cx, retval, &retObj);