Add top-level and auxiliary browsing contexts to a group of BrowsingContexts on creation and store a pointer to that group in all children of the BrowsingContexts in the group. With this it is possible to compute the transitive closure of related browsing contexts. Since we'll not be using linked lists of BrowsingContexts for neither groups nor children we can move children to be an array of BrowsingContexts and adjust to use a the more convenient HashMap for roots. Differential Revision: https://phabricator.services.mozilla.com/D13227
330 lines
10 KiB
C++
330 lines
10 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 "mozilla/dom/BrowsingContext.h"
|
|
|
|
#include "mozilla/dom/ChromeBrowsingContext.h"
|
|
#include "mozilla/dom/BrowsingContextBinding.h"
|
|
#include "mozilla/dom/ContentChild.h"
|
|
#include "mozilla/dom/ContentParent.h"
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "mozilla/HashTable.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/StaticPtr.h"
|
|
|
|
#include "nsDocShell.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsThreadUtils.h"
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
static LazyLogModule gBrowsingContextLog("BrowsingContext");
|
|
|
|
static StaticAutoPtr<BrowsingContext::Children> sRootBrowsingContexts;
|
|
|
|
template <template <typename> class PtrType>
|
|
using BrowsingContextMap =
|
|
HashMap<uint64_t, PtrType<BrowsingContext>, DefaultHasher<uint64_t>,
|
|
InfallibleAllocPolicy>;
|
|
|
|
static StaticAutoPtr<BrowsingContextMap<WeakPtr>> sBrowsingContexts;
|
|
|
|
// TODO(farre): This duplicates some of the work performed by the
|
|
// bfcache. This should be unified. [Bug 1471601]
|
|
static StaticAutoPtr<BrowsingContextMap<RefPtr>> sCachedBrowsingContexts;
|
|
|
|
static void Register(BrowsingContext* aBrowsingContext) {
|
|
MOZ_ALWAYS_TRUE(
|
|
sBrowsingContexts->putNew(aBrowsingContext->Id(), aBrowsingContext));
|
|
}
|
|
|
|
static void Sync(BrowsingContext* aBrowsingContext) {
|
|
if (!XRE_IsContentProcess()) {
|
|
return;
|
|
}
|
|
|
|
auto cc = ContentChild::GetSingleton();
|
|
MOZ_DIAGNOSTIC_ASSERT(cc);
|
|
nsAutoString name;
|
|
aBrowsingContext->GetName(name);
|
|
RefPtr<BrowsingContext> parent = aBrowsingContext->GetParent();
|
|
BrowsingContext* opener = aBrowsingContext->GetOpener();
|
|
cc->SendAttachBrowsingContext(BrowsingContextId(parent ? parent->Id() : 0),
|
|
BrowsingContextId(opener ? opener->Id() : 0),
|
|
BrowsingContextId(aBrowsingContext->Id()),
|
|
name);
|
|
}
|
|
|
|
/* static */ void BrowsingContext::Init() {
|
|
if (!sRootBrowsingContexts) {
|
|
sRootBrowsingContexts = new BrowsingContext::Children();
|
|
ClearOnShutdown(&sRootBrowsingContexts);
|
|
}
|
|
|
|
if (!sBrowsingContexts) {
|
|
sBrowsingContexts = new BrowsingContextMap<WeakPtr>();
|
|
ClearOnShutdown(&sBrowsingContexts);
|
|
}
|
|
|
|
if (!sCachedBrowsingContexts) {
|
|
sCachedBrowsingContexts = new BrowsingContextMap<RefPtr>();
|
|
ClearOnShutdown(&sCachedBrowsingContexts);
|
|
}
|
|
}
|
|
|
|
/* static */ LogModule* BrowsingContext::GetLog() {
|
|
return gBrowsingContextLog;
|
|
}
|
|
|
|
/* static */ already_AddRefed<BrowsingContext> BrowsingContext::Get(
|
|
uint64_t aId) {
|
|
if (BrowsingContextMap<WeakPtr>::Ptr abc = sBrowsingContexts->lookup(aId)) {
|
|
return do_AddRef(abc->value().get());
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/* static */ already_AddRefed<BrowsingContext> BrowsingContext::Create(
|
|
BrowsingContext* aParent, BrowsingContext* aOpener, const nsAString& aName,
|
|
Type aType) {
|
|
MOZ_DIAGNOSTIC_ASSERT(!aParent || aParent->mType == aType);
|
|
|
|
uint64_t id = nsContentUtils::GenerateBrowsingContextId();
|
|
|
|
MOZ_LOG(GetLog(), LogLevel::Debug,
|
|
("Creating 0x%08" PRIx64 " in %s", id,
|
|
XRE_IsParentProcess() ? "Parent" : "Child"));
|
|
|
|
RefPtr<BrowsingContext> context;
|
|
if (XRE_IsParentProcess()) {
|
|
context = new ChromeBrowsingContext(aParent, aOpener, aName, id,
|
|
/* aProcessId */ 0, aType);
|
|
} else {
|
|
context = new BrowsingContext(aParent, aOpener, aName, id, aType);
|
|
}
|
|
|
|
Register(context);
|
|
|
|
// Attach the browsing context to the tree.
|
|
context->Attach();
|
|
|
|
return context.forget();
|
|
}
|
|
|
|
/* static */ already_AddRefed<BrowsingContext> BrowsingContext::CreateFromIPC(
|
|
BrowsingContext* aParent, BrowsingContext* aOpener, const nsAString& aName,
|
|
uint64_t aId, ContentParent* aOriginProcess) {
|
|
MOZ_DIAGNOSTIC_ASSERT(aOriginProcess || XRE_IsContentProcess(),
|
|
"Parent Process IPC contexts need a Content Process.");
|
|
MOZ_DIAGNOSTIC_ASSERT(!aParent || aParent->IsContent());
|
|
|
|
MOZ_LOG(GetLog(), LogLevel::Debug,
|
|
("Creating 0x%08" PRIx64 " from IPC (origin=0x%08" PRIx64 ")", aId,
|
|
aOriginProcess ? uint64_t(aOriginProcess->ChildID()) : 0));
|
|
|
|
RefPtr<BrowsingContext> context;
|
|
if (XRE_IsParentProcess()) {
|
|
context = new ChromeBrowsingContext(
|
|
aParent, aOpener, aName, aId, aOriginProcess->ChildID(), Type::Content);
|
|
} else {
|
|
context = new BrowsingContext(aParent, aOpener, aName, aId, Type::Content);
|
|
}
|
|
|
|
Register(context);
|
|
|
|
context->Attach();
|
|
|
|
return context.forget();
|
|
}
|
|
|
|
BrowsingContext::BrowsingContext(BrowsingContext* aParent,
|
|
BrowsingContext* aOpener,
|
|
const nsAString& aName,
|
|
uint64_t aBrowsingContextId, Type aType)
|
|
: mType(aType),
|
|
mBrowsingContextId(aBrowsingContextId),
|
|
mParent(aParent),
|
|
mOpener(aOpener),
|
|
mName(aName) {
|
|
if (mParent) {
|
|
mBrowsingContextGroup = mParent->mBrowsingContextGroup;
|
|
} else if (mOpener) {
|
|
mBrowsingContextGroup = mOpener->mBrowsingContextGroup;
|
|
} else {
|
|
mBrowsingContextGroup = new BrowsingContextGroup();
|
|
}
|
|
|
|
if (!mParent) {
|
|
// If we don't have a parent we're either a top level or auxiliary
|
|
// BrowsingContext.
|
|
mBrowsingContextGroup->AppendElement(this);
|
|
}
|
|
}
|
|
|
|
void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) {
|
|
// XXX(nika): We should communicate that we are now an active BrowsingContext
|
|
// process to the parent & do other validation here.
|
|
MOZ_RELEASE_ASSERT(nsDocShell::Cast(aDocShell)->GetBrowsingContext() == this);
|
|
mDocShell = aDocShell;
|
|
}
|
|
|
|
void BrowsingContext::Attach() {
|
|
MOZ_LOG(GetLog(), LogLevel::Debug,
|
|
("%s: %s 0x%08" PRIx64 " to 0x%08" PRIx64,
|
|
XRE_IsParentProcess() ? "Parent" : "Child",
|
|
sCachedBrowsingContexts->has(Id()) ? "Re-connecting" : "Connecting",
|
|
Id(), mParent ? mParent->Id() : 0));
|
|
|
|
sCachedBrowsingContexts->remove(Id());
|
|
|
|
auto* children = mParent ? &mParent->mChildren : sRootBrowsingContexts.get();
|
|
MOZ_DIAGNOSTIC_ASSERT(!children->Contains(this));
|
|
|
|
children->AppendElement(this);
|
|
|
|
Sync(this);
|
|
}
|
|
|
|
void BrowsingContext::Detach() {
|
|
MOZ_LOG(GetLog(), LogLevel::Debug,
|
|
("%s: Detaching 0x%08" PRIx64 " from 0x%08" PRIx64,
|
|
XRE_IsParentProcess() ? "Parent" : "Child", Id(),
|
|
mParent ? mParent->Id() : 0));
|
|
|
|
RefPtr<BrowsingContext> kungFuDeathGrip(this);
|
|
|
|
BrowsingContextMap<RefPtr>::Ptr p;
|
|
if (sCachedBrowsingContexts && (p = sCachedBrowsingContexts->lookup(Id()))) {
|
|
MOZ_DIAGNOSTIC_ASSERT(!mParent || !mParent->mChildren.Contains(this));
|
|
MOZ_DIAGNOSTIC_ASSERT(!sRootBrowsingContexts->Contains(this));
|
|
sCachedBrowsingContexts->remove(p);
|
|
} else {
|
|
auto* children =
|
|
mParent ? &mParent->mChildren : sRootBrowsingContexts.get();
|
|
// TODO(farre): This assert looks extremely fishy, I know, but
|
|
// what we're actually saying is this: if we're detaching, but our
|
|
// parent doesn't have any children, it is because we're being
|
|
// detached by the cycle collector destroying docshells out of
|
|
// order.
|
|
MOZ_DIAGNOSTIC_ASSERT(children->IsEmpty() || children->Contains(this));
|
|
|
|
children->RemoveElement(this);
|
|
}
|
|
|
|
if (!XRE_IsContentProcess()) {
|
|
return;
|
|
}
|
|
|
|
auto cc = ContentChild::GetSingleton();
|
|
MOZ_DIAGNOSTIC_ASSERT(cc);
|
|
cc->SendDetachBrowsingContext(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()));
|
|
|
|
MOZ_ALWAYS_TRUE(sCachedBrowsingContexts->reserve(mChildren.Length()));
|
|
|
|
for (BrowsingContext* child : mChildren) {
|
|
MOZ_ALWAYS_TRUE(sCachedBrowsingContexts->putNew(child->Id(), child));
|
|
}
|
|
mChildren.Clear();
|
|
|
|
if (!XRE_IsContentProcess()) {
|
|
return;
|
|
}
|
|
|
|
auto cc = ContentChild::GetSingleton();
|
|
MOZ_DIAGNOSTIC_ASSERT(cc);
|
|
cc->SendDetachBrowsingContext(BrowsingContextId(Id()),
|
|
true /* aMoveToBFCache */);
|
|
}
|
|
|
|
bool BrowsingContext::IsCached() { return sCachedBrowsingContexts->has(Id()); }
|
|
|
|
void BrowsingContext::GetChildren(
|
|
nsTArray<RefPtr<BrowsingContext>>& aChildren) {
|
|
MOZ_ALWAYS_TRUE(aChildren.AppendElements(mChildren));
|
|
}
|
|
|
|
void BrowsingContext::SetOpener(BrowsingContext* aOpener) {
|
|
if (mOpener == aOpener) {
|
|
return;
|
|
}
|
|
|
|
mOpener = aOpener;
|
|
|
|
if (!XRE_IsContentProcess()) {
|
|
return;
|
|
}
|
|
|
|
auto cc = ContentChild::GetSingleton();
|
|
MOZ_DIAGNOSTIC_ASSERT(cc);
|
|
cc->SendSetOpenerBrowsingContext(
|
|
BrowsingContextId(Id()), BrowsingContextId(aOpener ? aOpener->Id() : 0));
|
|
}
|
|
|
|
/* static */ void BrowsingContext::GetRootBrowsingContexts(
|
|
nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts) {
|
|
MOZ_ALWAYS_TRUE(aBrowsingContexts.AppendElements(*sRootBrowsingContexts));
|
|
}
|
|
|
|
BrowsingContext::~BrowsingContext() {
|
|
MOZ_DIAGNOSTIC_ASSERT(!mParent || !mParent->mChildren.Contains(this));
|
|
MOZ_DIAGNOSTIC_ASSERT(!sRootBrowsingContexts ||
|
|
!sRootBrowsingContexts->Contains(this));
|
|
MOZ_DIAGNOSTIC_ASSERT(!sCachedBrowsingContexts ||
|
|
!sCachedBrowsingContexts->has(Id()));
|
|
|
|
if (sBrowsingContexts) {
|
|
sBrowsingContexts->remove(Id());
|
|
}
|
|
}
|
|
|
|
nsISupports* BrowsingContext::GetParentObject() const {
|
|
return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
|
|
}
|
|
|
|
JSObject* BrowsingContext::WrapObject(JSContext* aCx,
|
|
JS::Handle<JSObject*> aGivenProto) {
|
|
return BrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(BrowsingContext)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell, mChildren, mParent)
|
|
if (XRE_IsParentProcess()) {
|
|
ChromeBrowsingContext::Cast(tmp)->Unlink();
|
|
}
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowsingContext)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell, mChildren, mParent)
|
|
if (XRE_IsParentProcess()) {
|
|
ChromeBrowsingContext::Cast(tmp)->Traverse(cb);
|
|
}
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(BrowsingContext)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(BrowsingContext, AddRef)
|
|
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(BrowsingContext, Release)
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|