Bug 765936. r=bent,peterv
authorKyle Huey <khuey@kylehuey.com>
Fri, 06 Jul 2012 10:28:51 -0700
changeset 103348 03c5347327d89a2bc0101546146235858bac22b7
parent 103347 cd58bfdf6c71142ac0368ef79a85420ce19151bd
child 103349 8e47e74e35783bcb609934346dd3f59db6f3875d
push id1316
push userakeybl@mozilla.com
push dateMon, 27 Aug 2012 22:37:00 +0000
treeherdermozilla-beta@db4b09302ee2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent, peterv
bugs765936
milestone16.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 765936. r=bent,peterv
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IDBRequest.cpp
dom/indexedDB/IDBRequest.h
dom/indexedDB/IDBWrapperCache.cpp
dom/indexedDB/IDBWrapperCache.h
js/xpconnect/idl/nsIXPConnect.idl
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1277,16 +1277,18 @@ public:
    * Drop the JS objects held by aScriptObjectHolder.
    *
    * @param aScriptObjectHolder the object that holds JS objects that we want to
    *                            drop
    */
   static nsresult DropJSObjects(void* aScriptObjectHolder);
 
 #ifdef DEBUG
+  static bool AreJSObjectsHeld(void* aScriptObjectHolder); 
+
   static void CheckCCWrapperTraversal(nsISupports* aScriptObjectHolder,
                                       nsWrapperCache* aCache);
 #endif
 
   static void PreserveWrapper(nsISupports* aScriptObjectHolder,
                               nsWrapperCache* aCache)
   {
     if (!aCache->PreservingWrapper()) {
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -4448,16 +4448,27 @@ nsContentUtils::DropJSObjects(void* aScr
   NS_LOG_RELEASE(sXPConnect, sJSGCThingRootCount - 1, "HoldJSObjects");
   nsresult rv = sXPConnect->RemoveJSHolder(aScriptObjectHolder);
   if (--sJSGCThingRootCount == 0) {
     nsLayoutStatics::Release();
   }
   return rv;
 }
 
+#ifdef DEBUG
+/* static */
+bool
+nsContentUtils::AreJSObjectsHeld(void* aScriptHolder)
+{
+  bool isHeld = false;
+  sXPConnect->TestJSHolder(aScriptHolder, &isHeld);
+  return isHeld;
+}
+#endif
+
 /* static */
 void
 nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling)
 {
   nsIMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
 }
 
 static bool SchemeIs(nsIURI* aURI, const char* aScheme)
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -13,16 +13,17 @@
 #include "nsIScriptContext.h"
 
 #include "mozilla/storage.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
+#include "nsGlobalWindow.h"
 #include "nsHashKeys.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMCID.h"
 
 #include "AsyncConnectionHelper.h"
 #include "CheckPermissionsHelper.h"
@@ -408,16 +409,18 @@ IDBFactory::OpenCommon(const nsAString& 
   NS_ASSERTION(mWindow || mOwningObject, "Must have one of these!");
 
   nsCOMPtr<nsPIDOMWindow> window;
   nsCOMPtr<nsIScriptGlobalObject> sgo;
   JSObject* scriptOwner = nsnull;
 
   if (mWindow) {
     window = mWindow;
+    scriptOwner =
+      static_cast<nsGlobalWindow*>(window.get())->FastGetGlobalJSObject();
   }
   else {
     scriptOwner = mOwningObject;
   }
 
   nsRefPtr<IDBOpenDBRequest> request =
     IDBOpenDBRequest::Create(window, scriptOwner, aCallingCx);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -27,27 +27,24 @@
 
 USING_INDEXEDDB_NAMESPACE
 
 IDBRequest::IDBRequest()
 : mResultVal(JSVAL_VOID),
   mActorParent(nsnull),
   mErrorCode(NS_OK),
   mHaveResultOrErrorCode(false),
-  mRooted(false),
   mLineNo(0)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 IDBRequest::~IDBRequest()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  UnrootResultVal();
 }
 
 // static
 already_AddRefed<IDBRequest>
 IDBRequest::Create(nsISupports* aSource,
                    IDBWrapperCache* aOwnerCache,
                    IDBTransaction* aTransaction,
                    JSContext* aCallingCx)
@@ -69,25 +66,23 @@ IDBRequest::Create(nsISupports* aSource,
 
 void
 IDBRequest::Reset()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   mResultVal = JSVAL_VOID;
   mHaveResultOrErrorCode = false;
   mError = nsnull;
-  UnrootResultVal();
 }
 
 nsresult
 IDBRequest::NotifyHelperCompleted(HelperBase* aHelper)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!mHaveResultOrErrorCode, "Already called!");
