Merge mozilla-central into mozilla-inbound
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 28 Nov 2012 09:43:30 -0500
changeset 114364 33b2ec1bc7f37e943d33d251512c28756da1ef86
parent 114363 eb4eff0fc3d75dd237617aa76040d485421aaa5e (current diff)
parent 114257 6c23f41b074768a4970c493edeafedee43450a1e (diff)
child 114365 67e95e421678221c8d8532860ef990af49d73249
push id23917
push useremorley@mozilla.com
push dateThu, 29 Nov 2012 14:20:29 +0000
treeherdermozilla-central@c72d38e7a212 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone20.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
Merge mozilla-central into mozilla-inbound
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -2234,18 +2234,16 @@ private:
   static nsIStringBundle* sStringBundles[PropertiesFile_COUNT];
 
   static nsIContentPolicy* sContentPolicyService;
   static bool sTriedToGetContentPolicy;
 
   static nsILineBreaker* sLineBreaker;
   static nsIWordBreaker* sWordBreaker;
 
-  static uint32_t sJSGCThingRootCount;
-
 #ifdef IBMBIDI
   static nsIBidiKeyboard* sBidiKeyboard;
 #endif
 
   static bool sInitialized;
   static uint32_t sScriptBlockerCount;
 #ifdef DEBUG
   static uint32_t sDOMNodeRemovedSuppressCount;
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -203,17 +203,16 @@ nsDataHashtable<nsISupportsHashKey, Even
 nsDataHashtable<nsStringHashKey, EventNameMapping>* nsContentUtils::sStringEventTable = nullptr;
 nsCOMArray<nsIAtom>* nsContentUtils::sUserDefinedEvents = nullptr;
 nsIStringBundleService *nsContentUtils::sStringBundleService;
 nsIStringBundle *nsContentUtils::sStringBundles[PropertiesFile_COUNT];
 nsIContentPolicy *nsContentUtils::sContentPolicyService;
 bool nsContentUtils::sTriedToGetContentPolicy = false;
 nsILineBreaker *nsContentUtils::sLineBreaker;
 nsIWordBreaker *nsContentUtils::sWordBreaker;
-uint32_t nsContentUtils::sJSGCThingRootCount;
 #ifdef IBMBIDI
 nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nullptr;
 #endif
 uint32_t nsContentUtils::sScriptBlockerCount = 0;
 #ifdef DEBUG
 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
 #endif
 uint32_t nsContentUtils::sMicroTaskLevel = 0;
