bug 1457867, store DOMEventTargetHelper objects in global object as a linked list, r=bkelly
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Fri, 04 May 2018 19:25:05 +0300
changeset 473036 91ba35aee7500d305a5fb1446281268ef019d02c
parent 473035 f9c96567050b556a8e63d5c879dc77ea6b7a6f9d
child 473070 d577b7168a13aa32e1437b5a9e57c718c59302ea
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1457867
milestone61.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 1457867, store DOMEventTargetHelper objects in global object as a linked list, r=bkelly
dom/animation/AnimationTimeline.cpp
dom/animation/DocumentTimeline.cpp
dom/base/nsDocument.cpp
dom/base/nsIGlobalObject.cpp
dom/base/nsIGlobalObject.h
dom/events/DOMEventTargetHelper.h
layout/base/nsPresContext.cpp
layout/style/MediaQueryList.cpp
--- a/dom/animation/AnimationTimeline.cpp
+++ b/dom/animation/AnimationTimeline.cpp
@@ -43,16 +43,16 @@ AnimationTimeline::NotifyAnimationUpdate
     mAnimationOrder.insertBack(&aAnimation);
   }
 }
 
 void
 AnimationTimeline::RemoveAnimation(Animation* aAnimation)
 {
   MOZ_ASSERT(!aAnimation->GetTimeline() || aAnimation->GetTimeline() == this);
-  if (aAnimation->isInList()) {
-    aAnimation->remove();
+  if (static_cast<LinkedListElement<Animation>*>(aAnimation)->isInList()) {
+    static_cast<LinkedListElement<Animation>*>(aAnimation)->remove();
   }
   mAnimations.RemoveEntry(aAnimation);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/DocumentTimeline.cpp
+++ b/dom/animation/DocumentTimeline.cpp
@@ -162,17 +162,18 @@ DocumentTimeline::WillRefresh(mozilla::T
   // Note that this should be done before nsAutoAnimationMutationBatch.  If
   // PerformMicroTaskCheckpoint was called before nsAutoAnimationMutationBatch
   // is destroyed, some mutation records might not be delivered in this
   // checkpoint.
   nsAutoMicroTask mt;
   nsAutoAnimationMutationBatch mb(mDocument);
 
   for (Animation* animation = mAnimationOrder.getFirst(); animation;
-       animation = animation->getNext()) {
+       animation =
+         static_cast<LinkedListElement<Animation>*>(animation)->getNext()) {
     // Skip any animations that are longer need associated with this timeline.
     if (animation->GetTimeline() != this) {
       // If animation has some other timeline, it better not be also in the
       // animation list of this timeline object!
       MOZ_ASSERT(!animation->GetTimeline());
       animationsToRemove.AppendElement(animation);
       continue;
     }
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1954,17 +1954,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     }
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
 
   // We own only the items in mDOMMediaQueryLists that have listeners;
   // this reference is managed by their AddListener and RemoveListener
   // methods.
-  for (auto mql : tmp->mDOMMediaQueryLists) {
+  for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;
+       mql = static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext()) {
     if (mql->HasListeners()) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
       cb.NoteXPCOMChild(mql);
     }
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
@@ -2074,17 +2075,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     tmp->mCSSLoader->DropDocumentReference();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
   }
 
   // We own only the items in mDOMMediaQueryLists that have listeners;
   // this reference is managed by their AddListener and RemoveListener
   // methods.
   for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;) {
-    MediaQueryList* next = mql->getNext();
+    MediaQueryList* next =
+      static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext();
     mql->Disconnect();
     mql = next;
   }
 
   tmp->mInUnlinkOrDeletion = false;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 nsresult
--- a/dom/base/nsIGlobalObject.cpp
+++ b/dom/base/nsIGlobalObject.cpp
@@ -20,17 +20,17 @@ using mozilla::dom::ServiceWorker;
 using mozilla::dom::ServiceWorkerDescriptor;
 using mozilla::dom::ServiceWorkerRegistration;
 using mozilla::dom::ServiceWorkerRegistrationDescriptor;
 
 nsIGlobalObject::~nsIGlobalObject()
 {
   UnlinkHostObjectURIs();
   DisconnectEventTargetObjects();
-  MOZ_DIAGNOSTIC_ASSERT(mEventTargetObjects.IsEmpty());
+  MOZ_DIAGNOSTIC_ASSERT(mEventTargetObjects.isEmpty());
 }
 
 nsIPrincipal*
 nsIGlobalObject::PrincipalOrNull()
 {
   JSObject *global = GetGlobalJSObject();
   if (NS_WARN_IF(!global))
     return nullptr;
@@ -126,45 +126,47 @@ nsIGlobalObject::TraverseHostObjectURIs(
     nsHostObjectProtocolHandler::Traverse(mHostObjectURIs[index], aCb);
   }
 }
 
 void
 nsIGlobalObject::AddEventTargetObject(DOMEventTargetHelper* aObject)
 {
   MOZ_DIAGNOSTIC_ASSERT(aObject);
-  MOZ_ASSERT(!mEventTargetObjects.Contains(aObject));
-  mEventTargetObjects.PutEntry(aObject);
+  MOZ_ASSERT(!aObject->isInList());
+  mEventTargetObjects.insertBack(aObject);
 }
 
 void
 nsIGlobalObject::RemoveEventTargetObject(DOMEventTargetHelper* aObject)
 {
   MOZ_DIAGNOSTIC_ASSERT(aObject);
-  MOZ_ASSERT(mEventTargetObjects.Contains(aObject));
-  mEventTargetObjects.RemoveEntry(aObject);
+  MOZ_ASSERT(aObject->isInList());
+  MOZ_ASSERT(aObject->GetOwnerGlobal() == this);
+  aObject->remove();
 }
 
 void
 nsIGlobalObject::ForEachEventTargetObject(const std::function<void(DOMEventTargetHelper*, bool* aDoneOut)>& aFunc) const
 {
-  // Protect against the function call triggering a mutation of the hash table
+  // Protect against the function call triggering a mutation of the list
   // while we are iterating by copying the DETH references to a temporary
   // list.
   AutoTArray<DOMEventTargetHelper*, 64> targetList;
-  for (auto iter = mEventTargetObjects.ConstIter(); !iter.Done(); iter.Next()) {
-    targetList.AppendElement(iter.Get()->GetKey());
+  for (const DOMEventTargetHelper* deth = mEventTargetObjects.getFirst();
+       deth; deth = deth->getNext()) {
+    targetList.AppendElement(const_cast<DOMEventTargetHelper*>(deth));
   }
 
   // Iterate the target list and call the function on each one.
   bool done = false;
   for (auto target : targetList) {
     // Check to see if a previous iteration's callback triggered the removal
     // of this target as a side-effect.  If it did, then just ignore it.
-    if (!mEventTargetObjects.Contains(target)) {
+    if (target->GetOwnerGlobal() != this) {
       continue;
     }
     aFunc(target, &done);
     if (done) {
       break;
     }
   }
 }
@@ -172,17 +174,17 @@ nsIGlobalObject::ForEachEventTargetObjec
 void
 nsIGlobalObject::DisconnectEventTargetObjects()
 {
   ForEachEventTargetObject([&] (DOMEventTargetHelper* aTarget, bool* aDoneOut) {
     aTarget->DisconnectFromOwner();
 
     // Calling DisconnectFromOwner() should result in
     // RemoveEventTargetObject() being called.
-    MOZ_DIAGNOSTIC_ASSERT(!mEventTargetObjects.Contains(aTarget));
+    MOZ_DIAGNOSTIC_ASSERT(aTarget->GetOwnerGlobal() != this);
   });
 }
 
 Maybe<ClientInfo>
 nsIGlobalObject::GetClientInfo() const
 {
   // By default globals do not expose themselves as a client.  Only real
   // window and worker globals are currently considered clients.
@@ -210,11 +212,10 @@ nsIGlobalObject::GetOrCreateServiceWorke
   MOZ_DIAGNOSTIC_ASSERT(false, "this global should not have any service worker registrations");
   return nullptr;
 }
 
 size_t
 nsIGlobalObject::ShallowSizeOfExcludingThis(MallocSizeOf aSizeOf) const
 {
   size_t rtn = mHostObjectURIs.ShallowSizeOfExcludingThis(aSizeOf);
-  rtn += mEventTargetObjects.ShallowSizeOfExcludingThis(aSizeOf);
   return rtn;
 }
--- a/dom/base/nsIGlobalObject.h
+++ b/dom/base/nsIGlobalObject.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsIGlobalObject_h__
 #define nsIGlobalObject_h__
 
+#include "mozilla/LinkedList.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/dom/ClientInfo.h"
 #include "mozilla/dom/DispatcherTrait.h"
 #include "mozilla/dom/ServiceWorkerDescriptor.h"
 #include "nsHashKeys.h"
 #include "nsISupports.h"
 #include "nsStringFwd.h"
 #include "nsTArray.h"
@@ -36,20 +37,18 @@ class ServiceWorkerRegistrationDescripto
 } // namespace mozilla
 
 class nsIGlobalObject : public nsISupports,
                         public mozilla::dom::DispatcherTrait
 {
   nsTArray<nsCString> mHostObjectURIs;
 
   // Raw pointers to bound DETH objects.  These are added by
-  // AddEventTargetObject().  The DETH object must call
-  // RemoveEventTargetObject() before its destroyed to clear
-  // its raw pointer here.
-  nsTHashtable<nsPtrHashKey<mozilla::DOMEventTargetHelper>> mEventTargetObjects;
+  // AddEventTargetObject().
+  mozilla::LinkedList<mozilla::DOMEventTargetHelper> mEventTargetObjects;
 
   bool mIsDying;
 
 protected:
   nsIGlobalObject()
    : mIsDying(false)
   {}
 
--- a/dom/events/DOMEventTargetHelper.h
+++ b/dom/events/DOMEventTargetHelper.h
@@ -12,16 +12,17 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsPIDOMWindow.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsIWeakReferenceUtils.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EventListenerManager.h"
+#include "mozilla/LinkedList.h"
 #include "mozilla/dom/EventTarget.h"
 
 struct JSCompartment;
 class nsIDocument;
 
 namespace mozilla {
 
 class ErrorResult;
@@ -29,17 +30,18 @@ class ErrorResult;
 namespace dom {
 class Event;
 } // namespace dom
 
 #define NS_DOMEVENTTARGETHELPER_IID \
 { 0xa28385c6, 0x9451, 0x4d7e, \
   { 0xa3, 0xdd, 0xf4, 0xb6, 0x87, 0x2f, 0xa4, 0x76 } }
 
-class DOMEventTargetHelper : public dom::EventTarget
+class DOMEventTargetHelper : public dom::EventTarget,
+                             public LinkedListElement<DOMEventTargetHelper>
 {
 public:
   DOMEventTargetHelper()
     : mParentObject(nullptr)
     , mOwnerWindow(nullptr)
     , mHasOrHasHadOwnerWindow(false)
     , mIsKeptAlive(false)
   {
@@ -167,17 +169,17 @@ public:
   //       DOMEventTargetHelper::BindToOwner(aOwner).
   virtual void BindToOwner(nsIGlobalObject* aOwner);
 
   void BindToOwner(nsPIDOMWindowInner* aOwner);
   void BindToOwner(DOMEventTargetHelper* aOther);
 
   virtual void DisconnectFromOwner();
   using EventTarget::GetParentObject;
-  virtual nsIGlobalObject* GetOwnerGlobal() const override
+  virtual nsIGlobalObject* GetOwnerGlobal() const final
   {
     return mParentObject;
   }
   bool HasOrHasHadOwner() { return mHasOrHasHadOwnerWindow; }
 
   virtual void EventListenerAdded(nsAtom* aType) override;
   virtual void EventListenerAdded(const nsAString& aType) override;
 
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2079,17 +2079,18 @@ nsPresContext::FlushPendingMediaFeatureV
   }
 
   // We build a list of all the notifications we're going to send
   // before we send any of them.
 
   // Copy pointers to all the lists into a new array, in case one of our
   // notifications modifies the list.
   nsTArray<RefPtr<mozilla::dom::MediaQueryList>> localMediaQueryLists;
-  for (auto* mql : mDocument->MediaQueryLists()) {
+  for (MediaQueryList* mql = mDocument->MediaQueryLists().getFirst(); mql;
+       mql = static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext()) {
     localMediaQueryLists.AppendElement(mql);
   }
 
   // Now iterate our local array of the lists.
   for (const auto& mql : localMediaQueryLists) {
     nsAutoMicroTask mt;
     mql->MaybeNotify();
   }
--- a/layout/style/MediaQueryList.cpp
+++ b/layout/style/MediaQueryList.cpp
@@ -40,17 +40,17 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQuer
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaQueryList,
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaQueryList,
                                                 DOMEventTargetHelper)
   if (tmp->mDocument) {
-    tmp->remove();
+    static_cast<LinkedListElement<MediaQueryList>*>(tmp)->remove();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
   }
   tmp->Disconnect();
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaQueryList)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)