-  NS_ASSERTION(!PreservingWrapper(), "Already rooted?!");
   NS_ASSERTION(JSVAL_IS_VOID(mResultVal), "Should be undefined!");
 
   // See if our window is still valid. If not then we're going to pretend that
   // we never completed.
   if (NS_FAILED(CheckInnerWindowCorrectness())) {
     return NS_OK;
   }
 
@@ -111,17 +106,17 @@ IDBRequest::NotifyHelperCompleted(Helper
   }
 
   JSObject* global = GetParentObject();
   NS_ASSERTION(global, "This should never be null!");
 
   JSAutoRequest ar(cx);
   JSAutoEnterCompartment ac;
   if (ac.enter(cx, global)) {
-    RootResultVal();
+    AssertIsRooted();
 
     rv = aHelper->GetSuccessResult(cx, &mResultVal);
     if (NS_FAILED(rv)) {
       NS_WARNING("GetSuccessResult failed!");
     }
   }
   else {
     NS_WARNING("Failed to enter correct compartment!");
@@ -139,17 +134,16 @@ IDBRequest::NotifyHelperCompleted(Helper
   return rv;
 }
 
 void
 IDBRequest::NotifyHelperSentResultsToChildProcess(nsresult aRv)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!mHaveResultOrErrorCode, "Already called!");
-  NS_ASSERTION(!PreservingWrapper(), "Already rooted?!");
   NS_ASSERTION(JSVAL_IS_VOID(mResultVal), "Should be undefined!");
 
   // See if our window is still valid. If not then we're going to pretend that
   // we never completed.
   if (NS_FAILED(CheckInnerWindowCorrectness())) {
     return;
   }
 
@@ -166,17 +160,16 @@ IDBRequest::SetError(nsresult aRv)
   NS_ASSERTION(NS_FAILED(aRv), "Er, what?");
   NS_ASSERTION(!mError, "Already have an error?");
 
   mHaveResultOrErrorCode = true;
   mError = DOMError::CreateForNSResult(aRv);
   mErrorCode = aRv;
 
   mResultVal = JSVAL_VOID;
-  UnrootResultVal();
 }
 
 #ifdef DEBUG
 nsresult
 IDBRequest::GetErrorCode() const
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(mHaveResultOrErrorCode, "Don't call me yet!");
@@ -234,28 +227,16 @@ IDBRequest::CaptureCaller(JSContext* aCx
 
 void
 IDBRequest::FillScriptErrorEvent(nsScriptErrorEvent* aEvent) const
 {
   aEvent->lineNr = mLineNo;
   aEvent->fileName = mFilename.get();
 }
 
-void
-IDBRequest::RootResultValInternal()
-{
-  NS_HOLD_JS_OBJECTS(this, IDBRequest);
-}
-
-void
-IDBRequest::UnrootResultValInternal()
-{
-  NS_DROP_JS_OBJECTS(this, IDBRequest);
-}
-
 NS_IMETHODIMP
 IDBRequest::GetReadyState(nsAString& aReadyState)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (mHaveResultOrErrorCode) {
     aReadyState.AssignLiteral("done");
   }
@@ -322,17 +303,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSource)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mTransaction,
                                                        nsPIDOMEventTarget)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest, IDBWrapperCache)
   tmp->mResultVal = JSVAL_VOID;
-  tmp->UnrootResultVal();
   NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(success)
   NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSource)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTransaction)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