@@ -4514,38 +4513,28 @@ nsContentUtils::DestroyAnonymousContent(
 
 /* static */
 nsresult
 nsContentUtils::HoldJSObjects(void* aScriptObjectHolder,
                               nsScriptObjectTracer* aTracer)
 {
   NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED);
 
-  nsresult rv = sXPConnect->AddJSHolder(aScriptObjectHolder, aTracer);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (sJSGCThingRootCount++ == 0) {
-    nsLayoutStatics::AddRef();
-  }
-  NS_LOG_ADDREF(sXPConnect, sJSGCThingRootCount, "HoldJSObjects",
-                sizeof(void*));
-
-  return NS_OK;
+  return sXPConnect->AddJSHolder(aScriptObjectHolder, aTracer);
 }
 
 /* static */
 nsresult
 nsContentUtils::DropJSObjects(void* aScriptObjectHolder)
 {
-  NS_LOG_RELEASE(sXPConnect, sJSGCThingRootCount - 1, "HoldJSObjects");
-  nsresult rv = sXPConnect->RemoveJSHolder(aScriptObjectHolder);
-  if (--sJSGCThingRootCount == 0) {
-    nsLayoutStatics::Release();
-  }
-  return rv;
+  if (!sXPConnect) {
+    return NS_OK;
+  }
+
+  return sXPConnect->RemoveJSHolder(aScriptObjectHolder);
 }
 
 #ifdef DEBUG
 /* static */
 bool
 nsContentUtils::AreJSObjectsHeld(void* aScriptHolder)
 {
   bool isHeld = false;
@@ -6872,19 +6861,18 @@ nsContentUtils::ReleaseWrapper(void* aSc
   if (aCache->PreservingWrapper()) {
     // PreserveWrapper puts new DOM bindings in the JS holders hash, but they
     // can also be in the DOM expando hash, so we need to try to remove them
     // from both here.
     JSObject* obj = aCache->GetWrapperPreserveColor();
     if (aCache->IsDOMBinding() && obj) {
       xpc::GetObjectScope(obj)->RemoveDOMExpandoObject(obj);
     }
+    aCache->SetPreservingWrapper(false);
     DropJSObjects(aScriptObjectHolder);
-
-    aCache->SetPreservingWrapper(false);
   }
 }
 
 // static
 void
 nsContentUtils::TraceWrapper(nsWrapperCache* aCache, TraceCallback aCallback,
                              void *aClosure)
 {
--- a/content/canvas/src/ImageData.cpp
+++ b/content/canvas/src/ImageData.cpp
@@ -41,18 +41,18 @@ ImageData::HoldData()
 {
   NS_HOLD_JS_OBJECTS(this, ImageData);
 }
 
 void
 ImageData::DropData()
 {
   if (mData) {
+    mData = NULL;
     NS_DROP_JS_OBJECTS(this, ImageData);
-    mData = NULL;
   }
 }
 
 JSObject*
 ImageData::WrapObject(JSContext* cx, JSObject* scope)
 {
   return ImageDataBinding::Wrap(cx, scope, this);
 }
--- a/content/events/src/nsDOMMessageEvent.cpp
+++ b/content/events/src/nsDOMMessageEvent.cpp
@@ -57,19 +57,19 @@ nsDOMMessageEvent::RootData()
   NS_HOLD_JS_OBJECTS(this, nsDOMMessageEvent);
   mDataRooted = true;
 }
 
 void
 nsDOMMessageEvent::UnrootData()
 {
   NS_ASSERTION(mDataRooted, "...");
-  NS_DROP_JS_OBJECTS(this, nsDOMMessageEvent);
   mDataRooted = false;
   mData = JSVAL_VOID;
+  NS_DROP_JS_OBJECTS(this, nsDOMMessageEvent);
 }
 
 NS_IMETHODIMP
 nsDOMMessageEvent::GetData(JSContext* aCx, jsval* aData)
 {
   *aData = mData;
   if (!JS_WrapValue(aCx, aData))
     return NS_ERROR_FAILURE;
--- a/content/events/src/nsDOMNotifyAudioAvailableEvent.cpp
+++ b/content/events/src/nsDOMNotifyAudioAvailableEvent.cpp
@@ -33,18 +33,18 @@ DOMCI_DATA(NotifyAudioAvailableEvent, ns
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMNotifyAudioAvailableEvent)
 
 NS_IMPL_ADDREF_INHERITED(nsDOMNotifyAudioAvailableEvent, nsDOMEvent)
 NS_IMPL_RELEASE_INHERITED(nsDOMNotifyAudioAvailableEvent, nsDOMEvent)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMNotifyAudioAvailableEvent, nsDOMEvent)
   if (tmp->mCachedArray) {
+    tmp->mCachedArray = nullptr;
     NS_DROP_JS_OBJECTS(tmp, nsDOMNotifyAudioAvailableEvent);
-    tmp->mCachedArray = nullptr;
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMNotifyAudioAvailableEvent, nsDOMEvent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMNotifyAudioAvailableEvent)
@@ -55,18 +55,18 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
   NS_INTERFACE_MAP_ENTRY(nsIDOMNotifyAudioAvailableEvent)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(NotifyAudioAvailableEvent)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
 
 nsDOMNotifyAudioAvailableEvent::~nsDOMNotifyAudioAvailableEvent()
 {
   MOZ_COUNT_DTOR(nsDOMNotifyAudioAvailableEvent);
   if (mCachedArray) {
+    mCachedArray = nullptr;
     NS_DROP_JS_OBJECTS(this, nsDOMNotifyAudioAvailableEvent);
-    mCachedArray = nullptr;
   }
 }
 
 NS_IMETHODIMP
 nsDOMNotifyAudioAvailableEvent::GetFrameBuffer(JSContext* aCx, jsval* aResult)
 {
   if (!mAllowAudioData) {
     // Media is not same-origin, don't allow the data out.
--- a/content/media/webaudio/AudioBuffer.cpp
+++ b/content/media/webaudio/AudioBuffer.cpp
@@ -49,16 +49,17 @@ AudioBuffer::AudioBuffer(AudioContext* a
 {
   SetIsDOMBinding();
 
   NS_HOLD_JS_OBJECTS(this, AudioBuffer);
 }
 
 AudioBuffer::~AudioBuffer()
 {
+  mChannels.Clear();
   NS_DROP_JS_OBJECTS(this, AudioBuffer);
 }
 
 bool
 AudioBuffer::InitializeBuffers(uint32_t aNumberOfChannels, JSContext* aJSContext)
 {
   if (!mChannels.SetCapacity(aNumberOfChannels)) {
     return false;
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -517,18 +517,19 @@ nsXBLDocumentInfo::~nsXBLDocumentInfo()
 {
   /* destructor code */
   if (mGlobalObject) {
     // remove circular reference
     mGlobalObject->ClearScriptContext();
     mGlobalObject->ClearGlobalObjectOwner(); // just in case
   }
   if (mBindingTable) {
+    delete mBindingTable;
+    mBindingTable = nullptr;
     NS_DROP_JS_OBJECTS(this, nsXBLDocumentInfo);
-    delete mBindingTable;
   }
 }
 
 nsXBLPrototypeBinding*
 nsXBLDocumentInfo::GetPrototypeBinding(const nsACString& aRef)
 {
   if (!mBindingTable)
     return NULL;
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2486,18 +2486,18 @@ nsXULPrototypeScript::Compile(const PRUn
     Set(newScriptObject);
     return rv;
 }
 
 void
 nsXULPrototypeScript::UnlinkJSObjects()
 {
     if (mScriptObject.mObject) {
+        mScriptObject.mObject = nullptr;
         nsContentUtils::DropJSObjects(this);
-        mScriptObject.mObject = nullptr;
     }
 }
 
 void
 nsXULPrototypeScript::Set(JSScript* aObject)
 {
     NS_ASSERTION(!mScriptObject.mObject, "Leaking script object.");
     if (!aObject) {
--- a/dom/base/DOMRequest.cpp
+++ b/dom/base/DOMRequest.cpp
@@ -49,17 +49,16 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(DOMReques
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest,
                                                   nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest,
                                                 nsDOMEventTargetHelper)
   if (tmp->mRooted) {
-    tmp->mResult = JSVAL_VOID;
     tmp->UnrootResultVal();
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest,
                                                nsDOMEventTargetHelper)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
@@ -182,16 +181,17 @@ DOMRequest::RootResultVal()
                                 participant);
   mRooted = true;
 }
 
 void
 DOMRequest::UnrootResultVal()
 {
   NS_ASSERTION(mRooted, "Don't call me if not rooted!");
+  mResult = JSVAL_VOID;
   NS_DROP_JS_OBJECTS(this, DOMRequest);
   mRooted = false;
 }
 
 NS_IMPL_ISUPPORTS1(DOMRequestService, nsIDOMRequestService)
 
 NS_IMETHODIMP
 DOMRequestService::CreateRequest(nsIDOMWindow* aWindow,
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -4139,22 +4139,23 @@ nsJSArgArray::nsJSArgArray(JSContext *aC
 nsJSArgArray::~nsJSArgArray()
 {
   ReleaseJSObjects();
 }
 
 void
 nsJSArgArray::ReleaseJSObjects()
 {
-  if (mArgc > 0)
-    NS_DROP_JS_OBJECTS(this, nsJSArgArray);
   if (mArgv) {
     PR_DELETE(mArgv);
   }
-  mArgc = 0;
+  if (mArgc > 0) {
+    mArgc = 0;
+    NS_DROP_JS_OBJECTS(this, nsJSArgArray);
+  }
 }
 
 // QueryInterface implementation for nsJSArgArray
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray)
   tmp->ReleaseJSObjects();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -136,27 +136,22 @@ nsJSScriptTimeoutHandler::nsJSScriptTime
 nsJSScriptTimeoutHandler::~nsJSScriptTimeoutHandler()
 {
   ReleaseJSObjects();
 }
 
 void
 nsJSScriptTimeoutHandler::ReleaseJSObjects()
 {
-  if (mExpr || mFunObj) {
-    if (mExpr) {
-      NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
-      mExpr = nullptr;
-    } else if (mFunObj) {
-      NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
-      mFunObj = nullptr;
-    } else {
-      NS_WARNING("No func and no expr - roots may not have been removed");
-    }
+  if (mExpr) {
+    mExpr = nullptr;
+  } else {
+    mFunObj = nullptr;
   }
+  NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
 }
 
 nsresult
 nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval,
                                int32_t *aInterval)
 {
   mContext = aWindow->GetContextInternal();
   if (!mContext) {
--- a/dom/bindings/CallbackFunction.h
+++ b/dom/bindings/CallbackFunction.h
@@ -86,18 +86,18 @@ public:
     // Play it safe in case this gets called after unlink.
     return mCallable && xpc_IsGrayGCThing(mCallable);
   }
 
 protected:
   void DropCallback()
   {
     if (mCallable) {
+      mCallable = nullptr;
       NS_DROP_JS_OBJECTS(this, CallbackFunction);
-      mCallable = nullptr;
       nsLayoutStatics::Release();
     }
   }
 
   JSObject* mCallable;
 
   class NS_STACK_CLASS CallSetup
   {
--- a/dom/bluetooth/BluetoothAdapter.cpp
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -158,16 +158,18 @@ BluetoothAdapter::~BluetoothAdapter()
 }
 
 void
 BluetoothAdapter::Unroot()
 {
   if (!mIsRooted) {
     return;
   }
+  mJsUuids = nullptr;
+  mJsDeviceAddresses = nullptr;
   NS_DROP_JS_OBJECTS(this, BluetoothAdapter);
   mIsRooted = false;
 }
 
 void
 BluetoothAdapter::Root()
 {
   if (mIsRooted) {
--- a/dom/bluetooth/BluetoothDevice.cpp
+++ b/dom/bluetooth/BluetoothDevice.cpp
@@ -82,16 +82,18 @@ BluetoothDevice::Root()
     mIsRooted = true;
   }
 }
 
 void
 BluetoothDevice::Unroot()
 {
   if (mIsRooted) {
+    mJsUuids = nullptr;
+    mJsServices = nullptr;
     NS_DROP_JS_OBJECTS(this, BluetoothDevice);
     mIsRooted = false;
   }
 }
 
 void
 BluetoothDevice::SetPropertyByValue(const BluetoothNamedValue& aValue)
 {
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -1136,18 +1136,18 @@ nsDOMDeviceStorageCursor::Continue()
 {
   if (!mOkToCallContinue) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (mRooted) {
     // We call onsuccess multiple times. clear the last
     // rooted result.
+    mResult = JSVAL_VOID;
     NS_DROP_JS_OBJECTS(this, nsDOMDeviceStorageCursor);
-    mResult = JSVAL_VOID;
     mDone = false;
     mRooted = false;
   }
 
   nsCOMPtr<ContinueCursorEvent> event = new ContinueCursorEvent(this);
   NS_DispatchToMainThread(event);
 
   mOkToCallContinue = false;
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -364,20 +364,36 @@ IDBCursor::~IDBCursor()
 
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorChild->Send__delete__(mActorChild);
     NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
   }
 
-  if (mRooted) {
-    NS_DROP_JS_OBJECTS(this, IDBCursor);
+  DropJSObjects();
+  IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
+}
+
+void
+IDBCursor::DropJSObjects()
+{
+  if (!mRooted) {
+    return;
   }
-  IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
+  mScriptOwner = nullptr;
+  mCachedKey = JSVAL_VOID;
+  mCachedPrimaryKey = JSVAL_VOID;
+  mCachedValue = JSVAL_VOID;
+  mHaveCachedKey = false;
+  mHaveCachedPrimaryKey = false;
+  mHaveCachedValue = false;
+  mRooted = false;
+  mHaveValue = false;
+  NS_DROP_JS_OBJECTS(this, IDBCursor);
 }
 
 nsresult
 IDBCursor::ContinueInternal(const Key& aKey,
                             int32_t aCount)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aCount > 0, "Must have a count!");
@@ -450,28 +466,17 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDB
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptOwner)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKey)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedPrimaryKey)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedValue)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor)
   // Don't unlink mObjectStore, mIndex, or mTransaction!
-  if (tmp->mRooted) {
-    NS_DROP_JS_OBJECTS(tmp, IDBCursor);
-    tmp->mScriptOwner = nullptr;
-    tmp->mCachedKey = JSVAL_VOID;
-    tmp->mCachedPrimaryKey = JSVAL_VOID;
-    tmp->mCachedValue = JSVAL_VOID;
-    tmp->mHaveCachedKey = false;
-    tmp->mHaveCachedPrimaryKey = false;
-    tmp->mHaveCachedValue = false;
-    tmp->mRooted = false;
-    tmp->mHaveValue = false;
-  }
+  tmp->DropJSObjects();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBCursor)
   NS_INTERFACE_MAP_ENTRY(nsIIDBCursor)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIDBCursorWithValue, mType != INDEXKEY)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(IDBCursorWithValue,
                                                    mType != INDEXKEY)
--- a/dom/indexedDB/IDBCursor.h
+++ b/dom/indexedDB/IDBCursor.h
@@ -146,16 +146,18 @@ public:
   nsresult
   ContinueInternal(const Key& aKey,
                    int32_t aCount);
 
 protected:
   IDBCursor();
   ~IDBCursor();
 
+  void DropJSObjects();
+
   static
   already_AddRefed<IDBCursor>
   CreateCommon(IDBRequest* aRequest,
                IDBTransaction* aTransaction,
                IDBObjectStore* aObjectStore,
                Direction aDirection,
                const Key& aRangeKey,
                const nsACString& aContinueQuery,
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -239,18 +239,16 @@ IDBDatabase::~IDBDatabase()
   if (mRegistered) {
     CloseInternal(true);
 
     IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
     if (mgr) {
       mgr->UnregisterDatabase(this);
     }
   }
-
-  nsContentUtils::ReleaseWrapper(static_cast<nsIDOMEventTarget*>(this), this);
 }
 
 void
 IDBDatabase::Invalidate()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (IsInvalidated()) {
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -72,16 +72,17 @@ IDBFactory::~IDBFactory()
 {
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorChild->Send__delete__(mActorChild);
     NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
   }
   if (mRootedOwningObject) {
+    mOwningObject = nullptr;
     NS_DROP_JS_OBJECTS(this, IDBFactory);
   }
 }
 
 // static
 nsresult
 IDBFactory::Create(nsPIDOMWindow* aWindow,
                    const nsACString& aASCIIOrigin,
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -409,16 +409,17 @@ IDBIndex::IDBIndex()
 }
 
 IDBIndex::~IDBIndex()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
 
   if (mRooted) {
+    mCachedKeyPath = JSVAL_VOID;
     NS_DROP_JS_OBJECTS(this, IDBIndex);
   }
 
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorChild->Send__delete__(mActorChild);
     NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
   }
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -313,42 +313,47 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBKeyRange)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedLowerVal)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedUpperVal)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBKeyRange)
-  if (tmp->mRooted) {
-    NS_DROP_JS_OBJECTS(tmp, IDBKeyRange);
-    tmp->mCachedLowerVal = JSVAL_VOID;
-    tmp->mCachedUpperVal = JSVAL_VOID;
-    tmp->mHaveCachedLowerVal = false;
-    tmp->mHaveCachedUpperVal = false;
-    tmp->mRooted = false;
-  }
+  tmp->DropJSObjects();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBKeyRange)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIIDBKeyRange)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBKeyRange)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBKeyRange)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBKeyRange)
 
 DOMCI_DATA(IDBKeyRange, IDBKeyRange)
 
