Backed out 2 changesets (bug 1810619) for causing wd failures in dom_content_loaded.py CLOSED TREE

Backed out changeset 81d6ef111d4f (bug 1810619)
Backed out changeset 637be53c4d27 (bug 1810619)
This commit is contained in:
Cristian Tuns 2023-03-09 01:22:56 -05:00
parent 32646098cd
commit 2af0d81d29
18 changed files with 329 additions and 305 deletions

View File

@ -1194,15 +1194,104 @@ void BrowsingContext::GetAllBrowsingContextsInSubtree(
});
}
// FindWithName follows the rules for choosing a browsing context,
// with the exception of sandboxing for iframes. The implementation
// for arbitrarily choosing between two browsing contexts with the
// same name is as follows:
//
// 1) The start browsing context, i.e. 'this'
// 2) Descendants in insertion order
// 3) The parent
// 4) Siblings and their children, both in insertion order
// 5) After this we iteratively follow the parent chain, repeating 3
// and 4 until
// 6) If there is no parent, consider all other top level browsing
// contexts and their children, both in insertion order
//
// See
// https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
BrowsingContext* BrowsingContext::FindWithName(
const nsAString& aName, bool aUseEntryGlobalForAccessCheck) {
RefPtr<BrowsingContext> requestingContext = this;
if (aUseEntryGlobalForAccessCheck) {
if (nsGlobalWindowInner* caller = nsContentUtils::EntryInnerWindow()) {
if (caller->GetBrowsingContextGroup() == Group()) {
requestingContext = caller->GetBrowsingContext();
} else {
MOZ_RELEASE_ASSERT(caller->GetPrincipal()->IsSystemPrincipal(),
"caller must be either same-group or system");
}
}
}
MOZ_ASSERT(requestingContext, "must have a requestingContext");
BrowsingContext* found = nullptr;
if (aName.IsEmpty()) {
// You can't find a browsing context with an empty name.
found = nullptr;
} else if (aName.LowerCaseEqualsLiteral("_blank")) {
// Just return null. Caller must handle creating a new window with
// a blank name.
found = nullptr;
} else if (nsContentUtils::IsSpecialName(aName)) {
found = FindWithSpecialName(aName, *requestingContext);
} else if (BrowsingContext* child =
FindWithNameInSubtree(aName, *requestingContext)) {
found = child;
} else {
BrowsingContext* current = this;
do {
Span<RefPtr<BrowsingContext>> siblings;
BrowsingContext* parent = current->GetParent();
if (!parent) {
// We've reached the root of the tree, consider browsing
// contexts in the same browsing context group.
siblings = mGroup->Toplevels();
} else if (parent->NameEquals(aName) &&
requestingContext->CanAccess(parent) &&
parent->IsTargetable()) {
found = parent;
break;
} else {
siblings = parent->NonSyntheticChildren();
}
for (BrowsingContext* sibling : siblings) {
if (sibling == current) {
continue;
}
if (BrowsingContext* relative =
sibling->FindWithNameInSubtree(aName, *requestingContext)) {
found = relative;
// Breaks the outer loop
parent = nullptr;
break;
}
}
current = parent;
} while (current);
}
// Helpers should perform access control checks, which means that we
// only need to assert that we can access found.
MOZ_DIAGNOSTIC_ASSERT(!found || requestingContext->CanAccess(found));
return found;
}
BrowsingContext* BrowsingContext::FindChildWithName(
const nsAString& aName, WindowGlobalChild& aRequestingWindow) {
const nsAString& aName, BrowsingContext& aRequestingContext) {
if (aName.IsEmpty()) {
// You can't find a browsing context with the empty name.
return nullptr;
}
for (BrowsingContext* child : NonSyntheticChildren()) {
if (child->NameEquals(aName) && aRequestingWindow.CanNavigate(child) &&
if (child->NameEquals(aName) && aRequestingContext.CanAccess(child) &&
child->IsTargetable()) {
return child;
}
@ -1212,7 +1301,7 @@ BrowsingContext* BrowsingContext::FindChildWithName(
}
BrowsingContext* BrowsingContext::FindWithSpecialName(
const nsAString& aName, WindowGlobalChild& aRequestingWindow) {
const nsAString& aName, BrowsingContext& aRequestingContext) {
// TODO(farre): Neither BrowsingContext nor nsDocShell checks if the
// browsing context pointed to by a special name is active. Should
// it be? See Bug 1527913.
@ -1222,7 +1311,7 @@ BrowsingContext* BrowsingContext::FindWithSpecialName(
if (aName.LowerCaseEqualsLiteral("_parent")) {
if (BrowsingContext* parent = GetParent()) {
return aRequestingWindow.CanNavigate(parent) ? parent : nullptr;
return aRequestingContext.CanAccess(parent) ? parent : nullptr;
}
return this;
}
@ -1230,25 +1319,24 @@ BrowsingContext* BrowsingContext::FindWithSpecialName(
if (aName.LowerCaseEqualsLiteral("_top")) {
BrowsingContext* top = Top();
return aRequestingWindow.CanNavigate(top) ? top : nullptr;
return aRequestingContext.CanAccess(top) ? top : nullptr;
}
return nullptr;
}
BrowsingContext* BrowsingContext::FindWithNameInSubtree(
const nsAString& aName, WindowGlobalChild* aRequestingWindow) {
const nsAString& aName, BrowsingContext& aRequestingContext) {
MOZ_DIAGNOSTIC_ASSERT(!aName.IsEmpty());
if (NameEquals(aName) &&
(!aRequestingWindow || aRequestingWindow->CanNavigate(this)) &&
if (NameEquals(aName) && aRequestingContext.CanAccess(this) &&
IsTargetable()) {
return this;
}
for (BrowsingContext* child : NonSyntheticChildren()) {
if (BrowsingContext* found =
child->FindWithNameInSubtree(aName, aRequestingWindow)) {
child->FindWithNameInSubtree(aName, aRequestingContext)) {
return found;
}
}
@ -1256,6 +1344,48 @@ BrowsingContext* BrowsingContext::FindWithNameInSubtree(
return nullptr;
}
// For historical context, see:
//
// Bug 13871: Prevent frameset spoofing
// Bug 103638: Targets with same name in different windows open in wrong
// window with javascript
// Bug 408052: Adopt "ancestor" frame navigation policy
// Bug 1570207: Refactor logic to rely on BrowsingContextGroups to enforce
// origin attribute isolation.
bool BrowsingContext::CanAccess(BrowsingContext* aTarget,
bool aConsiderOpener) {
MOZ_ASSERT(
mDocShell,
"CanAccess() may only be called in the process of the accessing window");
MOZ_ASSERT(aTarget, "Must have a target");
MOZ_DIAGNOSTIC_ASSERT(
Group() == aTarget->Group(),
"A BrowsingContext should never see a context from a different group");
// A frame can navigate itself and its own root.
if (aTarget == this || aTarget == Top()) {
return true;
}
// A frame can navigate any frame with a same-origin ancestor.
for (BrowsingContext* bc = aTarget; bc; bc = bc->GetParent()) {
if (bc->mDocShell && nsDocShell::ValidateOrigin(this, bc)) {
return true;
}
}
// If the target is a top-level document, a frame can navigate it if it can
// navigate its opener.
if (aConsiderOpener && !aTarget->GetParent()) {
if (RefPtr<BrowsingContext> opener = aTarget->GetOpener()) {
return CanAccess(opener, false);
}
}
return false;
}
bool BrowsingContext::IsSandboxedFrom(BrowsingContext* aTarget) {
// If no target then not sandboxed.
if (!aTarget) {
@ -1940,12 +2070,13 @@ nsresult BrowsingContext::LoadURI(nsDocShellLoadState* aLoadState,
MOZ_DIAGNOSTIC_ASSERT(!sourceBC || sourceBC->Group() == Group());
if (sourceBC && sourceBC->IsInProcess()) {
if (!sourceBC->CanAccess(this)) {
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
}
nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
if (WindowGlobalChild* wgc =
win->GetCurrentInnerWindow()->GetWindowGlobalChild()) {
if (!wgc->CanNavigate(this)) {
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
}
wgc->SendLoadURI(this, aLoadState, aSetNavigating);
}
} else if (XRE_IsParentProcess()) {
@ -2044,15 +2175,16 @@ nsresult BrowsingContext::InternalLoad(nsDocShellLoadState* aLoadState) {
MOZ_DIAGNOSTIC_ASSERT(sourceBC);
MOZ_DIAGNOSTIC_ASSERT(sourceBC->Group() == Group());
if (!sourceBC->CanAccess(this)) {
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
}
nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
WindowGlobalChild* wgc =
win->GetCurrentInnerWindow()->GetWindowGlobalChild();
if (!wgc || !wgc->CanSend()) {
return NS_ERROR_FAILURE;
}
if (!wgc->CanNavigate(this)) {
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
}
MOZ_ALWAYS_SUCCEEDS(
SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier())));

View File

@ -75,7 +75,6 @@ class SessionHistoryInfo;
class SessionStorageManager;
class StructuredCloneHolder;
class WindowContext;
class WindowGlobalChild;
struct WindowPostMessageOptions;
class WindowProxyHolder;
@ -660,26 +659,32 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
}
}
// Using the rules for choosing a browsing context we try to find
// the browsing context with the given name in the set of
// transitively reachable browsing contexts. Performs access control
// checks with regard to this.
// See
// https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name.
//
// BrowsingContext::FindWithName(const nsAString&) is equivalent to
// calling nsIDocShellTreeItem::FindItemWithName(aName, nullptr,
// nullptr, false, <return value>).
BrowsingContext* FindWithName(const nsAString& aName,
bool aUseEntryGlobalForAccessCheck = true);
// Find a browsing context in this context's list of
// children. Doesn't consider the special names, '_self', '_parent',
// '_top', or '_blank'. Performs access control checks with regard to
// 'this'.
BrowsingContext* FindChildWithName(const nsAString& aName,
WindowGlobalChild& aRequestingWindow);
BrowsingContext& aRequestingContext);
// Find a browsing context in the subtree rooted at 'this' Doesn't
// consider the special names, '_self', '_parent', '_top', or
// '_blank'.
//
// If passed, performs access control checks with regard to
// 'aRequestingContext', otherwise performs no access checks.
// '_blank'. Performs access control checks with regard to
// 'aRequestingContext'.
BrowsingContext* FindWithNameInSubtree(const nsAString& aName,
WindowGlobalChild* aRequestingWindow);
// Find the special browsing context if aName is '_self', '_parent',
// '_top', but not '_blank'. The latter is handled in FindWithName
BrowsingContext* FindWithSpecialName(const nsAString& aName,
WindowGlobalChild& aRequestingWindow);
BrowsingContext& aRequestingContext);
nsISupports* GetParentObject() const;
JSObject* WrapObject(JSContext* aCx,
@ -786,6 +791,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
BrowsingContextGroup* aGroup,
ContentParent* aOriginProcess);
// Performs access control to check that 'this' can access 'aTarget'.
bool CanAccess(BrowsingContext* aTarget, bool aConsiderOpener = true);
bool IsSandboxedFrom(BrowsingContext* aTarget);
// The runnable will be called once there is idle time, or the top level
@ -956,6 +964,11 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
// parent WC changes.
void RecomputeCanExecuteScripts();
// Find the special browsing context if aName is '_self', '_parent',
// '_top', but not '_blank'. The latter is handled in FindWithName
BrowsingContext* FindWithSpecialName(const nsAString& aName,
BrowsingContext& aRequestingContext);
// Is it early enough in the BrowsingContext's lifecycle that it is still
// OK to set OriginAttributes?
bool CanSetOriginAttributes();

View File

@ -1438,6 +1438,61 @@ nsDOMNavigationTiming* nsDocShell::GetNavigationTiming() const {
return mTiming;
}
//
// Bug 13871: Prevent frameset spoofing
//
// This routine answers: 'Is origin's document from same domain as
// target's document?'
//
// file: uris are considered the same domain for the purpose of
// frame navigation regardless of script accessibility (bug 420425)
//
/* static */
bool nsDocShell::ValidateOrigin(BrowsingContext* aOrigin,
BrowsingContext* aTarget) {
nsIDocShell* originDocShell = aOrigin->GetDocShell();
MOZ_ASSERT(originDocShell, "originDocShell must not be null");
Document* originDocument = originDocShell->GetDocument();
NS_ENSURE_TRUE(originDocument, false);
nsIDocShell* targetDocShell = aTarget->GetDocShell();
MOZ_ASSERT(targetDocShell, "targetDocShell must not be null");
Document* targetDocument = targetDocShell->GetDocument();
NS_ENSURE_TRUE(targetDocument, false);
bool equal;
nsresult rv = originDocument->NodePrincipal()->Equals(
targetDocument->NodePrincipal(), &equal);
if (NS_SUCCEEDED(rv) && equal) {
return true;
}
// Not strictly equal, special case if both are file: uris
nsCOMPtr<nsIURI> originURI;
nsCOMPtr<nsIURI> targetURI;
nsCOMPtr<nsIURI> innerOriginURI;
nsCOMPtr<nsIURI> innerTargetURI;
// Casting to BasePrincipal, as we can't get InnerMost URI otherwise
auto* originDocumentBasePrincipal =
BasePrincipal::Cast(originDocument->NodePrincipal());
rv = originDocumentBasePrincipal->GetURI(getter_AddRefs(originURI));
if (NS_SUCCEEDED(rv) && originURI) {
innerOriginURI = NS_GetInnermostURI(originURI);
}
auto* targetDocumentBasePrincipal =
BasePrincipal::Cast(targetDocument->NodePrincipal());
rv = targetDocumentBasePrincipal->GetURI(getter_AddRefs(targetURI));
if (NS_SUCCEEDED(rv) && targetURI) {
innerTargetURI = NS_GetInnermostURI(targetURI);
}
return innerOriginURI && innerTargetURI && SchemeIsFile(innerOriginURI) &&
SchemeIsFile(innerTargetURI);
}
nsPresContext* nsDocShell::GetEldestPresContext() {
nsIContentViewer* viewer = mContentViewer;
while (viewer) {
@ -8444,11 +8499,7 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
aLoadState->Target().LowerCaseEqualsLiteral("_self") ||
aLoadState->Target().LowerCaseEqualsLiteral("_parent") ||
aLoadState->Target().LowerCaseEqualsLiteral("_top")) {
Document* document = GetDocument();
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
WindowGlobalChild* wgc = document->GetWindowGlobalChild();
NS_ENSURE_TRUE(wgc, NS_ERROR_FAILURE);
targetContext = wgc->FindBrowsingContextWithName(
targetContext = mBrowsingContext->FindWithName(
aLoadState->Target(), /* aUseEntryGlobalForAccessCheck */ false);
}

View File

@ -558,6 +558,11 @@ class nsDocShell final : public nsDocLoader,
nsDocShell(mozilla::dom::BrowsingContext* aBrowsingContext,
uint64_t aContentWindowID);
// Security check to prevent frameset spoofing. See comments at
// implementation site.
static bool ValidateOrigin(mozilla::dom::BrowsingContext* aOrigin,
mozilla::dom::BrowsingContext* aTarget);
static inline uint32_t PRTimeToSeconds(PRTime aTimeUsec) {
return uint32_t(aTimeUsec / PR_USEC_PER_SEC);
}

View File

@ -107,14 +107,12 @@ add_task(async function() {
// wish to confirm that targeting is able to find
// appropriate browsing contexts.
// WindowGlobalChild.findBrowsingContextWithName requires access
// checks, which can only be performed in the process of the accessor
// WindowGlobalChild.
// BrowsingContext.findWithName requires access checks, which
// can only be performed in the process of the accessor BC's
// docShell.
function findWithName(bc, name) {
return content.SpecialPowers.spawn(bc, [name], name => {
return content.windowGlobalChild.findBrowsingContextWithName(
name
);
return content.SpecialPowers.spawn(bc, [bc, name], (bc, name) => {
return bc.findWithName(name);
});
}

View File

@ -3966,11 +3966,9 @@ Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetTopOuter() {
already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetChildWindow(
const nsAString& aName) {
NS_ENSURE_TRUE(mBrowsingContext, nullptr);
NS_ENSURE_TRUE(mInnerWindow, nullptr);
NS_ENSURE_TRUE(mInnerWindow->GetWindowGlobalChild(), nullptr);
return do_AddRef(mBrowsingContext->FindChildWithName(
aName, *mInnerWindow->GetWindowGlobalChild()));
return do_AddRef(
mBrowsingContext->FindChildWithName(aName, *mBrowsingContext));
}
bool nsGlobalWindowOuter::DispatchCustomEvent(
@ -4038,10 +4036,7 @@ bool nsGlobalWindowOuter::WindowExists(const nsAString& aName,
aName.LowerCaseEqualsLiteral("_parent");
}
if (WindowGlobalChild* wgc = mInnerWindow->GetWindowGlobalChild()) {
return wgc->FindBrowsingContextWithName(aName, aLookForCallerOnJSStack);
}
return false;
return !!mBrowsingContext->FindWithName(aName, aLookForCallerOnJSStack);
}
already_AddRefed<nsIWidget> nsGlobalWindowOuter::GetMainWidget() {

View File

@ -71,6 +71,9 @@ interface BrowsingContext {
sequence<BrowsingContext> getAllBrowsingContextsInSubtree();
BrowsingContext? findChildWithName(DOMString name, BrowsingContext accessor);
BrowsingContext? findWithName(DOMString name);
readonly attribute DOMString name;
readonly attribute BrowsingContext? parent;

View File

@ -177,8 +177,6 @@ interface WindowGlobalChild {
static WindowGlobalChild? getByInnerWindowId(unsigned long long innerWIndowId);
BrowsingContext? findBrowsingContextWithName(DOMString name);
/**
* Get or create the JSWindowActor with the given name.
*

View File

@ -17,7 +17,6 @@
#include "nsFocusManager.h"
#include "nsIBrowserDOMWindow.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIDOMChromeWindow.h"
#include "nsIURI.h"
#include "nsIBrowser.h"
@ -328,33 +327,25 @@ void GeckoViewOpenWindow(const ClientOpenWindowArgsParsed& aArgsValidated,
promiseResult->Then(
GetMainThreadSerialEventTarget(), __func__,
[aArgsValidated, promise](nsString sessionId) {
// Retrieve the primary content BrowsingContext using the GeckoSession
// ID. The chrome window is named the same as the ID of the GeckoSession
// it is associated with.
RefPtr<BrowsingContext> browsingContext;
nsresult rv = [&sessionId, &browsingContext]() -> nsresult {
nsresult rv;
nsCOMPtr<nsIWindowWatcher> wwatch =
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIDOMWindowProxy> chromeWindow;
rv = wwatch->GetWindowByName(sessionId, getter_AddRefs(chromeWindow));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(chromeWindow, NS_ERROR_FAILURE);
nsCOMPtr<nsIDocShellTreeOwner> treeOwner =
nsPIDOMWindowOuter::From(chromeWindow)->GetTreeOwner();
NS_ENSURE_TRUE(treeOwner, NS_ERROR_FAILURE);
rv = treeOwner->GetPrimaryContentBrowsingContext(
getter_AddRefs(browsingContext));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(browsingContext, NS_ERROR_FAILURE);
return NS_OK;
}();
nsresult rv;
nsCOMPtr<nsIWindowWatcher> wwatch =
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->Reject(rv, __func__);
return rv;
}
// Retrieve the browsing context by using the GeckoSession ID. The
// window is named the same as the ID of the GeckoSession it is
// associated with.
RefPtr<BrowsingContext> browsingContext =
static_cast<nsWindowWatcher*>(wwatch.get())
->GetBrowsingContextByName(sessionId, false, nullptr);
if (NS_WARN_IF(!browsingContext)) {
promise->Reject(NS_ERROR_FAILURE, __func__);
return NS_ERROR_FAILURE;
}
WaitForLoad(aArgsValidated, browsingContext, promise);
return NS_OK;
},

View File

@ -41,7 +41,6 @@
#include "mozilla/dom/JSActorService.h"
#include "nsIHttpChannelInternal.h"
#include "nsIURIMutator.h"
#include "nsURLHelper.h"
using namespace mozilla::ipc;
using namespace mozilla::dom::ipc;
@ -642,169 +641,6 @@ bool WindowGlobalChild::SameOriginWithTop() {
return IsSameOriginWith(WindowContext()->TopWindowContext());
}
// For historical context, see:
//
// Bug 13871: Prevent frameset spoofing
// Bug 103638: Targets with same name in different windows open in wrong
// window with javascript
// Bug 408052: Adopt "ancestor" frame navigation policy
// Bug 1570207: Refactor logic to rely on BrowsingContextGroups to enforce
// origin attribute isolation
// Bug 1810619: Crash at null in nsDocShell::ValidateOrigin
bool WindowGlobalChild::CanNavigate(dom::BrowsingContext* aTarget,
bool aConsiderOpener) {
MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aTarget->Group(),
"A WindowGlobalChild should never try to navigate a "
"BrowsingContext from another group");
auto isFileScheme = [](nsIPrincipal* aPrincipal) -> bool {
// NOTE: This code previously checked for a file scheme using
// `nsIPrincipal::GetURI()` combined with `NS_GetInnermostURI`. We no longer
// use GetURI, as it has been deprecated, and it makes more sense to take
// advantage of the pre-computed origin, which will already use the
// innermost URI (bug 1810619)
nsAutoCString origin, scheme;
return NS_SUCCEEDED(aPrincipal->GetOriginNoSuffix(origin)) &&
NS_SUCCEEDED(net_ExtractURLScheme(origin, scheme)) &&
scheme == "file"_ns;
};
// A frame can navigate itself and its own root.
if (aTarget == BrowsingContext() || aTarget == BrowsingContext()->Top()) {
return true;
}
// If the target frame doesn't yet have a WindowContext, start checking
// principals from its direct ancestor instead. It would inherit its principal
// from this document upon creation.
dom::WindowContext* initialWc = aTarget->GetCurrentWindowContext();
if (!initialWc) {
initialWc = aTarget->GetParentWindowContext();
}
// A frame can navigate any frame with a same-origin ancestor.
bool isFileDocument = isFileScheme(DocumentPrincipal());
for (dom::WindowContext* wc = initialWc; wc;
wc = wc->GetParentWindowContext()) {
dom::WindowGlobalChild* wgc = wc->GetWindowGlobalChild();
if (!wgc) {
continue; // out-of process, so not same-origin.
}
if (DocumentPrincipal()->Equals(wgc->DocumentPrincipal())) {
return true;
}
// Not strictly equal, special case if both are file: URIs.
//
// file: URIs are considered the same domain for the purpose of frame
// navigation, regardless of script accessibility (bug 420425).
if (isFileDocument && isFileScheme(wgc->DocumentPrincipal())) {
return true;
}
}
// If the target is a top-level document, a frame can navigate it if it can
// navigate its opener.
if (aConsiderOpener && !aTarget->GetParent()) {
if (RefPtr<dom::BrowsingContext> opener = aTarget->GetOpener()) {
return CanNavigate(opener, false);
}
}
return false;
}
// FindWithName follows the rules for choosing a browsing context,
// with the exception of sandboxing for iframes. The implementation
// for arbitrarily choosing between two browsing contexts with the
// same name is as follows:
//
// 1) The start browsing context, i.e. 'this'
// 2) Descendants in insertion order
// 3) The parent
// 4) Siblings and their children, both in insertion order
// 5) After this we iteratively follow the parent chain, repeating 3
// and 4 until
// 6) If there is no parent, consider all other top level browsing
// contexts and their children, both in insertion order
//
// See
// https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
dom::BrowsingContext* WindowGlobalChild::FindBrowsingContextWithName(
const nsAString& aName, bool aUseEntryGlobalForAccessCheck) {
RefPtr<WindowGlobalChild> requestingContext = this;
if (aUseEntryGlobalForAccessCheck) {
if (nsGlobalWindowInner* caller = nsContentUtils::EntryInnerWindow()) {
if (caller->GetBrowsingContextGroup() == WindowContext()->Group()) {
requestingContext = caller->GetWindowGlobalChild();
} else {
MOZ_RELEASE_ASSERT(caller->GetPrincipal()->IsSystemPrincipal(),
"caller must be either same-group or system");
}
}
}
MOZ_ASSERT(requestingContext, "must have a requestingContext");
dom::BrowsingContext* found = nullptr;
if (aName.IsEmpty()) {
// You can't find a browsing context with an empty name.
found = nullptr;
} else if (aName.LowerCaseEqualsLiteral("_blank")) {
// Just return null. Caller must handle creating a new window with
// a blank name.
found = nullptr;
} else if (nsContentUtils::IsSpecialName(aName)) {
found = BrowsingContext()->FindWithSpecialName(aName, *requestingContext);
} else if (dom::BrowsingContext* child =
BrowsingContext()->FindWithNameInSubtree(aName,
requestingContext)) {
found = child;
} else {
dom::WindowContext* current = WindowContext();
do {
Span<RefPtr<dom::BrowsingContext>> siblings;
dom::WindowContext* parent = current->GetParentWindowContext();
if (!parent) {
// We've reached the root of the tree, consider browsing
// contexts in the same browsing context group.
siblings = WindowContext()->Group()->Toplevels();
} else if (dom::BrowsingContext* bc = parent->GetBrowsingContext();
bc && bc->NameEquals(aName) &&
requestingContext->CanNavigate(bc) && bc->IsTargetable()) {
found = bc;
break;
} else {
siblings = parent->NonSyntheticChildren();
}
for (dom::BrowsingContext* sibling : siblings) {
if (sibling == current->GetBrowsingContext()) {
continue;
}
if (dom::BrowsingContext* relative =
sibling->FindWithNameInSubtree(aName, requestingContext)) {
found = relative;
// Breaks the outer loop
parent = nullptr;
break;
}
}
current = parent;
} while (current);
}
// Helpers should perform access control checks, which means that we
// only need to assert that we can access found.
MOZ_DIAGNOSTIC_ASSERT(!found || requestingContext->CanNavigate(found));
return found;
}
void WindowGlobalChild::UnblockBFCacheFor(BFCacheStatus aStatus) {
SendUpdateBFCacheStatus(0, aStatus);
}

View File

@ -123,23 +123,6 @@ class WindowGlobalChild final : public WindowGlobalActor,
bool SameOriginWithTop();
// Returns `true` if this WindowGlobal is allowed to navigate the given
// BrowsingContext. BrowsingContexts which are currently out-of-process are
// supported, and assumed to be cross-origin.
//
// The given BrowsingContext must be in the same BrowsingContextGroup as this
// WindowGlobal.
bool CanNavigate(dom::BrowsingContext* aTarget, bool aConsiderOpener = true);
// Using the rules for choosing a browsing context we try to find
// the browsing context with the given name in the set of
// transitively reachable browsing contexts. Performs access control
// checks with regard to this.
// See
// https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name.
dom::BrowsingContext* FindBrowsingContextWithName(
const nsAString& aName, bool aUseEntryGlobalForAccessCheck = true);
nsISupports* GetParentObject();
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;

View File

@ -272,6 +272,7 @@ public final class GeckoRuntime implements Parcelable {
if (!session.isOpen()) {
session.open(sRuntime);
}
session.loadUri(url);
result.complete(session.getId());
} else {
result.complete(null);

View File

@ -193,7 +193,10 @@ class GeckoViewNavigation extends GeckoViewModule {
let triggeringPrincipal, referrerInfo, csp;
if (referrerSessionId) {
const referrerWindow = Services.ww.getWindowByName(referrerSessionId);
const referrerWindow = Services.ww.getWindowByName(
referrerSessionId,
this.window
);
triggeringPrincipal = referrerWindow.browser.contentPrincipal;
csp = referrerWindow.browser.csp;

View File

@ -137,17 +137,18 @@ interface nsIWindowWatcher : nsISupports {
nsIWebBrowserChrome getChromeForWindow(in mozIDOMWindowProxy aWindow);
/**
Retrieve an existing chrome window (or frame).
Retrieve an existing window (or frame).
@param aTargetName the window name
Note: This method will not consider special names like "_blank", "_top",
"_self", or "_parent", as there is no reference window.
@param aCurrentWindow a starting point in the window hierarchy to
begin the search. If null, each toplevel window
will be searched.
Note: This method will search all open windows for any window or
frame with the given window name. Make sure you understand the
security implications of this before using this method!
*/
mozIDOMWindowProxy getWindowByName(in AString aTargetName);
mozIDOMWindowProxy getWindowByName(in AString aTargetName,
in mozIDOMWindowProxy aCurrentWindow);
/**
Retrieves the active window from the focus manager.

View File

@ -71,7 +71,6 @@
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/BrowserHost.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/SessionStorageManager.h"
#include "nsIAppWindow.h"
#include "nsIXULBrowserWindow.h"
@ -697,35 +696,8 @@ nsresult nsWindowWatcher::OpenWindowInternal(
return NS_ERROR_ABORT;
}
// If no parent, consider it chrome when running in the parent process.
bool hasChromeParent = !XRE_IsContentProcess();
if (aParent) {
// Check if the parent document has chrome privileges.
hasChromeParent = parentDoc && nsContentUtils::IsChromeDoc(parentDoc);
}
bool isCallerChrome = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
// try to find an extant browsing context with the given name
if (!name.IsEmpty() &&
(!aForceNoOpener || nsContentUtils::IsSpecialName(name))) {
if (parentInnerWin && parentInnerWin->GetWindowGlobalChild()) {
// If we have a parent window, perform the look-up relative to the parent
// inner window.
targetBC =
parentInnerWin->GetWindowGlobalChild()->FindBrowsingContextWithName(
name);
} else if (hasChromeParent && isCallerChrome &&
!nsContentUtils::IsSpecialName(name)) {
// Otherwise, if this call is from chrome, perform the lookup relative
// to the system group.
nsCOMPtr<mozIDOMWindowProxy> chromeWindow;
MOZ_ALWAYS_SUCCEEDS(GetWindowByName(name, getter_AddRefs(chromeWindow)));
if (chromeWindow) {
targetBC = nsPIDOMWindowOuter::From(chromeWindow)->GetBrowsingContext();
}
}
}
targetBC = GetBrowsingContextByName(name, aForceNoOpener, parentBC);
// Do sandbox checks here, instead of waiting until nsIDocShell::LoadURI.
// The state of the window can change before this call and if we are blocked
@ -742,6 +714,15 @@ nsresult nsWindowWatcher::OpenWindowInternal(
// no extant window? make a new one.
// If no parent, consider it chrome when running in the parent process.
bool hasChromeParent = !XRE_IsContentProcess();
if (aParent) {
// Check if the parent document has chrome privileges.
hasChromeParent = parentDoc && nsContentUtils::IsChromeDoc(parentDoc);
}
bool isCallerChrome = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
CSSToDesktopScale cssToDesktopScale(1.0);
if (nsCOMPtr<nsIBaseWindow> win = do_QueryInterface(parentDocShell)) {
cssToDesktopScale = win->GetUnscaledCSSToDesktopScale();
@ -1713,6 +1694,7 @@ nsWindowWatcher::GetChromeForWindow(mozIDOMWindowProxy* aWindow,
NS_IMETHODIMP
nsWindowWatcher::GetWindowByName(const nsAString& aTargetName,
mozIDOMWindowProxy* aCurrentWindow,
mozIDOMWindowProxy** aResult) {
if (!aResult) {
return NS_ERROR_INVALID_ARG;
@ -1720,22 +1702,17 @@ nsWindowWatcher::GetWindowByName(const nsAString& aTargetName,
*aResult = nullptr;
// We won't be able to find any windows with a special or empty name.
if (aTargetName.IsEmpty() || nsContentUtils::IsSpecialName(aTargetName)) {
return NS_OK;
}
BrowsingContext* currentContext =
aCurrentWindow
? nsPIDOMWindowOuter::From(aCurrentWindow)->GetBrowsingContext()
: nullptr;
// Search each toplevel in the chrome BrowsingContextGroup for a window with
// the given name.
for (const RefPtr<BrowsingContext>& toplevel :
BrowsingContextGroup::GetChromeGroup()->Toplevels()) {
BrowsingContext* context =
toplevel->FindWithNameInSubtree(aTargetName, nullptr);
if (context) {
*aResult = do_AddRef(context->GetDOMWindow()).take();
MOZ_ASSERT(*aResult);
return NS_OK;
}
RefPtr<BrowsingContext> context =
GetBrowsingContextByName(aTargetName, false, currentContext);
if (context) {
*aResult = do_AddRef(context->GetDOMWindow()).take();
MOZ_ASSERT(*aResult);
}
return NS_OK;
@ -2052,6 +2029,36 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForSystem(
return chromeFlags;
}
already_AddRefed<BrowsingContext> nsWindowWatcher::GetBrowsingContextByName(
const nsAString& aName, bool aForceNoOpener,
BrowsingContext* aCurrentContext) {
if (aName.IsEmpty()) {
return nullptr;
}
if (aForceNoOpener && !nsContentUtils::IsSpecialName(aName)) {
// Ignore all other names in the noopener case.
return nullptr;
}
RefPtr<BrowsingContext> foundContext;
if (aCurrentContext) {
foundContext = aCurrentContext->FindWithName(aName);
} else if (!nsContentUtils::IsSpecialName(aName)) {
// If we are looking for an item and we don't have a docshell we are
// checking on, let's just look in the chrome browsing context group!
for (RefPtr<BrowsingContext> toplevel :
BrowsingContextGroup::GetChromeGroup()->Toplevels()) {
foundContext = toplevel->FindWithNameInSubtree(aName, *toplevel);
if (foundContext) {
break;
}
}
}
return foundContext.forget();
}
// public static
bool nsWindowWatcher::HaveSpecifiedSize(const WindowFeatures& features) {
return CalcSizeSpec(features, false, CSSToDesktopScale()).SizeSpecified();

View File

@ -56,6 +56,13 @@ class nsWindowWatcher : public nsIWindowWatcher,
uint32_t aChromeFlags,
bool aCalledFromJS, bool aIsForPrinting);
// Will first look for a caller on the JS stack, and then fall back on
// aCurrentContext if it can't find one.
// It also knows to not look for things if aForceNoOpener is set.
already_AddRefed<mozilla::dom::BrowsingContext> GetBrowsingContextByName(
const nsAString& aName, bool aForceNoOpener,
mozilla::dom::BrowsingContext* aCurrentContext);
static bool HaveSpecifiedSize(const mozilla::dom::WindowFeatures& features);
protected:

View File

@ -381,7 +381,7 @@ const AbuseReporter = {
* @returns {Window?}
*/
getOpenDialog() {
return Services.ww.getWindowByName(DIALOG_WINDOW_NAME);
return Services.ww.getWindowByName(DIALOG_WINDOW_NAME, null);
},
/**

View File

@ -188,7 +188,7 @@ const AbuseReportTestUtils = {
// Returns the currently open abuse report dialog window (if any).
getReportDialog() {
return Services.ww.getWindowByName("addons-abuse-report-dialog");
return Services.ww.getWindowByName("addons-abuse-report-dialog", null);
},
// Returns the parameters related to the report dialog (if any).