Have BrowsingContext keep its own cache to enable caching of BrowsingContexts, especially in the parent process. This isn't really optimal, since it effectively duplicates the cache in the child process. BFcache keeps a list of strong pointers to the list of cached nsDocShells, where each nsDocShell in turn keeps a reciprocated strong pointer to its BrowsingContext, which in turn is held in the BrowsingContexts list of cached contexts. Ideally these caches should be merged.
243 lines
6.4 KiB
C++
243 lines
6.4 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* 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/. */
|
|
|
|
#include "BrowsingContext.h"
|
|
|
|
#include "mozilla/dom/ContentChild.h"
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/StaticPtr.h"
|
|
|
|
#include "nsDataHashtable.h"
|
|
#include "nsRefPtrHashtable.h"
|
|
#include "nsIDocShell.h"
|
|
#include "nsContentUtils.h"
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
static LazyLogModule gBrowsingContextLog("BrowsingContext");
|
|
|
|
static StaticAutoPtr<BrowsingContext::Children> sRootBrowsingContexts;
|
|
|
|
static StaticAutoPtr<nsDataHashtable<nsUint64HashKey, BrowsingContext*>>
|
|
sBrowsingContexts;
|
|
|
|
// TODO(farre): This duplicates some of the work performed by the
|
|
// bfcache. This should be unified. [Bug 1471601]
|
|
static StaticAutoPtr<nsRefPtrHashtable<nsUint64HashKey, BrowsingContext>>
|
|
sCachedBrowsingContexts;
|
|
|
|
/* static */ void
|
|
BrowsingContext::Init()
|
|
{
|
|
if (!sRootBrowsingContexts) {
|
|
sRootBrowsingContexts = new BrowsingContext::Children();
|
|
ClearOnShutdown(&sRootBrowsingContexts);
|
|
}
|
|
|
|
if (!sBrowsingContexts) {
|
|
sBrowsingContexts =
|
|
new nsDataHashtable<nsUint64HashKey, BrowsingContext*>();
|
|
ClearOnShutdown(&sBrowsingContexts);
|
|
}
|
|
|
|
if (!sCachedBrowsingContexts) {
|
|
sCachedBrowsingContexts =
|
|
new nsRefPtrHashtable<nsUint64HashKey, BrowsingContext>();
|
|
ClearOnShutdown(&sCachedBrowsingContexts);
|
|
}
|
|
}
|
|
|
|
/* static */ LogModule*
|
|
BrowsingContext::GetLog()
|
|
{
|
|
return gBrowsingContextLog;
|
|
}
|
|
|
|
/* static */ already_AddRefed<BrowsingContext>
|
|
BrowsingContext::Get(uint64_t aId)
|
|
{
|
|
RefPtr<BrowsingContext> abc = sBrowsingContexts->Get(aId);
|
|
return abc.forget();
|
|
}
|
|
|
|
BrowsingContext::BrowsingContext(nsIDocShell* aDocShell)
|
|
: mBrowsingContextId(nsContentUtils::GenerateBrowsingContextId())
|
|
, mProcessId(Nothing())
|
|
, mDocShell(aDocShell)
|
|
{
|
|
sBrowsingContexts->Put(mBrowsingContextId, this);
|
|
}
|
|
|
|
BrowsingContext::BrowsingContext(uint64_t aBrowsingContextId,
|
|
const nsAString& aName,
|
|
const Maybe<uint64_t>& aProcessId)
|
|
: mBrowsingContextId(aBrowsingContextId)
|
|
, mProcessId(aProcessId)
|
|
, mName(aName)
|
|
{
|
|
// mProcessId only really has a meaning in the parent process, where
|
|
// it keeps track of which BrowsingContext is actually holding the
|
|
// nsDocShell.
|
|
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || aProcessId.isNothing());
|
|
sBrowsingContexts->Put(mBrowsingContextId, this);
|
|
}
|
|
|
|
void
|
|
BrowsingContext::Attach(BrowsingContext* aParent)
|
|
{
|
|
if (isInList()) {
|
|
MOZ_LOG(GetLog(),
|
|
LogLevel::Debug,
|
|
("%s: Connecting already existing 0x%08" PRIx64 " to 0x%08" PRIx64,
|
|
XRE_IsParentProcess() ? "Parent" : "Child",
|
|
Id(),
|
|
aParent ? aParent->Id() : 0));
|
|
MOZ_DIAGNOSTIC_ASSERT(sBrowsingContexts->Contains(Id()));
|
|
MOZ_DIAGNOSTIC_ASSERT(!IsCached());
|
|
return;
|
|
}
|
|
|
|
bool wasCached = sCachedBrowsingContexts->Remove(Id());
|
|
|
|
MOZ_LOG(GetLog(),
|
|
LogLevel::Debug,
|
|
("%s: %s 0x%08" PRIx64 " to 0x%08" PRIx64,
|
|
wasCached ? "Re-connecting" : "Connecting",
|
|
XRE_IsParentProcess() ? "Parent" : "Child",
|
|
Id(),
|
|
aParent ? aParent->Id() : 0));
|
|
|
|
auto* children = aParent ? &aParent->mChildren : sRootBrowsingContexts.get();
|
|
children->insertBack(this);
|
|
mParent = aParent;
|
|
|
|
if (!XRE_IsContentProcess()) {
|
|
return;
|
|
}
|
|
|
|
auto cc = dom::ContentChild::GetSingleton();
|
|
MOZ_DIAGNOSTIC_ASSERT(cc);
|
|
cc->SendAttachBrowsingContext(
|
|
dom::BrowsingContextId(mParent ? mParent->Id() : 0),
|
|
dom::BrowsingContextId(Id()),
|
|
mName);
|
|
}
|
|
|
|
void
|
|
BrowsingContext::Detach()
|
|
{
|
|
RefPtr<BrowsingContext> kungFuDeathGrip(this);
|
|
|
|
if (sCachedBrowsingContexts) {
|
|
sCachedBrowsingContexts->Remove(Id());
|
|
}
|
|
|
|
if (!isInList()) {
|
|
MOZ_LOG(GetLog(),
|
|
LogLevel::Debug,
|
|
("%s: Detaching already detached 0x%08" PRIx64,
|
|
XRE_IsParentProcess() ? "Parent" : "Child",
|
|
Id()));
|
|
return;
|
|
}
|
|
|
|
MOZ_LOG(GetLog(),
|
|
LogLevel::Debug,
|
|
("%s: Detaching 0x%08" PRIx64 " from 0x%08" PRIx64,
|
|
XRE_IsParentProcess() ? "Parent" : "Child",
|
|
Id(),
|
|
mParent ? mParent->Id() : 0));
|
|
|
|
remove();
|
|
|
|
if (!XRE_IsContentProcess()) {
|
|
return;
|
|
}
|
|
|
|
auto cc = dom::ContentChild::GetSingleton();
|
|
MOZ_DIAGNOSTIC_ASSERT(cc);
|
|
cc->SendDetachBrowsingContext(dom::BrowsingContextId(Id()),
|
|
false /* aMoveToBFCache */);
|
|
}
|
|
|
|
void
|
|
BrowsingContext::CacheChildren()
|
|
{
|
|
if (mChildren.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
MOZ_LOG(GetLog(),
|
|
LogLevel::Debug,
|
|
("%s: Caching children of 0x%08" PRIx64 "",
|
|
XRE_IsParentProcess() ? "Parent" : "Child",
|
|
Id()));
|
|
|
|
while (!mChildren.isEmpty()) {
|
|
RefPtr<BrowsingContext> child = mChildren.popFirst();
|
|
sCachedBrowsingContexts->Put(child->Id(), child);
|
|
}
|
|
|
|
if (!XRE_IsContentProcess()) {
|
|
return;
|
|
}
|
|
|
|
auto cc = dom::ContentChild::GetSingleton();
|
|
MOZ_DIAGNOSTIC_ASSERT(cc);
|
|
cc->SendDetachBrowsingContext(dom::BrowsingContextId(Id()),
|
|
true /* aMoveToBFCache */);
|
|
}
|
|
|
|
bool
|
|
BrowsingContext::IsCached()
|
|
{
|
|
return sCachedBrowsingContexts->Contains(Id());
|
|
}
|
|
|
|
uint64_t
|
|
BrowsingContext::OwnerProcessId() const
|
|
{
|
|
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
|
|
return mProcessId.value();
|
|
}
|
|
|
|
BrowsingContext::~BrowsingContext()
|
|
{
|
|
MOZ_DIAGNOSTIC_ASSERT(!isInList());
|
|
|
|
if (sBrowsingContexts) {
|
|
sBrowsingContexts->Remove(mBrowsingContextId);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ImplCycleCollectionUnlink(BrowsingContext::Children& aField)
|
|
{
|
|
aField.clear();
|
|
}
|
|
|
|
static void
|
|
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
|
|
BrowsingContext::Children& aField,
|
|
const char* aName,
|
|
uint32_t aFlags = 0)
|
|
{
|
|
for (BrowsingContext* aContext : aField) {
|
|
aCallback.NoteNativeChild(aContext,
|
|
NS_CYCLE_COLLECTION_PARTICIPANT(BrowsingContext));
|
|
}
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION(BrowsingContext, mDocShell, mChildren)
|
|
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(BrowsingContext, AddRef)
|
|
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(BrowsingContext, Release)
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|