+void
+IDBKeyRange::DropJSObjects()
+{
+  if (!mRooted) {
+    return;
+  }
+  mCachedLowerVal = JSVAL_VOID;
+  mCachedUpperVal = JSVAL_VOID;
+  mHaveCachedLowerVal = false;
+  mHaveCachedUpperVal = false;
+  mRooted = false;
+  NS_DROP_JS_OBJECTS(this, IDBKeyRange);
+}
+
 IDBKeyRange::~IDBKeyRange()
 {
-  if (mRooted) {
-    NS_DROP_JS_OBJECTS(this, IDBKeyRange);
-  }
+  DropJSObjects();
 }
 
 NS_IMETHODIMP
 IDBKeyRange::GetLower(JSContext* aCx,
                       jsval* aLower)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
--- a/dom/indexedDB/IDBKeyRange.h
+++ b/dom/indexedDB/IDBKeyRange.h
@@ -145,16 +145,18 @@ public:
     }
 
     return NS_OK;
   }
 
   template <class T>
   void ToSerializedKeyRange(T& aKeyRange);
 
+  void DropJSObjects();
+
 private:
   ~IDBKeyRange();
 
   Key mLower;
   Key mUpper;
   jsval mCachedLowerVal;
   jsval mCachedUpperVal;
   bool mLowerOpen;
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1538,16 +1538,17 @@ IDBObjectStore::~IDBObjectStore()
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorChild->Send__delete__(mActorChild);
     NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
   }
 
   if (mRooted) {
+    mCachedKeyPath = JSVAL_VOID;
     NS_DROP_JS_OBJECTS(this, IDBObjectStore);
   }
 }
 
 nsresult
 IDBObjectStore::GetAddInfo(JSContext* aCx,
                            jsval aValue,
                            jsval aKeyVal,
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -35,16 +35,17 @@ IDBRequest::IDBRequest()
   mHaveResultOrErrorCode(false),
   mLineNo(0)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 IDBRequest::~IDBRequest()
 {
+  mResultVal = JSVAL_VOID;
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 // static
 already_AddRefed<IDBRequest>
 IDBRequest::Create(nsISupports* aSource,
                    IDBWrapperCache* aOwnerCache,
                    IDBTransaction* aTransaction,
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -184,18 +184,16 @@ IDBTransaction::~IDBTransaction()
   NS_ASSERTION(mFiredCompleteOrAbort, "Should have fired event!");
 
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorChild->Send__delete__(mActorChild);
     NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
   }
-
-  nsContentUtils::ReleaseWrapper(static_cast<nsIDOMEventTarget*>(this), this);
 }
 
 void
 IDBTransaction::OnNewRequest()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   if (!mPendingRequests) {
     NS_ASSERTION(mReadyState == IDBTransaction::INITIAL,
--- a/dom/indexedDB/IDBWrapperCache.cpp
+++ b/dom/indexedDB/IDBWrapperCache.cpp
@@ -15,18 +15,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
                                                   nsDOMEventTargetHelper)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS because
   // nsDOMEventTargetHelper does it for us.
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBWrapperCache,
                                                 nsDOMEventTargetHelper)
   if (tmp->mScriptOwner) {
+    tmp->mScriptOwner = nullptr;
     NS_DROP_JS_OBJECTS(tmp, IDBWrapperCache);
-    tmp->mScriptOwner = nullptr;
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBWrapperCache,
                                                nsDOMEventTargetHelper)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
   // nsDOMEventTargetHelper does it for us.
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptOwner)
@@ -35,19 +35,19 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBWrapperCache)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(IDBWrapperCache, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(IDBWrapperCache, nsDOMEventTargetHelper)
 
 IDBWrapperCache::~IDBWrapperCache()
 {
-  if (mScriptOwner) {
-    NS_DROP_JS_OBJECTS(this, IDBWrapperCache);
-  }
+  mScriptOwner = nullptr;
+  nsContentUtils::ReleaseWrapper(this, this);
+  NS_DROP_JS_OBJECTS(this, IDBWrapperCache);
 }
 
 bool
 IDBWrapperCache::SetScriptOwner(JSObject* aScriptOwner)
 {
   NS_ASSERTION(aScriptOwner, "This should never be null!");
 
   mScriptOwner = aScriptOwner;
--- a/dom/sms/src/SmsRequest.cpp
+++ b/dom/sms/src/SmsRequest.cpp
@@ -38,17 +38,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCursor)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SmsRequest,
                                                 nsDOMEventTargetHelper)
   if (tmp->mResultRooted) {
-    tmp->mResult = JSVAL_VOID;
     tmp->UnrootResult();
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCursor)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(SmsRequest,
                                                nsDOMEventTargetHelper)
