Bug 1495748: Make sure ChannelWrappers get cleaned up before JS engine shutdown. r=aswan,mccr8

The gross contortions here are required to deal with the deferred finalizers
HTTP channels use for their property bags. The actual channels get destroyed
relatively early during shutdown, but their property bag hashes which hold our
ChannelWrapper reference end up being destroyed after JS engine shutdown,
which gives us no good point to clear our reference.

The stub holder class takes the place of our existing property bag entry, and
behaves more or less the same, but allows us to cut the reference to the
ChannelWrapper without having a strong reference to the channel.

Differential Revision: https://phabricator.services.mozilla.com/D8923
This commit is contained in:
Kris Maglione
2018-10-16 15:05:24 -07:00
parent 5a1442cde6
commit be3b740132
2 changed files with 102 additions and 7 deletions

View File

@@ -16,6 +16,7 @@
#include "nsITransportSecurityInfo.h"
#include "mozilla/AddonManagerWebAPI.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/ErrorNames.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/Unused.h"
@@ -32,6 +33,7 @@
#include "nsIProxiedChannel.h"
#include "nsIProxyInfo.h"
#include "nsITraceableChannel.h"
#include "nsIWritablePropertyBag.h"
#include "nsIWritablePropertyBag2.h"
#include "nsNetUtil.h"
#include "nsProxyRelease.h"
@@ -45,12 +47,81 @@ namespace extensions {
#define CHANNELWRAPPER_PROP_KEY NS_LITERAL_STRING("ChannelWrapper::CachedInstance")
/*****************************************************************************
* Lifetimes
*****************************************************************************/
namespace {
class ChannelListHolder : public LinkedList<ChannelWrapper>
{
public:
ChannelListHolder()
: LinkedList<ChannelWrapper>()
{}
~ChannelListHolder();
};
} // anonymous namespace
ChannelListHolder::~ChannelListHolder()
{
while (ChannelWrapper* wrapper = popFirst()) {
wrapper->Die();
}
}
static LinkedList<ChannelWrapper>&
ChannelList()
{
static UniquePtr<ChannelListHolder> sChannelList;
if (!sChannelList) {
sChannelList.reset(new ChannelListHolder());
ClearOnShutdown(&sChannelList, ShutdownPhase::Shutdown);
}
return *sChannelList;
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(ChannelWrapper::ChannelWrapperStub)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ChannelWrapper::ChannelWrapperStub)
NS_IMPL_CYCLE_COLLECTION(ChannelWrapper::ChannelWrapperStub, mChannelWrapper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChannelWrapper::ChannelWrapperStub)
NS_INTERFACE_MAP_ENTRY_TEAROFF(ChannelWrapper, mChannelWrapper)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
/*****************************************************************************
* Initialization
*****************************************************************************/
/* static */
ChannelWrapper::ChannelWrapper(nsISupports* aParent, nsIChannel* aChannel)
: ChannelHolder(aChannel)
, mParent(aParent)
{
mStub = new ChannelWrapperStub(this);
ChannelList().insertBack(this);
}
ChannelWrapper::~ChannelWrapper()
{
if (LinkedListElement<ChannelWrapper>::isInList()) {
LinkedListElement<ChannelWrapper>::remove();
}
}
void
ChannelWrapper::Die()
{
if (mStub) {
mStub->mChannelWrapper = nullptr;
}
}
/* static */
already_AddRefed<ChannelWrapper>
ChannelWrapper::Get(const GlobalObject& global, nsIChannel* channel)
{
@@ -72,7 +143,7 @@ ChannelWrapper::Get(const GlobalObject& global, nsIChannel* channel)
wrapper = new ChannelWrapper(global.GetAsSupports(), channel);
if (props) {
Unused << props->SetPropertyAsInterface(CHANNELWRAPPER_PROP_KEY,
wrapper);
wrapper->mStub);
}
}
@@ -1055,10 +1126,12 @@ NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ChannelWrapper, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mStub)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ChannelWrapper, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStub)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ChannelWrapper, DOMEventTargetHelper)

View File

@@ -15,6 +15,7 @@
#include "mozilla/extensions/WebExtensionPolicy.h"
#include "mozilla/Attributes.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/WeakPtr.h"
@@ -111,6 +112,7 @@ class WebRequestChannelEntry;
class ChannelWrapper final : public DOMEventTargetHelper
, public SupportsWeakPtr<ChannelWrapper>
, public LinkedListElement<ChannelWrapper>
, private detail::ChannelHolder
{
public:
@@ -120,6 +122,8 @@ public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CHANNELWRAPPER_IID)
void Die();
static already_AddRefed<extensions::ChannelWrapper> Get(const dom::GlobalObject& global, nsIChannel* channel);
static already_AddRefed<extensions::ChannelWrapper> GetRegisteredChannel(const dom::GlobalObject& global, uint64_t aChannelId, const WebExtensionPolicy& aAddon, nsITabParent* aTabParent);
@@ -241,13 +245,10 @@ public:
JSObject* WrapObject(JSContext* aCx, JS::HandleObject aGivenProto) override;
protected:
~ChannelWrapper() = default;
~ChannelWrapper();
private:
ChannelWrapper(nsISupports* aParent, nsIChannel* aChannel)
: ChannelHolder(aChannel)
, mParent(aParent)
{}
ChannelWrapper(nsISupports* aParent, nsIChannel* aChannel);
void ClearCachedAttributes();
@@ -279,6 +280,27 @@ private:
void CheckEventListeners();
class ChannelWrapperStub final : public nsISupports
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(ChannelWrapperStub)
explicit ChannelWrapperStub(ChannelWrapper* aChannelWrapper)
: mChannelWrapper(aChannelWrapper)
{}
private:
friend class ChannelWrapper;
RefPtr<ChannelWrapper> mChannelWrapper;
protected:
~ChannelWrapperStub() = default;
};
RefPtr<ChannelWrapperStub> mStub;
mutable Maybe<URLInfo> mFinalURLInfo;
mutable Maybe<URLInfo> mDocumentURLInfo;