Bug 1438541 P1 Track DOMEventTargetHelper objects in the nsIGlobalObject base class and always call DisconnectFromOwner() on them. r=smaug

This commit is contained in:
Ben Kelly 2018-02-21 10:53:22 -08:00
parent 582b8ef5c5
commit f696313a4a
6 changed files with 109 additions and 58 deletions

View File

@ -1102,29 +1102,6 @@ nsGlobalWindowInner::~nsGlobalWindowInner()
nsLayoutStatics::Release();
}
void
nsGlobalWindowInner::AddEventTargetObject(DOMEventTargetHelper* aObject)
{
mEventTargetObjects.PutEntry(aObject);
}
void
nsGlobalWindowInner::RemoveEventTargetObject(DOMEventTargetHelper* aObject)
{
mEventTargetObjects.RemoveEntry(aObject);
}
void
nsGlobalWindowInner::DisconnectEventTargetObjects()
{
for (auto iter = mEventTargetObjects.ConstIter(); !iter.Done();
iter.Next()) {
RefPtr<DOMEventTargetHelper> target = iter.Get()->GetKey();
target->DisconnectFromOwner();
}
mEventTargetObjects.Clear();
}
// static
void
nsGlobalWindowInner::ShutDown()
@ -6907,6 +6884,8 @@ void
nsGlobalWindowInner::AddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const
{
aWindowSizes.mDOMOtherSize += aWindowSizes.mState.mMallocSizeOf(this);
aWindowSizes.mDOMOtherSize +=
nsIGlobalObject::ShallowSizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf);
EventListenerManager* elm = GetExistingListenerManager();
if (elm) {
@ -6929,12 +6908,7 @@ nsGlobalWindowInner::AddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const
mNavigator->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
}
aWindowSizes.mDOMEventTargetsSize +=
mEventTargetObjects.ShallowSizeOfExcludingThis(
aWindowSizes.mState.mMallocSizeOf);
for (auto iter = mEventTargetObjects.ConstIter(); !iter.Done(); iter.Next()) {
DOMEventTargetHelper* et = iter.Get()->GetKey();
ForEachEventTargetObject([&] (DOMEventTargetHelper* et) {
if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
aWindowSizes.mDOMEventTargetsSize +=
iSizeOf->SizeOfEventTargetIncludingThis(
@ -6944,7 +6918,7 @@ nsGlobalWindowInner::AddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const
aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
}
++aWindowSizes.mDOMEventTargetsCount;
}
});
if (mPerformance) {
aWindowSizes.mDOMPerformanceUserEntries =

View File

@ -9,7 +9,6 @@
#include "nsPIDOMWindow.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
#include "nsRefPtrHashtable.h"
#include "nsInterfaceHashtable.h"
@ -92,7 +91,6 @@ class DialogValueHolder;
namespace mozilla {
class AbstractThread;
class DOMEventTargetHelper;
class ThrottledEventQueue;
namespace dom {
class BarProp;
@ -517,10 +515,6 @@ public:
void AddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const;
// Inner windows only.
void AddEventTargetObject(mozilla::DOMEventTargetHelper* aObject);
void RemoveEventTargetObject(mozilla::DOMEventTargetHelper* aObject);
void NotifyIdleObserver(IdleObserverHolder* aIdleObserverHolder,
bool aCallOnidle);
nsresult HandleIdleActiveEvent();
@ -1259,8 +1253,6 @@ private:
// Fire the JS engine's onNewGlobalObject hook. Only used on inner windows.
void FireOnNewGlobalObject();
void DisconnectEventTargetObjects();
// nsPIDOMWindow{Inner,Outer} should be able to see these helper methods.
friend class nsPIDOMWindowInner;
friend class nsPIDOMWindowOuter;
@ -1428,8 +1420,6 @@ protected:
// currently enabled on this window.
bool mAreDialogsEnabled;
nsTHashtable<nsPtrHashKey<mozilla::DOMEventTargetHelper> > mEventTargetObjects;
nsTArray<uint32_t> mEnabledSensors;
#if defined(MOZ_WIDGET_ANDROID)

View File

@ -11,7 +11,9 @@
#include "nsThreadUtils.h"
#include "nsHostObjectProtocolHandler.h"
using mozilla::MallocSizeOf;
using mozilla::Maybe;
using mozilla::DOMEventTargetHelper;
using mozilla::dom::ClientInfo;
using mozilla::dom::ServiceWorker;
using mozilla::dom::ServiceWorkerDescriptor;
@ -19,6 +21,8 @@ using mozilla::dom::ServiceWorkerDescriptor;
nsIGlobalObject::~nsIGlobalObject()
{
UnlinkHostObjectURIs();
DisconnectEventTargetObjects();
MOZ_DIAGNOSTIC_ASSERT(mEventTargetObjects.IsEmpty());
}
nsIPrincipal*
@ -120,6 +124,51 @@ nsIGlobalObject::TraverseHostObjectURIs(nsCycleCollectionTraversalCallback &aCb)
}
}
void
nsIGlobalObject::AddEventTargetObject(DOMEventTargetHelper* aObject)
{
MOZ_DIAGNOSTIC_ASSERT(aObject);
MOZ_ASSERT(!mEventTargetObjects.Contains(aObject));
mEventTargetObjects.PutEntry(aObject);
}
void
nsIGlobalObject::RemoveEventTargetObject(DOMEventTargetHelper* aObject)
{
MOZ_DIAGNOSTIC_ASSERT(aObject);
MOZ_ASSERT(mEventTargetObjects.Contains(aObject));
mEventTargetObjects.RemoveEntry(aObject);
}
void
nsIGlobalObject::ForEachEventTargetObject(const std::function<void(DOMEventTargetHelper*)>& aFunc) const
{
// Protect against the function call triggering a mutation of the hash table
// 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());
}
// Iterate the target list and call the function on each one.
for (auto target : targetList) {
aFunc(target);
}
}
void
nsIGlobalObject::DisconnectEventTargetObjects()
{
ForEachEventTargetObject([&] (DOMEventTargetHelper* aTarget) {
aTarget->DisconnectFromOwner();
// Calling DisconnectFromOwner() should result in
// RemoveEventTargetObject() being called.
MOZ_DIAGNOSTIC_ASSERT(!mEventTargetObjects.Contains(aTarget));
});
}
Maybe<ClientInfo>
nsIGlobalObject::GetClientInfo() const
{
@ -154,3 +203,11 @@ nsIGlobalObject::RemoveServiceWorker(ServiceWorker* aServiceWorker)
{
MOZ_DIAGNOSTIC_ASSERT(false, "this global should not have any service workers");
}
size_t
nsIGlobalObject::ShallowSizeOfExcludingThis(MallocSizeOf aSizeOf) const
{
size_t rtn = mHostObjectURIs.ShallowSizeOfExcludingThis(aSizeOf);
rtn += mEventTargetObjects.ShallowSizeOfExcludingThis(aSizeOf);
return rtn;
}

View File

@ -11,9 +11,11 @@
#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"
#include "nsTHashtable.h"
#include "js/TypeDecls.h"
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
@ -25,6 +27,7 @@ class nsCycleCollectionTraversalCallback;
class nsIPrincipal;
namespace mozilla {
class DOMEventTargetHelper;
namespace dom {
class ServiceWorker;
} // namespace dom
@ -34,6 +37,13 @@ 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;
bool mIsDying;
protected:
@ -84,6 +94,18 @@ public:
void UnlinkHostObjectURIs();
void TraverseHostObjectURIs(nsCycleCollectionTraversalCallback &aCb);
// DETH objects must register themselves on the global when they
// bind to it in order to get the DisconnectFromOwner() method
// called correctly. RemoveEventTargetObject() must be called
// before the DETH object is destroyed.
void AddEventTargetObject(mozilla::DOMEventTargetHelper* aObject);
void RemoveEventTargetObject(mozilla::DOMEventTargetHelper* aObject);
// Iterate the registered DETH objects and call the given function
// for each one.
void
ForEachEventTargetObject(const std::function<void(mozilla::DOMEventTargetHelper*)>& aFunc) const;
virtual bool IsInSyncOperation() { return false; }
virtual mozilla::Maybe<mozilla::dom::ClientInfo>
@ -115,6 +137,12 @@ protected:
{
mIsDying = true;
}
void
DisconnectEventTargetObjects();
size_t
ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aSizeOf) const;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIGlobalObject,

View File

@ -89,8 +89,8 @@ NS_IMPL_DOMTARGET_DEFAULTS(DOMEventTargetHelper)
DOMEventTargetHelper::~DOMEventTargetHelper()
{
if (nsPIDOMWindowInner* owner = GetOwner()) {
nsGlobalWindowInner::Cast(owner)->RemoveEventTargetObject(this);
if (mParentObject) {
mParentObject->RemoveEventTargetObject(this);
}
if (mListenerManager) {
mListenerManager->Disconnect();
@ -108,23 +108,21 @@ DOMEventTargetHelper::BindToOwner(nsPIDOMWindowInner* aOwner)
void
DOMEventTargetHelper::BindToOwner(nsIGlobalObject* aOwner)
{
nsCOMPtr<nsIGlobalObject> parentObject = do_QueryReferent(mParentObject);
if (parentObject) {
if (mParentObject) {
mParentObject->RemoveEventTargetObject(this);
if (mOwnerWindow) {
nsGlobalWindowInner::Cast(mOwnerWindow)->RemoveEventTargetObject(this);
mOwnerWindow = nullptr;
}
mParentObject = nullptr;
mHasOrHasHadOwnerWindow = false;
}
if (aOwner) {
mParentObject = do_GetWeakReference(aOwner);
MOZ_ASSERT(mParentObject, "All nsIGlobalObjects must support nsISupportsWeakReference");
mParentObject = aOwner;
aOwner->AddEventTargetObject(this);
// Let's cache the result of this QI for fast access and off main thread usage
mOwnerWindow = nsCOMPtr<nsPIDOMWindowInner>(do_QueryInterface(aOwner)).get();
if (mOwnerWindow) {
mHasOrHasHadOwnerWindow = true;
nsGlobalWindowInner::Cast(mOwnerWindow)->AddEventTargetObject(this);
}
}
}
@ -132,22 +130,23 @@ DOMEventTargetHelper::BindToOwner(nsIGlobalObject* aOwner)
void
DOMEventTargetHelper::BindToOwner(DOMEventTargetHelper* aOther)
{
if (mParentObject) {
mParentObject->RemoveEventTargetObject(this);
}
if (mOwnerWindow) {
nsGlobalWindowInner::Cast(mOwnerWindow)->RemoveEventTargetObject(this);
mOwnerWindow = nullptr;
mParentObject = nullptr;
mHasOrHasHadOwnerWindow = false;
}
if (aOther) {
mHasOrHasHadOwnerWindow = aOther->HasOrHasHadOwner();
if (aOther->GetParentObject()) {
mParentObject = do_GetWeakReference(aOther->GetParentObject());
MOZ_ASSERT(mParentObject, "All nsIGlobalObjects must support nsISupportsWeakReference");
mParentObject = aOther->GetParentObject();
if (mParentObject) {
mParentObject->AddEventTargetObject(this);
// Let's cache the result of this QI for fast access and off main thread usage
mOwnerWindow = nsCOMPtr<nsPIDOMWindowInner>(do_QueryInterface(aOther->GetParentObject())).get();
mOwnerWindow = nsCOMPtr<nsPIDOMWindowInner>(do_QueryInterface(mParentObject)).get();
if (mOwnerWindow) {
mHasOrHasHadOwnerWindow = true;
nsGlobalWindowInner::Cast(mOwnerWindow)->AddEventTargetObject(this);
}
}
}
@ -156,6 +155,9 @@ DOMEventTargetHelper::BindToOwner(DOMEventTargetHelper* aOther)
void
DOMEventTargetHelper::DisconnectFromOwner()
{
if (mParentObject) {
mParentObject->RemoveEventTargetObject(this);
}
mOwnerWindow = nullptr;
mParentObject = nullptr;
// Event listeners can't be handled anymore, so we can release them here.

View File

@ -146,8 +146,7 @@ public:
using EventTarget::GetParentObject;
virtual nsIGlobalObject* GetOwnerGlobal() const override
{
nsCOMPtr<nsIGlobalObject> parentObject = do_QueryReferent(mParentObject);
return parentObject;
return mParentObject;
}
bool HasOrHasHadOwner() { return mHasOrHasHadOwnerWindow; }
@ -195,8 +194,9 @@ protected:
void IgnoreKeepAliveIfHasListenersFor(nsAtom* aType);
private:
// Inner window or sandbox.
nsWeakPtr mParentObject;
// The parent global object. The global will clear this when
// it is destroyed by calling DisconnectFromOwner().
nsIGlobalObject* MOZ_NON_OWNING_REF mParentObject;
// mParentObject pre QI-ed and cached (inner window)
// (it is needed for off main thread access)
// It is obtained in BindToOwner and reset in DisconnectFromOwner.