@@ -132,16 +131,17 @@ SmsRequest::RootResult()
   NS_HOLD_JS_OBJECTS(this, SmsRequest);
   mResultRooted = true;
 }
 
 void
 SmsRequest::UnrootResult()
 {
   NS_ASSERTION(mResultRooted, "Don't call UnrotResult() if not rooted!");
+  mResult = JSVAL_VOID;
   NS_DROP_JS_OBJECTS(this, SmsRequest);
   mResultRooted = false;
 }
 
 void
 SmsRequest::SetSuccess(nsIDOMMozSmsMessage* aMessage)
 {
   SetSuccessInternal(aMessage);
@@ -201,17 +201,16 @@ SmsRequest::SetSuccessInternal(nsISuppor
 
   JSAutoRequest ar(cx);
   JSAutoCompartment ac(cx, global);
 
   RootResult();
 
   if (NS_FAILED(nsContentUtils::WrapNative(cx, global, aObject, &mResult))) {
     UnrootResult();
-    mResult = JSVAL_VOID;
     SetError(nsISmsRequest::INTERNAL_ERROR);
     return false;
   }
 
   mDone = true;
   return true;
 }
 
--- a/dom/src/events/nsJSEventListener.cpp
+++ b/dom/src/events/nsJSEventListener.cpp
@@ -56,37 +56,39 @@ nsJSEventListener::nsJSEventListener(nsI
   if (mScopeObject) {
     NS_HOLD_JS_OBJECTS(this, nsJSEventListener);
   }
 }
 
 nsJSEventListener::~nsJSEventListener() 
 {
   if (mScopeObject) {
+    mScopeObject = nullptr;
     NS_DROP_JS_OBJECTS(this, nsJSEventListener);
   }
 }
 
 /* virtual */
 void
 nsJSEventListener::UpdateScopeObject(JSObject* aScopeObject)
 {
   if (mScopeObject && !aScopeObject) {
+    mScopeObject = nullptr;
     NS_DROP_JS_OBJECTS(this, nsJSEventListener);
   } else if (aScopeObject && !mScopeObject) {
     NS_HOLD_JS_OBJECTS(this, nsJSEventListener);
   }
   mScopeObject = aScopeObject;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSEventListener)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSEventListener)
   if (tmp->mScopeObject) {
+    tmp->mScopeObject = nullptr;
     NS_DROP_JS_OBJECTS(tmp, nsJSEventListener);
-    tmp->mScopeObject = nullptr;
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
   }
   tmp->mHandler.ForgetHandler();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSEventListener)
   if (MOZ_UNLIKELY(cb.WantDebugInfo()) && tmp->mEventName) {
     nsAutoCString name;
     name.AppendLiteral("nsJSEventListener handlerName=");
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -48,16 +48,17 @@ Telephony::Telephony()
 
 Telephony::~Telephony()
 {
   if (mRIL && mRILTelephonyCallback) {
     mRIL->UnregisterTelephonyCallback(mRILTelephonyCallback);
   }
 
   if (mRooted) {
+    mCallsArray = nullptr;
     NS_DROP_JS_OBJECTS(this, Telephony);
   }
 
   NS_ASSERTION(gTelephonyList, "This should never be null!");
   NS_ASSERTION(gTelephonyList->Contains(this), "Should be in the list!");
 
   if (gTelephonyList->Length() == 1) {
     delete gTelephonyList;
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -245,25 +245,59 @@ CompartmentDestroyedCallback(JSFreeOp *f
     nsAutoPtr<CompartmentPrivate> priv(GetCompartmentPrivate(compartment));
     JS_SetCompartmentPrivate(compartment, nullptr);
 }
 
 nsresult
 XPCJSRuntime::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer)
 {
     MOZ_ASSERT(aTracer->Trace, "AddJSHolder needs a non-null Trace function");
+    bool wasEmpty = mJSHolders.Count() == 0;
     mJSHolders.Put(aHolder, aTracer);
+    if (wasEmpty && mJSHolders.Count() == 1) {
+      nsLayoutStatics::AddRef();
+    }
 
     return NS_OK;
 }
 
+#ifdef DEBUG
+static void
+AssertNoGcThing(void* aGCThing, const char* aName, void* aClosure)
+{
+    MOZ_ASSERT(!aGCThing);
+}
+
+void
+XPCJSRuntime::AssertNoObjectsToTrace(void* aPossibleJSHolder)
+{
+    nsScriptObjectTracer* tracer = mJSHolders.Get(aPossibleJSHolder);
+    if (tracer && tracer->Trace) {
+        tracer->Trace(aPossibleJSHolder, AssertNoGcThing, nullptr);
+    }
+}
+#endif
+
 nsresult
 XPCJSRuntime::RemoveJSHolder(void* aHolder)
 {
+#ifdef DEBUG
+    // Assert that the holder doesn't try to keep any GC things alive.
+    // In case of unlinking cycle collector calls AssertNoObjectsToTrace
+    // manually because we don't want to check the holder before we are
+    // finished unlinking it
+    if (aHolder != mObjectToUnlink) {
+        AssertNoObjectsToTrace(aHolder);
+    }
+#endif
+    bool hadOne = mJSHolders.Count() == 1;
     mJSHolders.Remove(aHolder);
+    if (hadOne && mJSHolders.Count() == 0) {
+      nsLayoutStatics::Release();
+    }
 
     return NS_OK;
 }
 
 nsresult
 XPCJSRuntime::TestJSHolder(void* aHolder, bool* aRetval)
 {
     *aRetval = mJSHolders.Get(aHolder, nullptr);
@@ -2398,16 +2432,19 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
    mWrappedJSRoots(nullptr),
    mObjectHolderRoots(nullptr),
    mWatchdogLock(nullptr),
    mWatchdogWakeup(nullptr),
    mWatchdogThread(nullptr),
    mWatchdogHibernating(false),
    mLastActiveTime(-1),
    mExceptionManagerNotAvailable(false)
+#ifdef DEBUG
+   , mObjectToUnlink(nullptr)
+#endif
 {
 #ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
     DEBUG_WrappedNativeHashtable =
         JS_NewDHashTable(JS_DHashGetStubOps(), nullptr,
                          sizeof(JSDHashEntryStub), 128);
 #endif
 
     DOM_InitInterfaces();
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -2708,16 +2708,32 @@ nsXPConnect::WriteFunction(nsIObjectOutp
 }
 
 NS_IMETHODIMP
 nsXPConnect::ReadFunction(nsIObjectInputStream *stream, JSContext *cx, JSObject **functionObjp)
 {
     return ReadScriptOrFunction(stream, cx, nullptr, functionObjp);
 }
 
+#ifdef DEBUG
+void
+nsXPConnect::SetObjectToUnlink(void* aObject)
+{
+    if (mRuntime)
+        mRuntime->SetObjectToUnlink(aObject);
+}
+
+void
+nsXPConnect::AssertNoObjectsToTrace(void* aPossibleJSHolder)
+{
+    if (mRuntime)
+        mRuntime->AssertNoObjectsToTrace(aPossibleJSHolder);
+}
+#endif
+
 /* These are here to be callable from a debugger */
 JS_BEGIN_EXTERN_C
 JS_EXPORT_API(void) DumpJSStack()
 {
     nsresult rv;
     nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
     if (NS_SUCCEEDED(rv) && xpc)
         xpc->DebugDumpJSStack(true, true, false);
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -480,16 +480,21 @@ public:
     // non-interface implementation
 public:
     // These get non-addref'd pointers
     static nsXPConnect*  GetXPConnect();
     static nsXPConnect*  FastGetXPConnect() { return gSelf ? gSelf : GetXPConnect(); }
     static XPCJSRuntime* GetRuntimeInstance();
     XPCJSRuntime* GetRuntime() {return mRuntime;}
 
+#ifdef DEBUG
+    void SetObjectToUnlink(void* aObject);
+    void AssertNoObjectsToTrace(void* aPossibleJSHolder);
+#endif
+
     // Gets addref'd pointer
     static nsresult GetInterfaceInfoManager(nsIInterfaceInfoSuperManager** iim,
                                             nsXPConnect* xpc = nullptr);
 
     static JSBool IsISupportsDescendant(nsIInterfaceInfo* info);
 
     nsIXPCSecurityManager* GetDefaultSecurityManager() const
     {
@@ -820,16 +825,20 @@ public:
 
     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);
+#ifdef DEBUG
+    void SetObjectToUnlink(void* aObject) { mObjectToUnlink = aObject; }
+    void AssertNoObjectsToTrace(void* aPossibleJSHolder);
+#endif
 
     static void SuspectWrappedNative(XPCWrappedNative *wrapper,
                                      nsCycleCollectionTraversalCallback &cb);
 
     void DebugDump(int16_t depth);
 
     void SystemIsBeingShutDown();
 
@@ -985,16 +994,20 @@ private:
         mozilla::AlignedStorage2<XPCReadableJSStringWrapper> mString;
         bool mInUse;
     };
 
     StringWrapperEntry mScratchStrings[XPCCCX_STRING_CACHE_SIZE];
 
     friend class AutoLockWatchdog;
     friend class XPCIncrementalReleaseRunnable;
+
+#ifdef DEBUG
+    void* mObjectToUnlink;
+#endif
 };
 
 /***************************************************************************/
 /***************************************************************************/
 // XPCContext is mostly a dumb class to hold JSContext specific data and
 // maps that let us find wrappers created for the given JSContext.
 
 // no virtuals
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -2376,17 +2376,28 @@ nsCycleCollector::CollectWhite(nsICycleC
             PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
             aListener->DescribeGarbage((uint64_t)pinfo->mPointer);
         }
         aListener->End();
     }
 
     for (uint32_t i = 0; i < count; ++i) {
         PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
+#ifdef DEBUG
+        if (mJSRuntime) {
+            mJSRuntime->SetObjectToUnlink(pinfo->mPointer);
+        }
+#endif
         rv = pinfo->mParticipant->Unlink(pinfo->mPointer);
+#ifdef DEBUG
+        if (mJSRuntime) {
+            mJSRuntime->SetObjectToUnlink(nullptr);
+            mJSRuntime->AssertNoObjectsToTrace(pinfo->mPointer);
+        }
+#endif
         if (NS_FAILED(rv)) {
             Fault("Failed unlink call while unlinking", pinfo);
 #ifdef DEBUG_CC
             mStats.mFailedUnlink++;
 #endif
         }
         else {
 #ifdef DEBUG_CC
--- a/xpcom/base/nsCycleCollector.h
+++ b/xpcom/base/nsCycleCollector.h
@@ -76,16 +76,21 @@ struct nsCycleCollectionJSRuntime
      * Runs the JavaScript GC. |reason| is a gcreason::Reason from jsfriendapi.h.
      */
     virtual void Collect(uint32_t reason) = 0;
 
     /**
      * Get the JS cycle collection participant.
      */
     virtual nsCycleCollectionParticipant *GetParticipant() = 0;
+
+#ifdef DEBUG
+    virtual void SetObjectToUnlink(void* aObject) = 0;
+    virtual void AssertNoObjectsToTrace(void* aPossibleJSHolder) = 0;
+#endif
 };
 
 // Helpers for interacting with JS
 void nsCycleCollector_registerJSRuntime(nsCycleCollectionJSRuntime *rt);
 void nsCycleCollector_forgetJSRuntime();
 
 #ifdef DEBUG
 void nsCycleCollector_DEBUG_shouldBeFreed(nsISupports *n);