@@ -361,18 +341,16 @@ IDBRequest::PreHandleEvent(nsEventChainP
   aVisitor.mCanHandle = true;
   aVisitor.mParentTarget = mTransaction;
   return NS_OK;
 }
 
 IDBOpenDBRequest::~IDBOpenDBRequest()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  UnrootResultVal();
 }
 
 // static
 already_AddRefed<IDBOpenDBRequest>
 IDBOpenDBRequest::Create(nsPIDOMWindow* aOwner,
                          JSObject* aScriptOwner,
                          JSContext* aCallingCx)
 {
@@ -395,28 +373,16 @@ IDBOpenDBRequest::SetTransaction(IDBTran
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   NS_ASSERTION(!aTransaction || !mTransaction,
                "Shouldn't have a transaction here!");
 
   mTransaction = aTransaction;
 }
 
-void
-IDBOpenDBRequest::RootResultValInternal()
-{
-  NS_HOLD_JS_OBJECTS(this, IDBOpenDBRequest);
-}
-
-void
-IDBOpenDBRequest::UnrootResultValInternal()
-{
-  NS_DROP_JS_OBJECTS(this, IDBOpenDBRequest);
-}
-
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBOpenDBRequest)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest,
                                                   IDBRequest)
   NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(upgradeneeded)
   NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(blocked)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -82,50 +82,30 @@ public:
   void CaptureCaller(JSContext* aCx);
 
   void FillScriptErrorEvent(nsScriptErrorEvent* aEvent) const;
 
 protected:
   IDBRequest();
   ~IDBRequest();
 
-  virtual void RootResultValInternal();
-  virtual void UnrootResultValInternal();
-
-  void RootResultVal()
-  {
-    if (!mRooted) {
-      RootResultValInternal();
-      mRooted = true;
-    }
-  }
-
-  void UnrootResultVal()
-  {
-    if (mRooted) {
-      UnrootResultValInternal();
-      mRooted = false;
-    }
-  }
-
   nsCOMPtr<nsISupports> mSource;
   nsRefPtr<IDBTransaction> mTransaction;
 
   NS_DECL_EVENT_HANDLER(success)
   NS_DECL_EVENT_HANDLER(error)
 
   jsval mResultVal;
 
   nsCOMPtr<nsIDOMDOMError> mError;
 
   IndexedDBRequestParentBase* mActorParent;
 
   nsresult mErrorCode;
   bool mHaveResultOrErrorCode;
-  bool mRooted;
 
   nsString mFilename;
   PRUint32 mLineNo;
 };
 
 class IDBOpenDBRequest : public IDBRequest,
                          public nsIIDBOpenDBRequest
 {
@@ -144,19 +124,16 @@ public:
   void SetTransaction(IDBTransaction* aTransaction);
 
   // nsIDOMEventTarget
   virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
 
 protected:
   ~IDBOpenDBRequest();
 
-  virtual void RootResultValInternal();
-  virtual void UnrootResultValInternal();
-
   // Only touched on the main thread.
   NS_DECL_EVENT_HANDLER(blocked)
   NS_DECL_EVENT_HANDLER(upgradeneeded)
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_idbrequest_h__
--- a/dom/indexedDB/IDBWrapperCache.cpp
+++ b/dom/indexedDB/IDBWrapperCache.cpp
@@ -43,30 +43,33 @@ IDBWrapperCache::~IDBWrapperCache()
   if (mScriptOwner) {
     NS_DROP_JS_OBJECTS(this, IDBWrapperCache);
   }
 }
 
 bool
 IDBWrapperCache::SetScriptOwner(JSObject* aScriptOwner)
 {
-  if (!aScriptOwner) {
-    NS_ASSERTION(!mScriptOwner,
-                 "Don't null out existing owner, we need to call "
-                 "DropJSObjects!");
-
-    return true;
-  }
+  NS_ASSERTION(aScriptOwner, "This should never be null!");
 
   mScriptOwner = aScriptOwner;
 
   nsISupports* thisSupports = NS_CYCLE_COLLECTION_UPCAST(this, IDBWrapperCache);
   nsXPCOMCycleCollectionParticipant* participant;
   CallQueryInterface(this, &participant);
   nsresult rv = nsContentUtils::HoldJSObjects(thisSupports, participant);
   if (NS_FAILED(rv)) {
     NS_WARNING("nsContentUtils::HoldJSObjects failed.");
     mScriptOwner = nsnull;
     return false;
   }
 
   return true;
 }
