Bug 1580565 - Part 4: Use WindowContext to manage BrowsingContext cached status, r=farre

The existing infrastructure which stored cached BrowsingContexts on the
BrowsingContextGroup was added before WindowContexts were added, and can cause
racing issues with partially discarded trees during process switches.

Differential Revision: https://phabricator.services.mozilla.com/D71238
This commit is contained in:
Nika Layzell 2020-04-22 01:37:55 +00:00
Родитель 20e0c066e7
Коммит 88b3fbe306
29 изменённых файлов: 354 добавлений и 565 удалений

Просмотреть файл

@ -159,9 +159,11 @@ bool BrowsingContext::SameOriginWithTop() {
/* static */
already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached(
BrowsingContext* aParent, BrowsingContext* aOpener, const nsAString& aName,
Type aType) {
MOZ_DIAGNOSTIC_ASSERT(!aParent || aParent->mType == aType);
nsGlobalWindowInner* aParent, BrowsingContext* aOpener,
const nsAString& aName, Type aType) {
MOZ_DIAGNOSTIC_ASSERT(!aParent ||
aParent->GetBrowsingContext()->mType == aType);
MOZ_DIAGNOSTIC_ASSERT(!aParent || aParent->GetWindowContext());
MOZ_DIAGNOSTIC_ASSERT(aType != Type::Chrome || XRE_IsParentProcess());
@ -171,31 +173,47 @@ already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached(
("Creating 0x%08" PRIx64 " in %s", id,
XRE_IsParentProcess() ? "Parent" : "Child"));
RefPtr<BrowsingContext> parentBC =
aParent ? aParent->GetBrowsingContext() : nullptr;
RefPtr<WindowContext> parentWC =
aParent ? aParent->GetWindowContext() : nullptr;
// Determine which BrowsingContextGroup this context should be created in.
RefPtr<BrowsingContextGroup> group =
(aType == Type::Chrome)
? do_AddRef(BrowsingContextGroup::GetChromeGroup())
: BrowsingContextGroup::Select(aParent, aOpener);
: BrowsingContextGroup::Select(parentWC, aOpener);
RefPtr<BrowsingContext> context;
if (XRE_IsParentProcess()) {
context =
new CanonicalBrowsingContext(aParent, group, id,
new CanonicalBrowsingContext(parentWC, group, id,
/* aOwnerProcessId */ 0,
/* aEmbedderProcessId */ 0, aType, {});
} else {
context = new BrowsingContext(aParent, group, id, aType, {});
context = new BrowsingContext(parentWC, group, id, aType, {});
}
// The name and opener fields need to be explicitly initialized. Don't bother
// using transactions to set them, as we haven't been attached yet.
context->mFields.SetWithoutSyncing<IDX_Name>(aName);
if (aOpener) {
MOZ_DIAGNOSTIC_ASSERT(!aParent,
"new BC with both initial opener and parent");
MOZ_DIAGNOSTIC_ASSERT(aOpener->Group() == context->Group());
MOZ_DIAGNOSTIC_ASSERT(aOpener->mType == context->mType);
context->mFields.SetWithoutSyncing<IDX_OpenerId>(aOpener->Id());
context->mFields.SetWithoutSyncing<IDX_HadOriginalOpener>(true);
}
if (aParent) {
MOZ_DIAGNOSTIC_ASSERT(parentBC->Group() == context->Group());
MOZ_DIAGNOSTIC_ASSERT(parentBC->mType == context->mType);
context->mEmbeddedByThisProcess = true;
context->mFields.SetWithoutSyncing<IDX_EmbedderInnerWindowId>(
aParent->WindowID());
}
context->mFields.SetWithoutSyncing<IDX_EmbedderPolicy>(
nsILoadInfo::EMBEDDER_POLICY_NULL);
context->mFields.SetWithoutSyncing<IDX_OpenerPolicy>(
@ -215,7 +233,7 @@ already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached(
nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS);
}
BrowsingContext* inherit = aParent ? aParent : aOpener;
BrowsingContext* inherit = parentBC ? parentBC.get() : aOpener;
if (inherit) {
context->mPrivateBrowsingId = inherit->mPrivateBrowsingId;
context->mUseRemoteTabs = inherit->mUseRemoteTabs;
@ -225,22 +243,17 @@ already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached(
// CORPP 3.1.3 https://mikewest.github.io/corpp/#integration-html
context->mFields.SetWithoutSyncing<IDX_EmbedderPolicy>(
inherit->GetEmbedderPolicy());
// if our parent has a parent that's loading, we need it too
bool ancestorLoading = aParent ? aParent->GetAncestorLoading() : false;
if (!ancestorLoading && aParent) {
// XXX(farre): Can/Should we check aParent->IsLoading() here? (Bug
// 1608448) Check if the parent was itself loading already
nsPIDOMWindowOuter* outer = aParent->GetDOMWindow();
if (outer) {
Document* document = nsGlobalWindowOuter::Cast(outer)->GetDocument();
auto readystate = document->GetReadyStateEnum();
if (readystate == Document::ReadyState::READYSTATE_LOADING ||
readystate == Document::ReadyState::READYSTATE_INTERACTIVE) {
ancestorLoading = true;
}
}
}
context->mFields.SetWithoutSyncing<IDX_AncestorLoading>(ancestorLoading);
}
// if our parent has a parent that's loading, we need it too
if (aParent) {
// XXX(farre): Can/Should we check aParent->IsLoading() here? (Bug
// 1608448) Check if the parent was itself loading already
auto readystate = aParent->GetDocument()->GetReadyStateEnum();
context->mFields.SetWithoutSyncing<IDX_AncestorLoading>(
parentBC->GetAncestorLoading() ||
readystate == Document::ReadyState::READYSTATE_LOADING ||
readystate == Document::ReadyState::READYSTATE_INTERACTIVE);
}
nsContentUtils::GenerateUUIDInPlace(
@ -301,7 +314,7 @@ already_AddRefed<BrowsingContext> BrowsingContext::CreateFromIPC(
("Creating 0x%08" PRIx64 " from IPC (origin=0x%08" PRIx64 ")",
aInit.mId, originId));
RefPtr<BrowsingContext> parent = aInit.GetParent();
RefPtr<WindowContext> parent = aInit.GetParent();
RefPtr<BrowsingContext> context;
if (XRE_IsParentProcess()) {
@ -330,14 +343,10 @@ already_AddRefed<BrowsingContext> BrowsingContext::CreateFromIPC(
// Caller handles attaching us to the tree.
if (aInit.mCached) {
context->mEverAttached = true;
}
return context.forget();
}
BrowsingContext::BrowsingContext(BrowsingContext* aParent,
BrowsingContext::BrowsingContext(WindowContext* aParentWindow,
BrowsingContextGroup* aGroup,
uint64_t aBrowsingContextId, Type aType,
FieldTuple&& aFields)
@ -345,7 +354,7 @@ BrowsingContext::BrowsingContext(BrowsingContext* aParent,
mType(aType),
mBrowsingContextId(aBrowsingContextId),
mGroup(aGroup),
mParent(aParent),
mParentWindow(aParentWindow),
mPrivateBrowsingId(0),
mEverAttached(false),
mIsInProcess(false),
@ -356,7 +365,10 @@ BrowsingContext::BrowsingContext(BrowsingContext* aParent,
mEmbeddedByThisProcess(false),
mUseRemoteTabs(false),
mUseRemoteSubframes(false) {
MOZ_RELEASE_ASSERT(!mParent || mParent->Group() == mGroup);
if (mParentWindow) {
MOZ_RELEASE_ASSERT(mParentWindow->Group() == mGroup);
mParent = mParentWindow->GetBrowsingContext();
}
MOZ_RELEASE_ASSERT(mBrowsingContextId != 0);
MOZ_RELEASE_ASSERT(mGroup);
}
@ -463,19 +475,14 @@ void BrowsingContext::Attach(bool aFromIPC) {
}
MOZ_DIAGNOSTIC_ASSERT(mGroup);
MOZ_DIAGNOSTIC_ASSERT(!mGroup->IsContextCached(this));
MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
Children* children = nullptr;
if (mParent) {
children = &mParent->mChildren;
BrowsingContext_Binding::ClearCachedChildrenValue(mParent);
// Add ourselves either to our parent or BrowsingContextGroup's child list.
if (mParentWindow) {
mParentWindow->AppendChildBrowsingContext(this);
} else {
children = &mGroup->Toplevels();
mGroup->Toplevels().AppendElement(this);
}
MOZ_DIAGNOSTIC_ASSERT(!children->Contains(this));
children->AppendElement(this);
if (GetIsPopupSpam()) {
PopupBlocker::RegisterOpenPopupSpam();
@ -503,27 +510,16 @@ void BrowsingContext::Detach(bool aFromIPC) {
XRE_IsParentProcess() ? "Parent" : "Child", Id(),
mParent ? mParent->Id() : 0));
// Unlinking might remove our group before Detach gets called.
// This will only ever be null if the cycle-collector has unlinked us. Don't
// try to detach ourselves in that case.
if (NS_WARN_IF(!mGroup)) {
return;
}
if (!mGroup->EvictCachedContext(this)) {
Children* children = nullptr;
if (mParent) {
children = &mParent->mChildren;
BrowsingContext_Binding::ClearCachedChildrenValue(mParent);
} else {
children = &mGroup->Toplevels();
}
children->RemoveElement(this);
}
if (!mChildren.IsEmpty()) {
mGroup->CacheContexts(mChildren);
mChildren.Clear();
BrowsingContext_Binding::ClearCachedChildrenValue(this);
if (mParentWindow) {
mParentWindow->RemoveChildBrowsingContext(this);
} else {
mGroup->Toplevels().RemoveElement(this);
}
{
@ -611,46 +607,10 @@ void BrowsingContext::PrepareForProcessChange() {
MOZ_ASSERT(!mWindowProxy);
}
void BrowsingContext::CacheChildren(bool aFromIPC) {
MOZ_LOG(GetLog(), LogLevel::Debug,
("%s: Caching children of 0x%08" PRIx64 "",
XRE_IsParentProcess() ? "Parent" : "Child", Id()));
mGroup->CacheContexts(mChildren);
mChildren.Clear();
BrowsingContext_Binding::ClearCachedChildrenValue(this);
if (!aFromIPC && XRE_IsContentProcess()) {
auto cc = ContentChild::GetSingleton();
MOZ_DIAGNOSTIC_ASSERT(cc);
cc->SendCacheBrowsingContextChildren(this);
}
bool BrowsingContext::IsCached() {
return mParentWindow && mParentWindow->IsCached();
}
void BrowsingContext::RestoreChildren(Children&& aChildren, bool aFromIPC) {
MOZ_LOG(GetLog(), LogLevel::Debug,
("%s: Restoring children of 0x%08" PRIx64 "",
XRE_IsParentProcess() ? "Parent" : "Child", Id()));
nsTArray<MaybeDiscarded<BrowsingContext>> ipcChildren(aChildren.Length());
for (BrowsingContext* child : aChildren) {
MOZ_DIAGNOSTIC_ASSERT(child->GetParent() == this);
Unused << mGroup->EvictCachedContext(child);
ipcChildren.AppendElement(child);
}
mChildren.AppendElements(aChildren);
BrowsingContext_Binding::ClearCachedChildrenValue(this);
if (!aFromIPC && XRE_IsContentProcess()) {
auto cc = ContentChild::GetSingleton();
MOZ_DIAGNOSTIC_ASSERT(cc);
cc->SendRestoreBrowsingContextChildren(this, ipcChildren);
}
}
bool BrowsingContext::IsCached() { return mGroup->IsContextCached(this); }
bool BrowsingContext::IsTargetable() {
return !GetClosed() && !mIsDiscarded && !IsCached();
}
@ -659,8 +619,16 @@ bool BrowsingContext::HasOpener() const {
return sBrowsingContexts->Contains(GetOpenerId());
}
void BrowsingContext::GetChildren(Children& aChildren) {
MOZ_ALWAYS_TRUE(aChildren.AppendElements(mChildren));
Span<RefPtr<BrowsingContext>> BrowsingContext::Children() const {
if (WindowContext* current = mCurrentWindowContext) {
return current->Children();
}
return Span<RefPtr<BrowsingContext>>();
}
void BrowsingContext::GetChildren(
nsTArray<RefPtr<BrowsingContext>>& aChildren) {
MOZ_ALWAYS_TRUE(aChildren.AppendElements(Children()));
}
void BrowsingContext::GetWindowContexts(
@ -671,10 +639,15 @@ void BrowsingContext::GetWindowContexts(
void BrowsingContext::RegisterWindowContext(WindowContext* aWindow) {
MOZ_ASSERT(!mWindowContexts.Contains(aWindow),
"WindowContext already registered!");
MOZ_ASSERT(aWindow->GetBrowsingContext() == this);
mWindowContexts.AppendElement(aWindow);
// If the newly registered WindowContext is for our current inner window ID,
// re-run the `DidSet` handler to re-establish the relationship.
if (aWindow->InnerWindowId() == GetCurrentInnerWindowId()) {
MOZ_ASSERT(aWindow->GetBrowsingContext() == this);
mCurrentWindowContext = aWindow;
DidSet(FieldIndex<IDX_CurrentInnerWindowId>());
MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowContext == aWindow);
}
}
@ -683,15 +656,9 @@ void BrowsingContext::UnregisterWindowContext(WindowContext* aWindow) {
"WindowContext not registered!");
mWindowContexts.RemoveElement(aWindow);
// Our current window global should be in our mWindowGlobals set. If it's not
// anymore, clear that reference.
// FIXME: There are probably situations where this is wrong. We should
// double-check.
// If our currently active window was unregistered, clear our reference to it.
if (aWindow == mCurrentWindowContext) {
mCurrentWindowContext = nullptr;
if (XRE_IsParentProcess()) {
BrowserParent::UpdateFocusFromBrowsingContext();
}
SetCurrentInnerWindowId(0);
}
}
@ -739,23 +706,23 @@ BrowsingContext* BrowsingContext::FindWithName(
BrowsingContext* current = this;
do {
Children* siblings;
Span<RefPtr<BrowsingContext>> siblings;
BrowsingContext* parent = current->mParent;
if (!parent) {
// We've reached the root of the tree, consider browsing
// contexts in the same browsing context group.
siblings = &mGroup->Toplevels();
siblings = mGroup->Toplevels();
} else if (parent->NameEquals(aName) &&
requestingContext->CanAccess(parent) &&
parent->IsTargetable()) {
found = parent;
break;
} else {
siblings = &parent->mChildren;
siblings = parent->Children();
}
for (BrowsingContext* sibling : *siblings) {
for (BrowsingContext* sibling : siblings) {
if (sibling == current) {
continue;
}
@ -787,7 +754,7 @@ BrowsingContext* BrowsingContext::FindChildWithName(
return nullptr;
}
for (BrowsingContext* child : mChildren) {
for (BrowsingContext* child : Children()) {
if (child->NameEquals(aName) && aRequestingContext.CanAccess(child) &&
child->IsTargetable()) {
return child;
@ -831,7 +798,7 @@ BrowsingContext* BrowsingContext::FindWithNameInSubtree(
return this;
}
for (BrowsingContext* child : mChildren) {
for (BrowsingContext* child : Children()) {
if (BrowsingContext* found =
child->FindWithNameInSubtree(aName, aRequestingContext)) {
return found;
@ -947,9 +914,9 @@ RefPtr<SessionStorageManager> BrowsingContext::GetSessionStorageManager() {
}
BrowsingContext::~BrowsingContext() {
MOZ_DIAGNOSTIC_ASSERT(!mParent || !mParent->mChildren.Contains(this));
MOZ_DIAGNOSTIC_ASSERT(!mParentWindow ||
!mParentWindow->mChildren.Contains(this));
MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->IsContextCached(this));
mDeprioritizedLoadRunner.clear();
@ -1316,7 +1283,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext)
tmp->mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell, mChildren, mParent, mGroup,
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell, mParentWindow, mParent, mGroup,
mEmbedderElement, mWindowContexts,
mCurrentWindowContext, mSessionStorageManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@ -1324,8 +1291,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowsingContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
mDocShell, mChildren, mParent, mGroup, mEmbedderElement, mWindowContexts,
mCurrentWindowContext, mSessionStorageManager)
mDocShell, mParentWindow, mParent, mGroup, mEmbedderElement,
mWindowContexts, mCurrentWindowContext, mSessionStorageManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
class RemoteLocationProxy
@ -1678,8 +1645,7 @@ BrowsingContext::IPCInitializer BrowsingContext::GetIPCInitializer() {
IPCInitializer init;
init.mId = Id();
init.mParentId = mParent ? mParent->Id() : 0;
init.mCached = IsCached();
init.mParentId = mParentWindow ? mParentWindow->Id() : 0;
init.mWindowless = mWindowless;
init.mUseRemoteTabs = mUseRemoteTabs;
init.mUseRemoteSubframes = mUseRemoteSubframes;
@ -1688,10 +1654,10 @@ BrowsingContext::IPCInitializer BrowsingContext::GetIPCInitializer() {
return init;
}
already_AddRefed<BrowsingContext> BrowsingContext::IPCInitializer::GetParent() {
RefPtr<BrowsingContext> parent;
already_AddRefed<WindowContext> BrowsingContext::IPCInitializer::GetParent() {
RefPtr<WindowContext> parent;
if (mParentId != 0) {
parent = BrowsingContext::Get(mParentId);
parent = WindowContext::GetById(mParentId);
MOZ_RELEASE_ASSERT(parent);
}
return parent.forget();
@ -1847,56 +1813,14 @@ bool BrowsingContext::CheckOnlyEmbedderCanSet(ContentParent* aSource) {
bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderInnerWindowId>,
const uint64_t& aValue, ContentParent* aSource) {
// Generally allow clearing this. We may want to be more precise about this
// check in the future.
if (aValue == 0) {
return true;
// If we have a parent window, our embedder inner window ID must match it.
if (mParentWindow) {
return mParentWindow->Id() == aValue;
}
// If we don't have a specified source, we're the setting process. The window
// which we're setting this to must be in-process.
RefPtr<BrowsingContext> impliedParent;
if (!aSource) {
nsGlobalWindowInner* innerWindow =
nsGlobalWindowInner::GetInnerWindowWithId(aValue);
if (NS_WARN_IF(!innerWindow)) {
return false;
}
impliedParent = innerWindow->GetBrowsingContext();
}
// If in the parent process, double-check ownership and WindowGlobalParent as
// well.
if (XRE_IsParentProcess()) {
RefPtr<WindowGlobalParent> wgp =
WindowGlobalParent::GetByInnerWindowId(aValue);
if (NS_WARN_IF(!wgp)) {
return false;
}
// Deduce the implied parent from the WindowGlobalParent actor.
if (impliedParent) {
MOZ_ASSERT(impliedParent == wgp->BrowsingContext());
}
impliedParent = wgp->BrowsingContext();
// Double-check ownership if we aren't the setter.
if (aSource &&
!impliedParent->Canonical()->IsOwnedByProcess(aSource->ChildID()) &&
aSource->ChildID() !=
impliedParent->Canonical()->GetInFlightProcessId()) {
return false;
}
}
// If we would have an invalid implied parent, something has gone wrong.
MOZ_ASSERT(impliedParent);
if (NS_WARN_IF(mParent && mParent != impliedParent)) {
return false;
}
return true;
// For toplevel BrowsingContext instances, this value may only be set by the
// parent process, or initialized to `0`.
return CheckOnlyEmbedderCanSet(aSource);
}
bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderElementType>,
@ -1937,6 +1861,14 @@ bool BrowsingContext::CanSet(FieldIndex<IDX_CurrentInnerWindowId>,
void BrowsingContext::DidSet(FieldIndex<IDX_CurrentInnerWindowId>) {
mCurrentWindowContext = WindowContext::GetById(GetCurrentInnerWindowId());
MOZ_ASSERT(
!mCurrentWindowContext || mWindowContexts.Contains(mCurrentWindowContext),
"WindowContext not registered?");
// Clear our cached `children` value, to ensure that JS sees the up-to-date
// value.
BrowsingContext_Binding::ClearCachedChildrenValue(this);
if (XRE_IsParentProcess()) {
BrowserParent::UpdateFocusFromBrowsingContext();
}
@ -2062,7 +1994,6 @@ void IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Write(
// Write actor ID parameters.
WriteIPDLParam(aMessage, aActor, aInit.mId);
WriteIPDLParam(aMessage, aActor, aInit.mParentId);
WriteIPDLParam(aMessage, aActor, aInit.mCached);
WriteIPDLParam(aMessage, aActor, aInit.mWindowless);
WriteIPDLParam(aMessage, aActor, aInit.mUseRemoteTabs);
WriteIPDLParam(aMessage, aActor, aInit.mUseRemoteSubframes);
@ -2076,7 +2007,6 @@ bool IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Read(
// Read actor ID parameters.
if (!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mId) ||
!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mParentId) ||
!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mCached) ||
!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mWindowless) ||
!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mUseRemoteTabs) ||
!ReadIPDLParam(aMessage, aIterator, aActor,

Просмотреть файл

@ -11,6 +11,7 @@
#include "mozilla/LinkedList.h"
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Span.h"
#include "mozilla/Tuple.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/dom/BindingDeclarations.h"
@ -32,6 +33,7 @@
#include "nsILoadContext.h"
class nsDocShellLoadState;
class nsGlobalWindowInner;
class nsGlobalWindowOuter;
class nsILoadInfo;
class nsIPrincipal;
@ -147,8 +149,6 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
public:
enum class Type { Chrome, Content };
using Children = nsTArray<RefPtr<BrowsingContext>>;
static void Init();
static LogModule* GetLog();
static void CleanupContexts(uint64_t aProcessId);
@ -184,7 +184,7 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
// `EnsureAttached()` must be called before the BrowsingContext is used for a
// DocShell, BrowserParent, or BrowserBridgeChild.
static already_AddRefed<BrowsingContext> CreateDetached(
BrowsingContext* aParent, BrowsingContext* aOpener,
nsGlobalWindowInner* aParent, BrowsingContext* aOpener,
const nsAString& aName, Type aType);
void EnsureAttached();
@ -251,13 +251,6 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
// Prepare this BrowsingContext to leave the current process.
void PrepareForProcessChange();
// Remove all children from the current BrowsingContext and cache
// them to allow them to be attached again.
void CacheChildren(bool aFromIPC = false);
// Restore cached browsing contexts.
void RestoreChildren(Children&& aChildren, bool aFromIPC = false);
// Triggers a load in the process which currently owns this BrowsingContext.
nsresult LoadURI(nsDocShellLoadState* aLoadState, bool aSetNavigating = false);
@ -271,8 +264,7 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
void DisplayLoadError(const nsAString& aURI);
// Determine if the current BrowsingContext was 'cached' by the logic in
// CacheChildren.
// Determine if the current BrowsingContext is in the BFCache.
bool IsCached();
// Check that this browsing context is targetable for navigations (i.e. that
@ -300,6 +292,10 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
}
BrowsingContext* Top();
// NOTE: Unlike `GetEmbedderWindowGlobal`, `GetParentWindow` does not cross
// toplevel content browser boundaries.
WindowContext* GetParentWindow() const { return mParentWindow; }
already_AddRefed<BrowsingContext> GetOpener() const {
RefPtr<BrowsingContext> opener(Get(GetOpenerId()));
if (!mIsDiscarded && opener && !opener->mIsDiscarded) {
@ -340,8 +336,12 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
uint32_t SandboxFlags() { return GetSandboxFlags(); }
void GetChildren(Children& aChildren);
Span<RefPtr<BrowsingContext>> Children() const;
void GetChildren(nsTArray<RefPtr<BrowsingContext>>& aChildren);
const nsTArray<RefPtr<WindowContext>>& GetWindowContexts() {
return mWindowContexts;
}
void GetWindowContexts(nsTArray<RefPtr<WindowContext>>& aWindows);
void RegisterWindowContext(WindowContext* aWindow);
@ -449,22 +449,17 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BrowsingContext)
NS_DECL_NSILOADCONTEXT
const Children& GetChildren() { return mChildren; }
const nsTArray<RefPtr<WindowContext>>& GetWindowContexts() {
return mWindowContexts;
}
// Perform a pre-order walk of this BrowsingContext subtree.
void PreOrderWalk(const std::function<void(BrowsingContext*)>& aCallback) {
aCallback(this);
for (auto& child : GetChildren()) {
for (auto& child : Children()) {
child->PreOrderWalk(aCallback);
}
}
// Perform an post-order walk of this BrowsingContext subtree.
void PostOrderWalk(const std::function<void(BrowsingContext*)>& aCallback) {
for (auto& child : GetChildren()) {
for (auto& child : Children()) {
child->PostOrderWalk(aCallback);
}
@ -481,7 +476,7 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
void Focus(CallerType aCallerType, ErrorResult& aError);
void Blur(ErrorResult& aError);
WindowProxyHolder GetFrames(ErrorResult& aError);
int32_t Length() const { return mChildren.Length(); }
int32_t Length() const { return Children().Length(); }
Nullable<WindowProxyHolder> GetTop(ErrorResult& aError);
void GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aOpener,
ErrorResult& aError) const;
@ -522,18 +517,26 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
// deserialized before other BrowsingContext in the BrowsingContextGroup
// have been initialized.
uint64_t mParentId = 0;
already_AddRefed<BrowsingContext> GetParent();
already_AddRefed<WindowContext> GetParent();
already_AddRefed<BrowsingContext> GetOpener();
uint64_t GetOpenerId() const { return mozilla::Get<IDX_OpenerId>(mFields); }
bool mCached = false;
bool mWindowless = false;
bool mUseRemoteTabs = false;
bool mUseRemoteSubframes = false;
OriginAttributes mOriginAttributes;
FieldTuple mFields;
bool operator==(const IPCInitializer& aOther) const {
return mId == aOther.mId && mParentId == aOther.mParentId &&
mWindowless == aOther.mWindowless &&
mUseRemoteTabs == aOther.mUseRemoteTabs &&
mUseRemoteSubframes == aOther.mUseRemoteSubframes &&
mOriginAttributes == aOther.mOriginAttributes &&
mFields == aOther.mFields;
}
};
// Create an IPCInitializer object for this BrowsingContext.
@ -564,7 +567,7 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
protected:
virtual ~BrowsingContext();
BrowsingContext(BrowsingContext* aParent, BrowsingContextGroup* aGroup,
BrowsingContext(WindowContext* aParentWindow, BrowsingContextGroup* aGroup,
uint64_t aBrowsingContextId, Type aType,
FieldTuple&& aFields);
@ -712,10 +715,11 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
const uint64_t mBrowsingContextId;
RefPtr<BrowsingContextGroup> mGroup;
RefPtr<WindowContext> mParentWindow;
// NOTE: `mParent` must be the same as `mParentWindow->GetBrowsingContext()`
// at all times.
// FIXME: Consider removing this field?
RefPtr<BrowsingContext> mParent;
// Note: BrowsingContext_Binding::ClearCachedChildrenValue must be called any
// time this array is mutated to keep the JS-exposed reflection in sync.
Children mChildren;
nsCOMPtr<nsIDocShell> mDocShell;
RefPtr<Element> mEmbedderElement;
@ -824,7 +828,6 @@ extern bool GetRemoteOuterWindowProxy(JSContext* aCx, BrowsingContext* aContext,
using BrowsingContextTransaction = BrowsingContext::BaseTransaction;
using BrowsingContextInitializer = BrowsingContext::IPCInitializer;
using BrowsingContextChildren = BrowsingContext::Children;
using MaybeDiscardedBrowsingContext = MaybeDiscarded<BrowsingContext>;
// Specialize the transaction object for every translation unit it's used in.

Просмотреть файл

@ -69,6 +69,35 @@ void BrowsingContextGroup::Unsubscribe(ContentParent* aOriginProcess) {
MOZ_DIAGNOSTIC_ASSERT(aOriginProcess);
mSubscribers.RemoveEntry(aOriginProcess);
aOriginProcess->OnBrowsingContextGroupUnsubscribe(this);
// If this origin process still embeds any non-discarded BrowsingContexts in
// this BrowsingContextGroup, make sure to discard them, as this process is
// going away.
nsTArray<RefPtr<BrowsingContext>> toDiscard;
for (auto& context : mContexts) {
if (context.GetKey()->Canonical()->IsEmbeddedInProcess(
aOriginProcess->ChildID())) {
toDiscard.AppendElement(context.GetKey());
}
}
for (auto& context : toDiscard) {
context->Detach(/* aFromIPC */ true);
}
}
static void CollectContextInitializers(
Span<RefPtr<BrowsingContext>> aContexts,
nsTArray<SyncedContextInitializer>& aInits) {
// The order that we record these initializers is important, as it will keep
// the order that children are attached to their parent in the newly connected
// content process consistent.
for (auto& context : aContexts) {
aInits.AppendElement(context->GetIPCInitializer());
for (auto& window : context->GetWindowContexts()) {
aInits.AppendElement(window->GetIPCInitializer());
CollectContextInitializers(window->Children(), aInits);
}
}
}
void BrowsingContextGroup::EnsureSubscribed(ContentParent* aProcess) {
@ -79,79 +108,33 @@ void BrowsingContextGroup::EnsureSubscribed(ContentParent* aProcess) {
Subscribe(aProcess);
bool sendFocused = false;
bool sendActive = false;
BrowsingContext* focused = nullptr;
BrowsingContext* active = nullptr;
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (fm) {
focused = fm->GetFocusedBrowsingContextInChrome();
active = fm->GetActiveBrowsingContextInChrome();
}
nsTArray<BrowsingContext::IPCInitializer> inits(mContexts.Count());
nsTArray<WindowContext::IPCInitializer> windowInits(mContexts.Count());
auto addInits = [&](BrowsingContext* aContext) {
inits.AppendElement(aContext->GetIPCInitializer());
if (focused == aContext) {
sendFocused = true;
}
if (active == aContext) {
sendActive = true;
}
for (auto& window : aContext->GetWindowContexts()) {
windowInits.AppendElement(window->GetIPCInitializer());
}
};
// First, perform a pre-order walk of our BrowsingContext objects from our
// toplevels. This should visit every active BrowsingContext.
for (auto& context : mToplevels) {
MOZ_DIAGNOSTIC_ASSERT(!IsContextCached(context),
"cached contexts must have a parent");
context->PreOrderWalk(addInits);
}
// Ensure that cached BrowsingContext objects are also visited, by visiting
// them after mToplevels.
for (auto iter = mCachedContexts.Iter(); !iter.Done(); iter.Next()) {
iter.Get()->GetKey()->PreOrderWalk(addInits);
}
// We should have visited every browsing context.
MOZ_DIAGNOSTIC_ASSERT(inits.Length() == mContexts.Count(),
"Visited the wrong number of contexts!");
// FIXME: This won't send non-discarded children of discarded BCs, but those
// BCs will be in the process of being destroyed anyway.
// FIXME: Prevent that situation from occuring.
nsTArray<SyncedContextInitializer> inits(mContexts.Count() * 2);
CollectContextInitializers(mToplevels, inits);
// Send all of our contexts to the target content process.
Unused << aProcess->SendRegisterBrowsingContextGroup(inits, windowInits);
Unused << aProcess->SendRegisterBrowsingContextGroup(inits);
if (sendActive || sendFocused) {
Unused << aProcess->SendSetupFocusedAndActive(
sendFocused ? focused : nullptr, sendActive ? active : nullptr);
// If the focused or active BrowsingContexts belong in this group, tell the
// newly subscribed process.
if (nsFocusManager* fm = nsFocusManager::GetFocusManager()) {
BrowsingContext* focused = fm->GetFocusedBrowsingContextInChrome();
if (focused && focused->Group() != this) {
focused = nullptr;
}
BrowsingContext* active = fm->GetActiveBrowsingContextInChrome();
if (active && active->Group() != this) {
active = nullptr;
}
if (focused || active) {
Unused << aProcess->SendSetupFocusedAndActive(focused, active);
}
}
}
bool BrowsingContextGroup::IsContextCached(BrowsingContext* aContext) const {
MOZ_DIAGNOSTIC_ASSERT(aContext);
return mCachedContexts.Contains(aContext);
}
void BrowsingContextGroup::CacheContext(BrowsingContext* aContext) {
mCachedContexts.PutEntry(aContext);
}
void BrowsingContextGroup::CacheContexts(
const BrowsingContext::Children& aContexts) {
for (BrowsingContext* child : aContexts) {
mCachedContexts.PutEntry(child);
}
}
bool BrowsingContextGroup::EvictCachedContext(BrowsingContext* aContext) {
return mCachedContexts.EnsureRemoved(aContext);
}
BrowsingContextGroup::~BrowsingContextGroup() {
UnsubscribeAllContentParents();
}
@ -260,8 +243,30 @@ void BrowsingContextGroup::RemoveDocument(const nsACString& aKey,
}
}
already_AddRefed<BrowsingContextGroup> BrowsingContextGroup::Select(
WindowContext* aParent, BrowsingContext* aOpener) {
if (aParent) {
return do_AddRef(aParent->Group());
}
if (aOpener) {
return do_AddRef(aOpener->Group());
}
return MakeAndAddRef<BrowsingContextGroup>();
}
already_AddRefed<BrowsingContextGroup> BrowsingContextGroup::Select(
uint64_t aParentId, uint64_t aOpenerId) {
RefPtr<WindowContext> parent = WindowContext::GetById(aParentId);
MOZ_RELEASE_ASSERT(parent || aParentId == 0);
RefPtr<BrowsingContext> opener = BrowsingContext::Get(aOpenerId);
MOZ_RELEASE_ASSERT(opener || aOpenerId == 0);
return Select(parent, opener);
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(BrowsingContextGroup, mContexts,
mToplevels, mSubscribers, mCachedContexts,
mToplevels, mSubscribers,
mTimerEventQueue, mWorkerEventQueue)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(BrowsingContextGroup, AddRef)

Просмотреть файл

@ -21,6 +21,7 @@ class ThrottledEventQueue;
namespace dom {
class BrowsingContext;
class WindowContext;
class ContentParent;
// A BrowsingContextGroup represents the Unit of Related Browsing Contexts in
@ -47,16 +48,10 @@ class BrowsingContextGroup final : public nsWrapperCache {
// Force the given ContentParent to subscribe to our BrowsingContextGroup.
void EnsureSubscribed(ContentParent* aProcess);
// Methods interacting with cached contexts.
bool IsContextCached(BrowsingContext* aContext) const;
void CacheContext(BrowsingContext* aContext);
void CacheContexts(const BrowsingContext::Children& aContexts);
bool EvictCachedContext(BrowsingContext* aContext);
// Get a reference to the list of toplevel contexts in this
// BrowsingContextGroup.
BrowsingContext::Children& Toplevels() { return mToplevels; }
void GetToplevels(BrowsingContext::Children& aToplevels) {
nsTArray<RefPtr<BrowsingContext>>& Toplevels() { return mToplevels; }
void GetToplevels(nsTArray<RefPtr<BrowsingContext>>& aToplevels) {
aToplevels.AppendElements(mToplevels);
}
@ -67,26 +62,10 @@ class BrowsingContextGroup final : public nsWrapperCache {
BrowsingContextGroup();
static already_AddRefed<BrowsingContextGroup> Select(
BrowsingContext* aParent, BrowsingContext* aOpener) {
if (aParent) {
return do_AddRef(aParent->Group());
}
if (aOpener) {
return do_AddRef(aOpener->Group());
}
return MakeAndAddRef<BrowsingContextGroup>();
}
WindowContext* aParent, BrowsingContext* aOpener);
static already_AddRefed<BrowsingContextGroup> Select(uint64_t aParentId,
uint64_t aOpenerId) {
RefPtr<BrowsingContext> parent = BrowsingContext::Get(aParentId);
MOZ_RELEASE_ASSERT(parent || aParentId == 0);
RefPtr<BrowsingContext> opener = BrowsingContext::Get(aOpenerId);
MOZ_RELEASE_ASSERT(opener || aOpenerId == 0);
return Select(parent, opener);
}
uint64_t aOpenerId);
// For each 'ContentParent', except for 'aExcludedParent',
// associated with this group call 'aCallback'.
@ -148,7 +127,7 @@ class BrowsingContextGroup final : public nsWrapperCache {
nsTHashtable<nsRefPtrHashKey<BrowsingContext>> mContexts;
// The set of toplevel browsing contexts in the current BrowsingContextGroup.
BrowsingContext::Children mToplevels;
nsTArray<RefPtr<BrowsingContext>> mToplevels;
// DocGroups are thread-safe, and not able to be cycle collected,
// but we still keep strong pointers. When all Documents are removed
@ -158,9 +137,6 @@ class BrowsingContextGroup final : public nsWrapperCache {
ContentParents mSubscribers;
// Map of cached contexts that need to stay alive due to bfcache.
nsTHashtable<nsRefPtrHashKey<BrowsingContext>> mCachedContexts;
// A queue to store postMessage events during page load, the queue will be
// flushed once the page is loaded
RefPtr<mozilla::ThrottledEventQueue> mPostMessageEventQueue;

Просмотреть файл

@ -33,14 +33,14 @@ extern mozilla::LazyLogModule gUserInteractionPRLog;
#define USER_ACTIVATION_LOG(msg, ...) \
MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
CanonicalBrowsingContext::CanonicalBrowsingContext(BrowsingContext* aParent,
CanonicalBrowsingContext::CanonicalBrowsingContext(WindowContext* aParentWindow,
BrowsingContextGroup* aGroup,
uint64_t aBrowsingContextId,
uint64_t aOwnerProcessId,
uint64_t aEmbedderProcessId,
BrowsingContext::Type aType,
FieldTuple&& aFields)
: BrowsingContext(aParent, aGroup, aBrowsingContextId, aType,
: BrowsingContext(aParentWindow, aGroup, aBrowsingContextId, aType,
std::move(aFields)),
mProcessId(aOwnerProcessId),
mEmbedderProcessId(aEmbedderProcessId) {

Просмотреть файл

@ -134,7 +134,7 @@ class CanonicalBrowsingContext final : public BrowsingContext {
void CanonicalDiscard();
using Type = BrowsingContext::Type;
CanonicalBrowsingContext(BrowsingContext* aParent,
CanonicalBrowsingContext(WindowContext* aParentWindow,
BrowsingContextGroup* aGroup,
uint64_t aBrowsingContextId,
uint64_t aOwnerProcessId,

Просмотреть файл

@ -45,10 +45,43 @@ WindowGlobalParent* WindowContext::Canonical() {
return static_cast<WindowGlobalParent*>(this);
}
bool WindowContext::IsCached() const {
return mBrowsingContext->mCurrentWindowContext != this;
}
nsIGlobalObject* WindowContext::GetParentObject() const {
return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
}
void WindowContext::AppendChildBrowsingContext(
BrowsingContext* aBrowsingContext) {
MOZ_DIAGNOSTIC_ASSERT(Group() == aBrowsingContext->Group(),
"Mismatched groups?");
MOZ_DIAGNOSTIC_ASSERT(!mChildren.Contains(aBrowsingContext));
mChildren.AppendElement(aBrowsingContext);
// If we're the current WindowContext in our BrowsingContext, make sure to
// clear any cached `children` value.
if (!IsCached()) {
BrowsingContext_Binding::ClearCachedChildrenValue(mBrowsingContext);
}
}
void WindowContext::RemoveChildBrowsingContext(
BrowsingContext* aBrowsingContext) {
MOZ_DIAGNOSTIC_ASSERT(Group() == aBrowsingContext->Group(),
"Mismatched groups?");
mChildren.RemoveElement(aBrowsingContext);
// If we're the current WindowContext in our BrowsingContext, make sure to
// clear any cached `children` value.
if (!IsCached()) {
BrowsingContext_Binding::ClearCachedChildrenValue(mBrowsingContext);
}
}
void WindowContext::SendCommitTransaction(ContentParent* aParent,
const BaseTransaction& aTxn,
uint64_t aEpoch) {
@ -160,11 +193,13 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WindowContext)
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildren)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WindowContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildren)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(WindowContext)

Просмотреть файл

@ -7,6 +7,7 @@
#ifndef mozilla_dom_WindowContext_h
#define mozilla_dom_WindowContext_h
#include "mozilla/Span.h"
#include "mozilla/dom/MaybeDiscarded.h"
#include "mozilla/dom/SyncedContext.h"
#include "mozilla/net/NeckoChannelParams.h"
@ -14,6 +15,8 @@
namespace mozilla {
namespace dom {
class WindowGlobalParent;
#define MOZ_EACH_WC_FIELD(FIELD) \
FIELD(OuterWindowId, uint64_t) \
FIELD(CookieJarSettings, Maybe<mozilla::net::CookieJarSettingsArgs>) \
@ -36,6 +39,10 @@ class WindowContext : public nsISupports, public nsWrapperCache {
uint64_t OuterWindowId() const { return GetOuterWindowId(); }
bool IsDiscarded() const { return mIsDiscarded; }
bool IsCached() const;
Span<RefPtr<BrowsingContext>> Children() { return mChildren; }
// Cast this object to it's parent-process canonical form.
WindowGlobalParent* Canonical();
@ -50,6 +57,12 @@ class WindowContext : public nsISupports, public nsWrapperCache {
uint64_t mBrowsingContextId;
FieldTuple mFields;
bool operator==(const IPCInitializer& aOther) const {
return mInnerWindowId == aOther.mInnerWindowId &&
mBrowsingContextId == aOther.mBrowsingContextId &&
mFields == aOther.mFields;
}
};
IPCInitializer GetIPCInitializer() {
return {mInnerWindowId, mBrowsingContext->Id(), mFields.Fields()};
@ -66,6 +79,11 @@ class WindowContext : public nsISupports, public nsWrapperCache {
void Init();
private:
friend class BrowsingContext;
void AppendChildBrowsingContext(BrowsingContext* aBrowsingContext);
void RemoveChildBrowsingContext(BrowsingContext* aBrowsingContext);
// Send a given `BaseTransaction` object to the correct remote.
void SendCommitTransaction(ContentParent* aParent,
const BaseTransaction& aTxn, uint64_t aEpoch);
@ -100,6 +118,12 @@ class WindowContext : public nsISupports, public nsWrapperCache {
uint64_t mInnerWindowId;
RefPtr<BrowsingContext> mBrowsingContext;
// --- NEVER CHANGE `mChildren` DIRECTLY! ---
// Changes to this list need to be synchronized to the list within our
// `mBrowsingContext`, and should only be performed through the
// `AppendChildBrowsingContext` and `RemoveChildBrowsingContext` methods.
nsTArray<RefPtr<BrowsingContext>> mChildren;
bool mIsDiscarded = false;
};

Просмотреть файл

@ -7132,12 +7132,6 @@ nsresult nsDocShell::RestoreFromHistory() {
// Order the mContentViewer setup just like Embed does.
mContentViewer = nullptr;
if (!mWillChangeProcess) {
// Move the browsing context's children to the cache. If we're
// detaching them, we'll detach them from there.
mBrowsingContext->CacheChildren();
}
// Now that we're about to switch documents, forget all of our children.
// Note that we cached them as needed up in CaptureState above.
DestroyChildren();
@ -7256,7 +7250,6 @@ nsresult nsDocShell::RestoreFromHistory() {
// <head> is parsed.
document->NotifyPossibleTitleChange(false);
BrowsingContext::Children contexts(childShells.Count());
// Now we simulate appending child docshells for subframes.
for (i = 0; i < childShells.Count(); ++i) {
nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
@ -7293,8 +7286,6 @@ nsresult nsDocShell::RestoreFromHistory() {
// child inherits our mPrivateBrowsingId, which is what we want.
AddChild(childItem);
contexts.AppendElement(childShell->GetBrowsingContext());
childShell->SetAllowJavascript(allowJavascript);
childShell->SetAllowMetaRedirects(allowRedirects);
childShell->SetAllowSubframes(allowSubframes);
@ -7310,10 +7301,6 @@ nsresult nsDocShell::RestoreFromHistory() {
NS_ENSURE_SUCCESS(rv, rv);
}
if (!contexts.IsEmpty()) {
mBrowsingContext->RestoreChildren(std::move(contexts));
}
// Make sure to restore the window state after adding the child shells back
// to the tree. This is necessary for Thaw() and Resume() to propagate
// properly.
@ -7866,10 +7853,6 @@ nsresult nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer,
mContentViewer = nullptr;
// Move the browsing ontext's children to the cache. If we're
// detaching them, we'll detach them from there.
mBrowsingContext->CacheChildren();
// Now that we're about to switch documents, forget all of our children.
// Note that we cached them as needed up in CaptureState above.
DestroyChildren();

Просмотреть файл

@ -386,7 +386,7 @@ nsresult nsSHistory::WalkHistoryEntries(nsISHEntry* aRootEntry,
BrowsingContext* childBC = nullptr;
if (aBC) {
for (BrowsingContext* child : aBC->GetChildren()) {
for (BrowsingContext* child : aBC->Children()) {
// If the SH pref is on, or we are in the parent process, update
// canonical BC directly
if (StaticPrefs::fission_sessionHistoryInParent() ||

Просмотреть файл

@ -10459,7 +10459,7 @@ bool Document::CanSavePresentation(nsIRequest* aNewRequest,
// BFCache is currently not compatible with remote subframes (bug 1609324)
if (RefPtr<BrowsingContext> browsingContext = GetBrowsingContext()) {
for (auto& child : browsingContext->GetChildren()) {
for (auto& child : browsingContext->Children()) {
if (!child->IsInProcess()) {
aBFCacheCombo |= BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES;
ret = false;
@ -11158,7 +11158,7 @@ void Document::NotifyLoading(bool aNewParentIsLoading,
// readystates of the subdocument. In the child process it will call
// NotifyLoading() to notify the innerwindow/TimeoutManager, and then
// iterate it's children
for (auto& child : context->GetChildren()) {
for (auto& child : context->Children()) {
MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
("bc: %p SetAncestorLoading(%d)", (void*)child, is_loading));
child->SetAncestorLoading(is_loading);

Просмотреть файл

@ -95,7 +95,7 @@ bool RemoteOuterWindowProxy::getOwnPropertyDescriptor(
BrowsingContext* bc = GetBrowsingContext(aProxy);
uint32_t index = GetArrayIndexFromId(aId);
if (IsArrayIndex(index)) {
const BrowsingContext::Children& children = bc->GetChildren();
Span<RefPtr<BrowsingContext>> children = bc->Children();
if (index < children.Length()) {
return WrapResult(aCx, aProxy, children[index],
JSPROP_READONLY | JSPROP_ENUMERATE, aDesc);
@ -120,7 +120,7 @@ bool RemoteOuterWindowProxy::getOwnPropertyDescriptor(
return false;
}
for (BrowsingContext* child : bc->GetChildren()) {
for (BrowsingContext* child : bc->Children()) {
if (child->NameEquals(str)) {
return WrapResult(aCx, aProxy, child, JSPROP_READONLY, aDesc);
}
@ -132,7 +132,7 @@ bool RemoteOuterWindowProxy::getOwnPropertyDescriptor(
bool AppendIndexedPropertyNames(JSContext* aCx, BrowsingContext* aContext,
JS::MutableHandleVector<jsid> aIndexedProps) {
int32_t length = aContext->GetChildren().Length();
int32_t length = aContext->Children().Length();
if (!aIndexedProps.reserve(aIndexedProps.length() + length)) {
return false;
}

Просмотреть файл

@ -169,7 +169,7 @@ bool WindowNamedPropertiesHandler::ownPropNames(
nsGlobalWindowOuter* outer = win->GetOuterWindowInternal();
if (outer) {
if (BrowsingContext* bc = outer->GetBrowsingContext()) {
for (const auto& child : bc->GetChildren()) {
for (const auto& child : bc->Children()) {
const nsString& name = child->Name();
if (!name.IsEmpty() && !names.Contains(name)) {
// Make sure we really would expose it from getOwnPropDescriptor.

Просмотреть файл

@ -564,9 +564,7 @@ void AutoSuppressEventHandlingAndSuspend::SuppressBrowsingContext(
}
}
BrowsingContext::Children children;
aBC->GetChildren(children);
for (const auto& bc : children) {
for (const auto& bc : aBC->Children()) {
SuppressBrowsingContext(bc);
}
}

Просмотреть файл

@ -274,24 +274,18 @@ static already_AddRefed<BrowsingContext> CreateBrowsingContext(
RefPtr<BrowsingContext> opener;
if (aOpenWindowInfo && !aOpenWindowInfo->GetForceNoOpener()) {
opener = aOpenWindowInfo->GetParent();
MOZ_ASSERT(opener->IsInProcess(),
"Must create BrowsingContext with opener in-process");
}
Document* doc = aOwner->OwnerDoc();
// Get our parent docshell off the document of mOwnerContent
// XXXbz this is such a total hack.... We really need to have a
// better setup for doing this.
// Determine our parent nsDocShell
RefPtr<nsDocShell> parentDocShell = nsDocShell::Cast(doc->GetDocShell());
if (NS_WARN_IF(!parentDocShell)) {
RefPtr<nsGlobalWindowInner> parentInner =
nsGlobalWindowInner::Cast(aOwner->OwnerDoc()->GetInnerWindow());
if (NS_WARN_IF(!parentInner) || parentInner->IsDying()) {
return nullptr;
}
RefPtr<BrowsingContext> parentContext = parentDocShell->GetBrowsingContext();
// Don't create a child docshell for a discarded browsing context.
if (NS_WARN_IF(!parentContext) || parentContext->IsDiscarded()) {
BrowsingContext* parentBC = parentInner->GetBrowsingContext();
if (NS_WARN_IF(!parentBC) || parentBC->IsDiscarded()) {
return nullptr;
}
@ -305,7 +299,7 @@ static already_AddRefed<BrowsingContext> CreateBrowsingContext(
// currently active. And in that latter case, if we try to attach our BC now,
// it will wind up attached as a child of the currently active inner window
// for the BrowsingContext, and cause no end of trouble.
if (IsTopContent(parentContext, aOwner)) {
if (IsTopContent(parentBC, aOwner)) {
// Create toplevel content without a parent & as Type::Content.
return BrowsingContext::CreateDetached(nullptr, opener, frameName,
BrowsingContext::Type::Content);
@ -314,11 +308,8 @@ static already_AddRefed<BrowsingContext> CreateBrowsingContext(
MOZ_ASSERT(!aOpenWindowInfo,
"Can't have openWindowInfo for non-toplevel context");
auto type = parentContext->IsContent() ? BrowsingContext::Type::Content
: BrowsingContext::Type::Chrome;
return BrowsingContext::CreateDetached(parentContext, nullptr, frameName,
type);
return BrowsingContext::CreateDetached(parentInner, nullptr, frameName,
parentBC->GetType());
}
static bool InitialLoadIsRemote(Element* aOwner) {
@ -827,7 +818,7 @@ void nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem,
static bool AllDescendantsOfType(BrowsingContext* aParent,
BrowsingContext::Type aType) {
for (auto& child : aParent->GetChildren()) {
for (auto& child : aParent->Children()) {
if (child->GetType() != aType || !AllDescendantsOfType(child, aType)) {
return false;
}

Просмотреть файл

@ -5388,10 +5388,7 @@ CallState nsGlobalWindowInner::CallOnInProcessChildren(Method aMethod,
return state;
}
BrowsingContext::Children children;
GetBrowsingContext()->GetChildren(children);
for (const RefPtr<BrowsingContext>& bc : children) {
for (const RefPtr<BrowsingContext>& bc : GetBrowsingContext()->Children()) {
nsCOMPtr<nsPIDOMWindowOuter> pWin = bc->GetDOMWindow();
if (!pWin) {
continue;

Просмотреть файл

@ -3424,7 +3424,7 @@ Nullable<WindowProxyHolder> nsGlobalWindowOuter::IndexedGetterOuter(
BrowsingContext* bc = GetBrowsingContext();
NS_ENSURE_TRUE(bc, nullptr);
const BrowsingContext::Children& children = bc->GetChildren();
Span<RefPtr<BrowsingContext>> children = bc->Children();
if (aIndex < children.Length()) {
return WindowProxyHolder(children[aIndex]);
}
@ -4192,7 +4192,7 @@ double nsGlobalWindowOuter::GetScrollYOuter() { return GetScrollXY(false).y; }
uint32_t nsGlobalWindowOuter::Length() {
BrowsingContext* bc = GetBrowsingContext();
return bc ? bc->GetChildren().Length() : 0;
return bc ? bc->Children().Length() : 0;
}
Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetTopOuter() {

Просмотреть файл

@ -68,7 +68,7 @@ static nsresult AddNonJSSizeOfWindowAndItsDescendents(
}
// Measure this window's descendents.
for (const auto& frame : bc->GetChildren()) {
for (const auto& frame : bc->Children()) {
if (auto* childWin = nsGlobalWindowOuter::Cast(frame->GetDOMWindow())) {
MOZ_TRY(AddNonJSSizeOfWindowAndItsDescendents(childWin, aSizes));
}

Просмотреть файл

@ -4055,56 +4055,19 @@ void BrowserParent::OnSubFrameCrashed() {
if (mBrowsingContext->IsDiscarded()) {
return;
}
auto processId = Manager()->ChildID();
BrowsingContext* parent = mBrowsingContext->GetParent();
ContentParent* embedderProcess = parent->Canonical()->GetContentParent();
if (!embedderProcess) {
BrowserBridgeParent* bridge = GetBrowserBridgeParent();
if (!bridge || !bridge->CanSend()) {
return;
}
ContentParent* manager = Manager();
// Set the owner process of a browsing context belonging to a
// crashed process to the parent context's process, since
// we'll be showing the crashed page in that process.
mBrowsingContext->SetOwnerProcessId(embedderProcess->ChildID());
// Set the owner process of the root context belonging to a crashed process to
// the embedding process, since we'll be showing the crashed page in that
// process.
mBrowsingContext->SetOwnerProcessId(bridge->Manager()->Manager()->ChildID());
mBrowsingContext->SetCurrentInnerWindowId(0);
// Find all same process sub tree nodes and detach them, cache all
// other nodes in the sub tree.
mBrowsingContext->PostOrderWalk([&](auto* aContext) {
// By iterating in reverse we can deal with detach removing the child that
// we're currently on
for (auto it = aContext->GetChildren().rbegin();
it != aContext->GetChildren().rend(); it++) {
RefPtr<BrowsingContext> context = *it;
if (context->Canonical()->IsOwnedByProcess(processId)) {
// Hold a reference to `context` until the response comes back to
// ensure it doesn't die while messages relating to this context are
// in-flight.
auto resolve = [context](bool) {};
auto reject = [context](ResponseRejectReason) {};
context->Group()->EachOtherParent(manager, [&](auto* aParent) {
aParent->SendDetachBrowsingContext(context->Id(), resolve, reject);
});
context->Detach(/* aFromIPC */ true);
}
}
// Cache all the children not owned by crashing process. Note that
// all remaining children are out of process, which makes it ok to
// just cache.
aContext->Group()->EachOtherParent(manager, [&](auto* aParent) {
Unused << aParent->SendCacheBrowsingContextChildren(aContext);
});
aContext->CacheChildren(/* aFromIPC */ true);
});
MOZ_DIAGNOSTIC_ASSERT(!mBrowsingContext->GetChildren().Length());
// Tell the browser bridge to show the subframe crashed page.
if (GetBrowserBridgeParent()) {
Unused << GetBrowserBridgeParent()->SendSubFrameCrashed(mBrowsingContext);
}
Unused << bridge->SendSubFrameCrashed(mBrowsingContext);
}
mozilla::ipc::IPCResult BrowserParent::RecvIsWindowSupportingProtectedMedia(

Просмотреть файл

@ -3647,75 +3647,48 @@ mozilla::ipc::IPCResult ContentChild::RecvDetachBrowsingContext(
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvCacheBrowsingContextChildren(
const MaybeDiscarded<BrowsingContext>& aContext) {
if (aContext.IsNullOrDiscarded()) {
return IPC_OK();
}
aContext.get()->CacheChildren(/* aFromIPC */ true);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvRestoreBrowsingContextChildren(
const MaybeDiscarded<BrowsingContext>& aContext,
const nsTArray<MaybeDiscarded<BrowsingContext>>& aChildren) {
if (aContext.IsNullOrDiscarded()) {
return IPC_OK();
}
nsTArray<RefPtr<BrowsingContext>> children(aChildren.Length());
for (const auto& child : aChildren) {
if (!child.IsNullOrDiscarded()) {
children.AppendElement(child.get());
}
}
aContext.get()->RestoreChildren(std::move(children), /* aFromIPC */ true);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvRegisterBrowsingContextGroup(
nsTArray<BrowsingContext::IPCInitializer>&& aInits,
nsTArray<WindowContext::IPCInitializer>&& aWindowInits) {
nsTArray<SyncedContextInitializer>&& aInits) {
RefPtr<BrowsingContextGroup> group = new BrowsingContextGroup();
// Each of the initializers in aInits is sorted in pre-order, so our parent
// should always be available before the element itself.
for (auto& init : aInits) {
for (auto& initUnion : aInits) {
switch (initUnion.type()) {
case SyncedContextInitializer::TBrowsingContextInitializer: {
auto& init = initUnion.get_BrowsingContextInitializer();
#ifdef DEBUG
RefPtr<BrowsingContext> existing = BrowsingContext::Get(init.mId);
MOZ_ASSERT(!existing, "BrowsingContext must not exist yet!");
RefPtr<BrowsingContext> existing = BrowsingContext::Get(init.mId);
MOZ_ASSERT(!existing, "BrowsingContext must not exist yet!");
RefPtr<BrowsingContext> parent = init.GetParent();
MOZ_ASSERT_IF(parent, parent->Group() == group);
RefPtr<WindowContext> parent = init.GetParent();
MOZ_ASSERT_IF(parent, parent->Group() == group);
#endif
bool cached = init.mCached;
RefPtr<BrowsingContext> ctxt =
BrowsingContext::CreateFromIPC(std::move(init), group, nullptr);
RefPtr<BrowsingContext> ctxt =
BrowsingContext::CreateFromIPC(std::move(init), group, nullptr);
ctxt->Attach(/* aFromIPC */ true);
break;
}
case SyncedContextInitializer::TWindowContextInitializer: {
auto& init = initUnion.get_WindowContextInitializer();
#ifdef DEBUG
RefPtr<WindowContext> existing =
WindowContext::GetById(init.mInnerWindowId);
MOZ_ASSERT(!existing, "WindowContext must not exist yet!");
RefPtr<BrowsingContext> parent =
BrowsingContext::Get(init.mBrowsingContextId);
MOZ_ASSERT(parent && parent->Group() == group);
#endif
// If the browsing context is cached don't attach it, but add it
// to the cache here as well
if (cached) {
ctxt->Group()->CacheContext(ctxt);
} else {
ctxt->Attach(/* aFromIPC */ true);
WindowContext::CreateFromIPC(std::move(init));
break;
};
default:
MOZ_ASSERT_UNREACHABLE();
}
}
for (auto& init : aWindowInits) {
#ifdef DEBUG
RefPtr<WindowContext> existing =
WindowContext::GetById(init.mInnerWindowId);
MOZ_ASSERT(!existing, "WindowContext must not exist yet!");
RefPtr<BrowsingContext> parent =
BrowsingContext::Get(init.mBrowsingContextId);
MOZ_ASSERT(parent && parent->Group() == group);
#endif
WindowContext::CreateFromIPC(std::move(init));
}
return IPC_OK();
}

Просмотреть файл

@ -716,16 +716,8 @@ class ContentChild final
mozilla::ipc::IPCResult RecvDetachBrowsingContext(
uint64_t aContextId, DetachBrowsingContextResolver&& aResolve);
mozilla::ipc::IPCResult RecvCacheBrowsingContextChildren(
const MaybeDiscarded<BrowsingContext>& aContext);
mozilla::ipc::IPCResult RecvRestoreBrowsingContextChildren(
const MaybeDiscarded<BrowsingContext>& aContext,
const nsTArray<MaybeDiscarded<BrowsingContext>>& aChildren);
mozilla::ipc::IPCResult RecvRegisterBrowsingContextGroup(
nsTArray<BrowsingContext::IPCInitializer>&& aInits,
nsTArray<WindowContext::IPCInitializer>&& aWindowInits);
nsTArray<SyncedContextInitializer>&& aInits);
mozilla::ipc::IPCResult RecvWindowClose(
const MaybeDiscarded<BrowsingContext>& aContext, bool aTrustedCaller);

Просмотреть файл

@ -5915,13 +5915,13 @@ mozilla::ipc::IPCResult ContentParent::RecvSessionStorageData(
mozilla::ipc::IPCResult ContentParent::RecvAttachBrowsingContext(
BrowsingContext::IPCInitializer&& aInit) {
RefPtr<CanonicalBrowsingContext> parent;
RefPtr<WindowGlobalParent> parent;
if (aInit.mParentId != 0) {
parent = CanonicalBrowsingContext::Get(aInit.mParentId);
parent = WindowGlobalParent::GetByInnerWindowId(aInit.mParentId);
MOZ_RELEASE_ASSERT(parent, "Parent doesn't exist in parent process");
}
if (parent && !parent->IsOwnedByProcess(ChildID())) {
if (parent && parent->GetContentParent() != this) {
// We're trying attach a child BrowsingContext to a parent
// BrowsingContext in another process. This is illegal since the
// only thing that could create that child BrowsingContext is a
@ -6017,73 +6017,6 @@ mozilla::ipc::IPCResult ContentParent::RecvDetachBrowsingContext(
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvCacheBrowsingContextChildren(
const MaybeDiscarded<BrowsingContext>& aContext) {
if (aContext.IsNullOrDiscarded()) {
MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
("ParentIPC: Trying to cache already detached"));
return IPC_OK();
}
CanonicalBrowsingContext* context = aContext.get_canonical();
if (!CheckBrowsingContextOwnership(context, "cache")) {
// We're trying to cache a child BrowsingContext in another child
// process. This is illegal since the owner of the BrowsingContext
// is the proccess with the in-process docshell, which is tracked
// by OwnerProcessId.
return IPC_OK();
}
context->CacheChildren(/* aFromIPC */ true);
context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
Unused << aParent->SendCacheBrowsingContextChildren(context);
});
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvRestoreBrowsingContextChildren(
const MaybeDiscarded<BrowsingContext>& aContext,
nsTArray<MaybeDiscarded<BrowsingContext>>&& aChildren) {
if (aContext.IsNullOrDiscarded()) {
MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
("ParentIPC: Trying to restore already detached"));
return IPC_OK();
}
CanonicalBrowsingContext* context = aContext.get_canonical();
if (!CheckBrowsingContextOwnership(context, "restore")) {
// We're trying to cache a child BrowsingContext in another child
// process. This is illegal since the owner of the BrowsingContext
// is the proccess with the in-process docshell, which is tracked
// by OwnerProcessId.
return IPC_OK();
}
// Remove any null or discarded child BrowsingContexts, creating a list with
// only active contexts.
// Modify the existing BrowsingContext as it will be passed to each other
// process using `SendRestoreBrowsingContextChildren`.
nsTArray<RefPtr<BrowsingContext>> children(aChildren.Length());
aChildren.RemoveElementsBy(
[&](const MaybeDiscarded<BrowsingContext>& child) -> bool {
if (child.IsNullOrDiscarded()) {
return true;
}
children.AppendElement(child.get());
return false;
});
context->RestoreChildren(std::move(children), /* aFromIPC */ true);
context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
Unused << aParent->SendRestoreBrowsingContextChildren(context, aChildren);
});
return IPC_OK();
}
void ContentParent::RegisterRemoteWorkerActor() {
auto lock = mRemoteWorkerActorData.Lock();
++lock->mCount;

Просмотреть файл

@ -646,13 +646,6 @@ class ContentParent final
mozilla::ipc::IPCResult RecvDetachBrowsingContext(
uint64_t aContextId, DetachBrowsingContextResolver&& aResolve);
mozilla::ipc::IPCResult RecvCacheBrowsingContextChildren(
const MaybeDiscarded<BrowsingContext>& aContext);
mozilla::ipc::IPCResult RecvRestoreBrowsingContextChildren(
const MaybeDiscarded<BrowsingContext>& aContext,
nsTArray<MaybeDiscarded<BrowsingContext>>&& aChildren);
mozilla::ipc::IPCResult RecvWindowClose(
const MaybeDiscarded<BrowsingContext>& aContext, bool aTrustedCaller);
mozilla::ipc::IPCResult RecvWindowFocus(

Просмотреть файл

@ -342,6 +342,12 @@ struct KeyValuePair
nsString value;
};
union SyncedContextInitializer
{
BrowsingContextInitializer;
WindowContextInitializer;
};
/**
* The PContent protocol is a top-level protocol between the UI process
* and a content process. There is exactly one PContentParent/PContentChild pair
@ -826,8 +832,7 @@ child:
// Begin subscribing to a new BrowsingContextGroup, sending down the current
// value for every individual BrowsingContext.
async RegisterBrowsingContextGroup(BrowsingContextInitializer[] aInits,
WindowContextInitializer[] aWindowInits);
async RegisterBrowsingContextGroup(SyncedContextInitializer[] aInits);
#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
// Initialize top-level actor for testing content process sandbox.
@ -1594,18 +1599,6 @@ both:
*/
async DetachBrowsingContext(uint64_t aContextId) returns (bool unused);
/**
* Removes all of 'aContext'\'s children, and caches them in the
* BrowsingContextGroup.
*/
async CacheBrowsingContextChildren(MaybeDiscardedBrowsingContext aContext);
/**
* Re-attach all BrowsingContexts in a 'aContext'.
*/
async RestoreBrowsingContextChildren(MaybeDiscardedBrowsingContext aContext,
MaybeDiscardedBrowsingContext[] aChildren);
async WindowClose(MaybeDiscardedBrowsingContext aContext,
bool aTrustedCaller);
async WindowFocus(MaybeDiscardedBrowsingContext aContext,

Просмотреть файл

@ -11081,7 +11081,7 @@ void PresShell::SetIsUnderHiddenEmbedderElement(
BrowsingContext* bc = docShell->GetBrowsingContext();
// Propagate to children.
for (BrowsingContext* child : bc->GetChildren()) {
for (BrowsingContext* child : bc->Children()) {
Element* embedderElement = child->GetEmbedderElement();
if (!embedderElement) {
// TODO: We shouldn't need to null check here since `child` and the

Просмотреть файл

@ -3195,7 +3195,7 @@ static void DumpViews(nsIDocShell* aDocShell, FILE* out) {
// dump the views of the sub documents
int32_t i, n;
BrowsingContext* bc = nsDocShell::Cast(aDocShell)->GetBrowsingContext();
for (auto& child : bc->GetChildren()) {
for (auto& child : bc->Children()) {
if (auto childDS = child->GetDocShell()) {
DumpViews(childAsShell, out);
}

Просмотреть файл

@ -99,7 +99,7 @@ nsresult GetTabSizes(nsGlobalWindowOuter* aWindow, nsTabSizes* aSizes) {
}
// Measure this window's descendents.
for (const auto& frame : bc->GetChildren()) {
for (const auto& frame : bc->Children()) {
if (auto* childWin = nsGlobalWindowOuter::Cast(frame->GetDOMWindow())) {
MOZ_TRY(GetTabSizes(childWin, aSizes));
}

Просмотреть файл

@ -487,7 +487,7 @@ int CollectPositions(BrowsingContext* aBrowsingContext,
/* Collect data from all child frame */
// This is not going to work for fission. Bug 1572084 for tracking it.
for (auto& child : aBrowsingContext->GetChildren()) {
for (auto& child : aBrowsingContext->Children()) {
aPositionDescendants[currentIdx] +=
CollectPositions(child, aPositions, aPositionDescendants);
}
@ -576,7 +576,7 @@ int CollectInputs(BrowsingContext* aBrowsingContext,
/* Collect data from all child frame */
// This is not going to work for fission. Bug 1572084 for tracking it.
for (auto& child : aBrowsingContext->GetChildren()) {
for (auto& child : aBrowsingContext->Children()) {
aInputs[currentIdx].descendants +=
CollectInputs(child, aInputs, aIdVals, aXPathVals);
}

Просмотреть файл

@ -1169,7 +1169,7 @@ void SessionStoreUtils::CollectedSessionStorage(
}
// This is not going to work for fission. Bug 1572084 for tracking it.
for (BrowsingContext* child : aBrowsingContext->GetChildren()) {
for (BrowsingContext* child : aBrowsingContext->Children()) {
window = child->GetDOMWindow();
if (!window) {
return;
@ -1279,7 +1279,7 @@ static void CollectFrameTreeData(JSContext* aCx,
uint32_t trailingNullCounter = 0;
// This is not going to work for fission. Bug 1572084 for tracking it.
for (auto& child : aBrowsingContext->GetChildren()) {
for (auto& child : aBrowsingContext->Children()) {
NullableRootedDictionary<CollectedData> data(aCx);
CollectFrameTreeData(aCx, child, data, aFunc);
if (data.IsNull()) {