Bug 1650257: Part 2 - Abort SetNewDocument() if ancestors are discarded/cached. r=nika,smaug,sg

Differential Revision: https://phabricator.services.mozilla.com/D87486
This commit is contained in:
Kris Maglione 2020-08-31 18:51:56 +00:00
Родитель 5f2d674982
Коммит c2c6945169
12 изменённых файлов: 86 добавлений и 59 удалений

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

@ -770,6 +770,25 @@ bool BrowsingContext::HasOpener() const {
return sBrowsingContexts->Contains(GetOpenerId());
}
bool BrowsingContext::AncestorsAreCurrent() const {
const BrowsingContext* bc = this;
while (true) {
if (bc->IsDiscarded()) {
return false;
}
if (WindowContext* wc = bc->GetParentWindowContext()) {
if (wc->IsCached() || wc->IsDiscarded()) {
return false;
}
bc = wc->GetBrowsingContext();
} else {
return true;
}
}
}
Span<RefPtr<BrowsingContext>> BrowsingContext::Children() const {
if (WindowContext* current = mCurrentWindowContext) {
return current->Children();
@ -1610,6 +1629,15 @@ void BrowsingContext::Location(JSContext* aCx,
}
}
bool BrowsingContext::RemoveRootFromBFCacheSync() {
if (WindowContext* wc = GetParentWindowContext()) {
if (RefPtr<Document> doc = wc->TopWindowContext()->GetDocument()) {
return doc->RemoveFromBFCacheSync();
}
}
return false;
}
nsresult BrowsingContext::CheckSandboxFlags(nsDocShellLoadState* aLoadState) {
const auto& sourceBC = aLoadState->SourceBrowsingContext();
if (sourceBC.IsDiscarded() || (sourceBC && sourceBC->IsSandboxedFrom(this))) {

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

@ -239,6 +239,10 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
// message.
bool IsDiscarded() const { return mIsDiscarded; }
// Returns true if none of the BrowsingContext's ancestor BrowsingContexts or
// WindowContexts are discarded or cached.
bool AncestorsAreCurrent() const;
bool Windowless() const { return mWindowless; }
// Get the DocShell for this BrowsingContext if it is in-process, or
@ -293,6 +297,10 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
nsresult InternalLoad(nsDocShellLoadState* aLoadState);
// Removes the root document for this BrowsingContext tree from the BFCache,
// if it is cached, and returns true if it was.
bool RemoveRootFromBFCacheSync();
// If the load state includes a source BrowsingContext has been passed, check
// to see if we are sandboxed from it as the result of an iframe or CSP
// sandbox.

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

@ -24,6 +24,8 @@
#include "mozilla/NullPrincipal.h"
#include "nsIWebNavigation.h"
#include "mozilla/MozPromiseInlines.h"
#include "nsDocShell.h"
#include "nsFrameLoader.h"
#include "nsGlobalWindowOuter.h"
#include "nsIWebBrowserChrome.h"
#include "nsNetUtil.h"
@ -748,28 +750,6 @@ void CanonicalBrowsingContext::Stop(uint32_t aStopFlags) {
}
}
// While process switching, we need to check if any of our ancestors are
// discarded or no longer current, in which case the process switch needs to be
// aborted.
static bool AncestorsAreCurrent(CanonicalBrowsingContext* aContext) {
if (aContext->IsDiscarded()) {
return false;
}
RefPtr<WindowGlobalParent> ancestorWindow(aContext->GetParentWindowContext());
while (ancestorWindow) {
// If our ancestor window is no longer the current window, or if any of our
// ancestors have been discarded, return `false` to abort the process
// switch.
if (ancestorWindow->IsCached() || ancestorWindow->IsDiscarded() ||
ancestorWindow->GetBrowsingContext()->IsDiscarded()) {
return false;
}
ancestorWindow = ancestorWindow->GetParentWindowContext();
}
return true;
}
void CanonicalBrowsingContext::PendingRemotenessChange::ProcessReady() {
if (!mPromise) {
return;
@ -798,7 +778,10 @@ void CanonicalBrowsingContext::PendingRemotenessChange::Finish() {
return;
}
if (!AncestorsAreCurrent(target)) {
// While process switching, we need to check if any of our ancestors are
// discarded or no longer current, in which case the process switch needs to be
// aborted.
if (!target->AncestorsAreCurrent()) {
NS_WARNING("Ancestor context is no longer current");
Cancel(NS_ERROR_FAILURE);
return;
@ -1054,7 +1037,7 @@ CanonicalBrowsingContext::ChangeRemoteness(const nsACString& aRemoteType,
MOZ_DIAGNOSTIC_ASSERT(aSpecificGroupId == 0 || aReplaceBrowsingContext,
"Cannot specify group ID unless replacing BC");
if (!AncestorsAreCurrent(this)) {
if (!AncestorsAreCurrent()) {
NS_WARNING("An ancestor context is no longer current");
return RemotenessPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}

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

@ -6398,6 +6398,11 @@ nsresult nsDocShell::CreateAboutBlankContentViewer(
return NS_ERROR_FAILURE;
}
if (!mBrowsingContext->AncestorsAreCurrent()) {
mBrowsingContext->RemoveRootFromBFCacheSync();
return NS_ERROR_NOT_AVAILABLE;
}
// mContentViewer->PermitUnload may release |this| docshell.
nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
@ -7518,6 +7523,11 @@ nsresult nsDocShell::CreateContentViewer(const nsACString& aContentType,
return NS_ERROR_DOCSHELL_DYING;
}
if (!mBrowsingContext->AncestorsAreCurrent()) {
mBrowsingContext->RemoveRootFromBFCacheSync();
return NS_ERROR_NOT_AVAILABLE;
}
// Can we check the content type of the current content viewer
// and reuse it without destroying it and re-creating it?

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

@ -6355,6 +6355,14 @@ void Document::SetBFCacheEntry(nsIBFCacheEntry* aEntry) {
mBFCacheEntry = aEntry;
}
bool Document::RemoveFromBFCacheSync() {
if (nsCOMPtr<nsIBFCacheEntry> entry = GetBFCacheEntry()) {
entry->RemoveFromBFCacheSync();
return true;
}
return false;
}
static void SubDocClearEntry(PLDHashTable* table, PLDHashEntryHdr* entry) {
SubDocMapEntry* e = static_cast<SubDocMapEntry*>(entry);

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

@ -1156,6 +1156,10 @@ class Document : public nsINode,
nsIBFCacheEntry* GetBFCacheEntry() const { return mBFCacheEntry; }
// Removes this document from the BFCache, if it is cached, and returns
// true if it was.
bool RemoveFromBFCacheSync();
/**
* Return the parent document of this document. Will return null
* unless this document is within a compound document and has a

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

@ -7418,6 +7418,13 @@ mozilla::dom::WindowContext* nsPIDOMWindowInner::GetWindowContext() const {
return mWindowGlobalChild ? mWindowGlobalChild->WindowContext() : nullptr;
}
bool nsPIDOMWindowInner::RemoveFromBFCacheSync() {
if (Document* doc = GetExtantDoc()) {
return doc->RemoveFromBFCacheSync();
}
return false;
}
void nsPIDOMWindowInner::MaybeCreateDoc() {
// XXX: Forward to outer?
MOZ_ASSERT(!mDoc);

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

@ -2076,6 +2076,10 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
return NS_ERROR_UNEXPECTED;
}
if (!mBrowsingContext->AncestorsAreCurrent()) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<Document> oldDoc = mDoc;
MOZ_RELEASE_ASSERT(oldDoc != aDocument);

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

@ -354,6 +354,10 @@ class nsPIDOMWindowInner : public mozIDOMWindow {
return mWindowGlobalChild;
}
// Removes this inner window from the BFCache, if it is cached, and returns
// true if it was.
bool RemoveFromBFCacheSync();
// Determine if the window is suspended or frozen. Outer windows
// will forward this call to the inner window for convenience. If
// there is no inner window then the outer window is considered

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

@ -354,22 +354,9 @@ void BroadcastChannel::RemoveDocFromBFCache() {
return;
}
nsPIDOMWindowInner* window = GetOwner();
if (!window) {
return;
if (nsPIDOMWindowInner* window = GetOwner()) {
window->RemoveFromBFCacheSync();
}
Document* doc = window->GetExtantDoc();
if (!doc) {
return;
}
nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry();
if (!bfCacheEntry) {
return;
}
bfCacheEntry->RemoveFromBFCacheSync();
}
void BroadcastChannel::DisconnectFromOwner() {

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

@ -1786,11 +1786,8 @@ mozilla::ipc::IPCResult BackgroundDatabaseChild::RecvVersionChange(
// Anything in the bfcache has to be evicted and then we have to close the
// database also.
if (nsCOMPtr<Document> doc = owner->GetExtantDoc()) {
if (nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry()) {
bfCacheEntry->RemoveFromBFCacheSync();
shouldAbortAndClose = true;
}
if (owner->RemoveFromBFCacheSync()) {
shouldAbortAndClose = true;
}
if (shouldAbortAndClose) {

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

@ -834,22 +834,9 @@ void MessagePort::RemoveDocFromBFCache() {
return;
}
nsPIDOMWindowInner* window = GetOwner();
if (!window) {
return;
if (nsPIDOMWindowInner* window = GetOwner()) {
window->RemoveFromBFCacheSync();
}
Document* doc = window->GetExtantDoc();
if (!doc) {
return;
}
nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry();
if (!bfCacheEntry) {
return;
}
bfCacheEntry->RemoveFromBFCacheSync();
}
/* static */