+
+#ifdef DEBUG
+void
+IDBWrapperCache::AssertIsRooted() const
+{
+  NS_ASSERTION(nsContentUtils::AreJSObjectsHeld(const_cast<IDBWrapperCache*>(this)),
+               "Why aren't we rooted?!");
+}
+#endif
--- a/dom/indexedDB/IDBWrapperCache.h
+++ b/dom/indexedDB/IDBWrapperCache.h
@@ -41,16 +41,24 @@ public:
   }
 
   static IDBWrapperCache* FromSupports(nsISupports* aSupports)
   {
     return static_cast<IDBWrapperCache*>(
       nsDOMEventTargetHelper::FromSupports(aSupports));
   }
 
+#ifdef DEBUG
+  void AssertIsRooted() const;
+#else
+  inline void AssertIsRooted() const
+  {
+  }
+#endif
+
 protected:
   IDBWrapperCache()
   : mScriptOwner(nsnull)
   { }
 
   virtual ~IDBWrapperCache();
 
 private:
--- a/js/xpconnect/idl/nsIXPConnect.idl
+++ b/js/xpconnect/idl/nsIXPConnect.idl
@@ -669,16 +669,23 @@ interface nsIXPConnect : nsISupports
 
     /**
      * Stop rooting the JS objects held by aHolder.
      * @param aHolder The object that hold the rooted JS objects.
      */
     [noscript] void removeJSHolder(in voidPtr aHolder);
 
     /**
+     * Test to see if a JS holder is in our hashtable.
+     * Only available in debug builds.
+     * @param aHolder The object to test for.
+     */
+    [noscript] bool testJSHolder(in voidPtr aHolder);
+
+    /**
      * Note aJSContext as a child to the cycle collector.
      * @param aJSContext The JSContext to note.
      * @param aCb The cycle collection traversal callback.
      */
     [noscript,notxpcom] void noteJSContext(in JSContextPtr aJSContext,
                                            in nsCCTraversalCallbackRef aCb);
 
     /**
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -291,16 +291,27 @@ XPCJSRuntime::RemoveJSHolder(void* aHold
     if (!mJSHolders.ops)
         return NS_ERROR_OUT_OF_MEMORY;
 
     JS_DHashTableOperate(&mJSHolders, aHolder, JS_DHASH_REMOVE);
 
     return NS_OK;
 }
 
+nsresult
+XPCJSRuntime::TestJSHolder(void* aHolder, bool* aRetval)
+{
+    if (!mJSHolders.ops)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    *aRetval = !!JS_DHashTableOperate(&mJSHolders, aHolder, JS_DHASH_LOOKUP);
+
+    return NS_OK;
+}
+
 // static
 void XPCJSRuntime::TraceBlackJS(JSTracer* trc, void* data)
 {
     XPCJSRuntime* self = (XPCJSRuntime*)data;
 
     // Skip this part if XPConnect is shutting down. We get into
     // bad locking problems with the thread iteration otherwise.
     if (!self->GetXPConnect()->IsShuttingDown()) {
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -2256,16 +2256,27 @@ nsXPConnect::AddJSHolder(void* aHolder, 
 
 NS_IMETHODIMP
 nsXPConnect::RemoveJSHolder(void* aHolder)
 {
     return mRuntime->RemoveJSHolder(aHolder);
 }
 
 NS_IMETHODIMP
+nsXPConnect::TestJSHolder(void* aHolder, bool* aRetval)
+{
+#ifdef DEBUG
+    return mRuntime->TestJSHolder(aHolder, aRetval);
+#else
+    MOZ_ASSERT(false);
+    return NS_ERROR_FAILURE;
+#endif
+}
+
+NS_IMETHODIMP
 nsXPConnect::SetReportAllJSExceptions(bool newval)
 {
     // Ignore if the environment variable was set.
     if (gReportAllJSExceptions != 1)
         gReportAllJSExceptions = newval ? 2 : 0;
 
     return NS_OK;
 }
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -758,16 +758,17 @@ public:
     static void FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status, JSBool isCompartmentGC);
 
     inline void AddVariantRoot(XPCTraceableVariant* variant);
     inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS);
     inline void AddObjectHolderRoot(XPCJSObjectHolder* holder);
 
     nsresult AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
     nsresult RemoveJSHolder(void* aHolder);
+    nsresult TestJSHolder(void* aHolder, bool* aRetval);
 
     static void SuspectWrappedNative(XPCWrappedNative *wrapper,
                                      nsCycleCollectionTraversalCallback &cb);
 
     void DebugDump(PRInt16 depth);
 
     void SystemIsBeingShutDown();