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( BrowsingContext* BrowsingContext::FindChildWithName(
const nsAString& aName, WindowGlobalChild& aRequestingWindow) { const nsAString& aName, BrowsingContext& aRequestingContext) {
if (aName.IsEmpty()) { if (aName.IsEmpty()) {
// You can't find a browsing context with the empty name. // You can't find a browsing context with the empty name.
return nullptr; return nullptr;
} }
for (BrowsingContext* child : NonSyntheticChildren()) { for (BrowsingContext* child : NonSyntheticChildren()) {
if (child->NameEquals(aName) && aRequestingWindow.CanNavigate(child) && if (child->NameEquals(aName) && aRequestingContext.CanAccess(child) &&
child->IsTargetable()) { child->IsTargetable()) {
return child; return child;
} }
@ -1212,7 +1301,7 @@ BrowsingContext* BrowsingContext::FindChildWithName(
} }
BrowsingContext* BrowsingContext::FindWithSpecialName( BrowsingContext* BrowsingContext::FindWithSpecialName(
const nsAString& aName, WindowGlobalChild& aRequestingWindow) { const nsAString& aName, BrowsingContext& aRequestingContext) {
// TODO(farre): Neither BrowsingContext nor nsDocShell checks if the // TODO(farre): Neither BrowsingContext nor nsDocShell checks if the
// browsing context pointed to by a special name is active. Should // browsing context pointed to by a special name is active. Should
// it be? See Bug 1527913. // it be? See Bug 1527913.
@ -1222,7 +1311,7 @@ BrowsingContext* BrowsingContext::FindWithSpecialName(
if (aName.LowerCaseEqualsLiteral("_parent")) { if (aName.LowerCaseEqualsLiteral("_parent")) {
if (BrowsingContext* parent = GetParent()) { if (BrowsingContext* parent = GetParent()) {
return aRequestingWindow.CanNavigate(parent) ? parent : nullptr; return aRequestingContext.CanAccess(parent) ? parent : nullptr;
} }
return this; return this;
} }
@ -1230,25 +1319,24 @@ BrowsingContext* BrowsingContext::FindWithSpecialName(
if (aName.LowerCaseEqualsLiteral("_top")) { if (aName.LowerCaseEqualsLiteral("_top")) {
BrowsingContext* top = Top(); BrowsingContext* top = Top();
return aRequestingWindow.CanNavigate(top) ? top : nullptr; return aRequestingContext.CanAccess(top) ? top : nullptr;
} }
return nullptr; return nullptr;
} }
BrowsingContext* BrowsingContext::FindWithNameInSubtree( BrowsingContext* BrowsingContext::FindWithNameInSubtree(
const nsAString& aName, WindowGlobalChild* aRequestingWindow) { const nsAString& aName, BrowsingContext& aRequestingContext) {
MOZ_DIAGNOSTIC_ASSERT(!aName.IsEmpty()); MOZ_DIAGNOSTIC_ASSERT(!aName.IsEmpty());
if (NameEquals(aName) && if (NameEquals(aName) && aRequestingContext.CanAccess(this) &&
(!aRequestingWindow || aRequestingWindow->CanNavigate(this)) &&
IsTargetable()) { IsTargetable()) {
return this; return this;
} }
for (BrowsingContext* child : NonSyntheticChildren()) { for (BrowsingContext* child : NonSyntheticChildren()) {
if (BrowsingContext* found = if (BrowsingContext* found =
child->FindWithNameInSubtree(aName, aRequestingWindow)) { child->FindWithNameInSubtree(aName, aRequestingContext)) {
return found; return found;
} }
} }
@ -1256,6 +1344,48 @@ BrowsingContext* BrowsingContext::FindWithNameInSubtree(
return nullptr; 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) { bool BrowsingContext::IsSandboxedFrom(BrowsingContext* aTarget) {
// If no target then not sandboxed. // If no target then not sandboxed.
if (!aTarget) { if (!aTarget) {
@ -1940,12 +2070,13 @@ nsresult BrowsingContext::LoadURI(nsDocShellLoadState* aLoadState,
MOZ_DIAGNOSTIC_ASSERT(!sourceBC || sourceBC->Group() == Group()); MOZ_DIAGNOSTIC_ASSERT(!sourceBC || sourceBC->Group() == Group());
if (sourceBC && sourceBC->IsInProcess()) { if (sourceBC && sourceBC->IsInProcess()) {
if (!sourceBC->CanAccess(this)) {
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
}
nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow()); nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
if (WindowGlobalChild* wgc = if (WindowGlobalChild* wgc =
win->GetCurrentInnerWindow()->GetWindowGlobalChild()) { win->GetCurrentInnerWindow()->GetWindowGlobalChild()) {
if (!wgc->CanNavigate(this)) {
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
}
wgc->SendLoadURI(this, aLoadState, aSetNavigating); wgc->SendLoadURI(this, aLoadState, aSetNavigating);
} }
} else if (XRE_IsParentProcess()) { } else if (XRE_IsParentProcess()) {
@ -2044,15 +2175,16 @@ nsresult BrowsingContext::InternalLoad(nsDocShellLoadState* aLoadState) {
MOZ_DIAGNOSTIC_ASSERT(sourceBC); MOZ_DIAGNOSTIC_ASSERT(sourceBC);
MOZ_DIAGNOSTIC_ASSERT(sourceBC->Group() == Group()); MOZ_DIAGNOSTIC_ASSERT(sourceBC->Group() == Group());
if (!sourceBC->CanAccess(this)) {
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
}
nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow()); nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
WindowGlobalChild* wgc = WindowGlobalChild* wgc =
win->GetCurrentInnerWindow()->GetWindowGlobalChild(); win->GetCurrentInnerWindow()->GetWindowGlobalChild();
if (!wgc || !wgc->CanSend()) { if (!wgc || !wgc->CanSend()) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
if (!wgc->CanNavigate(this)) {
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
}
MOZ_ALWAYS_SUCCEEDS( MOZ_ALWAYS_SUCCEEDS(
SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier()))); SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier())));

View File

@ -75,7 +75,6 @@ class SessionHistoryInfo;
class SessionStorageManager; class SessionStorageManager;
class StructuredCloneHolder; class StructuredCloneHolder;
class WindowContext; class WindowContext;
class WindowGlobalChild;
struct WindowPostMessageOptions; struct WindowPostMessageOptions;
class WindowProxyHolder; 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 // Find a browsing context in this context's list of
// children. Doesn't consider the special names, '_self', '_parent', // children. Doesn't consider the special names, '_self', '_parent',
// '_top', or '_blank'. Performs access control checks with regard to // '_top', or '_blank'. Performs access control checks with regard to
// 'this'. // 'this'.
BrowsingContext* FindChildWithName(const nsAString& aName, BrowsingContext* FindChildWithName(const nsAString& aName,
WindowGlobalChild& aRequestingWindow); BrowsingContext& aRequestingContext);
// Find a browsing context in the subtree rooted at 'this' Doesn't // Find a browsing context in the subtree rooted at 'this' Doesn't
// consider the special names, '_self', '_parent', '_top', or // consider the special names, '_self', '_parent', '_top', or
// '_blank'. // '_blank'. Performs access control checks with regard to
// // 'aRequestingContext'.
// If passed, performs access control checks with regard to
// 'aRequestingContext', otherwise performs no access checks.
BrowsingContext* FindWithNameInSubtree(const nsAString& aName, BrowsingContext* FindWithNameInSubtree(const nsAString& aName,
WindowGlobalChild* aRequestingWindow); BrowsingContext& aRequestingContext);
// 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);
nsISupports* GetParentObject() const; nsISupports* GetParentObject() const;
JSObject* WrapObject(JSContext* aCx, JSObject* WrapObject(JSContext* aCx,
@ -786,6 +791,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
BrowsingContextGroup* aGroup, BrowsingContextGroup* aGroup,
ContentParent* aOriginProcess); ContentParent* aOriginProcess);
// Performs access control to check that 'this' can access 'aTarget'.
bool CanAccess(BrowsingContext* aTarget, bool aConsiderOpener = true);
bool IsSandboxedFrom(BrowsingContext* aTarget); bool IsSandboxedFrom(BrowsingContext* aTarget);
// The runnable will be called once there is idle time, or the top level // 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. // parent WC changes.
void RecomputeCanExecuteScripts(); 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 // Is it early enough in the BrowsingContext's lifecycle that it is still
// OK to set OriginAttributes? // OK to set OriginAttributes?
bool CanSetOriginAttributes(); bool CanSetOriginAttributes();

View File

@ -1438,6 +1438,61 @@ nsDOMNavigationTiming* nsDocShell::GetNavigationTiming() const {
return mTiming; 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() { nsPresContext* nsDocShell::GetEldestPresContext() {
nsIContentViewer* viewer = mContentViewer; nsIContentViewer* viewer = mContentViewer;
while (viewer) { while (viewer) {
@ -8444,11 +8499,7 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
aLoadState->Target().LowerCaseEqualsLiteral("_self") || aLoadState->Target().LowerCaseEqualsLiteral("_self") ||
aLoadState->Target().LowerCaseEqualsLiteral("_parent") || aLoadState->Target().LowerCaseEqualsLiteral("_parent") ||
aLoadState->Target().LowerCaseEqualsLiteral("_top")) { aLoadState->Target().LowerCaseEqualsLiteral("_top")) {
Document* document = GetDocument(); targetContext = mBrowsingContext->FindWithName(
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
WindowGlobalChild* wgc = document->GetWindowGlobalChild();
NS_ENSURE_TRUE(wgc, NS_ERROR_FAILURE);
targetContext = wgc->FindBrowsingContextWithName(
aLoadState->Target(), /* aUseEntryGlobalForAccessCheck */ false); aLoadState->Target(), /* aUseEntryGlobalForAccessCheck */ false);
} }

View File

@ -558,6 +558,11 @@ class nsDocShell final : public nsDocLoader,
nsDocShell(mozilla::dom::BrowsingContext* aBrowsingContext, nsDocShell(mozilla::dom::BrowsingContext* aBrowsingContext,
uint64_t aContentWindowID); 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) { static inline uint32_t PRTimeToSeconds(PRTime aTimeUsec) {
return uint32_t(aTimeUsec / PR_USEC_PER_SEC); 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 // wish to confirm that targeting is able to find
// appropriate browsing contexts. // appropriate browsing contexts.
// WindowGlobalChild.findBrowsingContextWithName requires access // BrowsingContext.findWithName requires access checks, which
// checks, which can only be performed in the process of the accessor // can only be performed in the process of the accessor BC's
// WindowGlobalChild. // docShell.
function findWithName(bc, name) { function findWithName(bc, name) {
return content.SpecialPowers.spawn(bc, [name], name => { return content.SpecialPowers.spawn(bc, [bc, name], (bc, name) => {
return content.windowGlobalChild.findBrowsingContextWithName( return bc.findWithName(name);
name
);
}); });
} }

View File

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

View File

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

View File

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

View File

@ -17,7 +17,6 @@
#include "nsFocusManager.h" #include "nsFocusManager.h"
#include "nsIBrowserDOMWindow.h" #include "nsIBrowserDOMWindow.h"
#include "nsIDocShell.h" #include "nsIDocShell.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIDOMChromeWindow.h" #include "nsIDOMChromeWindow.h"
#include "nsIURI.h" #include "nsIURI.h"
#include "nsIBrowser.h" #include "nsIBrowser.h"
@ -328,33 +327,25 @@ void GeckoViewOpenWindow(const ClientOpenWindowArgsParsed& aArgsValidated,
promiseResult->Then( promiseResult->Then(
GetMainThreadSerialEventTarget(), __func__, GetMainThreadSerialEventTarget(), __func__,
[aArgsValidated, promise](nsString sessionId) { [aArgsValidated, promise](nsString sessionId) {
// Retrieve the primary content BrowsingContext using the GeckoSession nsresult rv;
// ID. The chrome window is named the same as the ID of the GeckoSession nsCOMPtr<nsIWindowWatcher> wwatch =
// it is associated with. do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
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;
}();
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
promise->Reject(rv, __func__); promise->Reject(rv, __func__);
return rv; 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); WaitForLoad(aArgsValidated, browsingContext, promise);
return NS_OK; return NS_OK;
}, },

View File

@ -41,7 +41,6 @@
#include "mozilla/dom/JSActorService.h" #include "mozilla/dom/JSActorService.h"
#include "nsIHttpChannelInternal.h" #include "nsIHttpChannelInternal.h"
#include "nsIURIMutator.h" #include "nsIURIMutator.h"
#include "nsURLHelper.h"
using namespace mozilla::ipc; using namespace mozilla::ipc;
using namespace mozilla::dom::ipc; using namespace mozilla::dom::ipc;
@ -642,169 +641,6 @@ bool WindowGlobalChild::SameOriginWithTop() {
return IsSameOriginWith(WindowContext()->TopWindowContext()); 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) { void WindowGlobalChild::UnblockBFCacheFor(BFCacheStatus aStatus) {
SendUpdateBFCacheStatus(0, aStatus); SendUpdateBFCacheStatus(0, aStatus);
} }

View File

@ -123,23 +123,6 @@ class WindowGlobalChild final : public WindowGlobalActor,
bool SameOriginWithTop(); 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(); nsISupports* GetParentObject();
JSObject* WrapObject(JSContext* aCx, JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override; JS::Handle<JSObject*> aGivenProto) override;

View File

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

View File

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

View File

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

View File

@ -71,7 +71,6 @@
#include "mozilla/dom/BrowserParent.h" #include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/BrowserHost.h" #include "mozilla/dom/BrowserHost.h"
#include "mozilla/dom/DocGroup.h" #include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/SessionStorageManager.h" #include "mozilla/dom/SessionStorageManager.h"
#include "nsIAppWindow.h" #include "nsIAppWindow.h"
#include "nsIXULBrowserWindow.h" #include "nsIXULBrowserWindow.h"
@ -697,35 +696,8 @@ nsresult nsWindowWatcher::OpenWindowInternal(
return NS_ERROR_ABORT; 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 // try to find an extant browsing context with the given name
if (!name.IsEmpty() && targetBC = GetBrowsingContextByName(name, aForceNoOpener, parentBC);
(!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();
}
}
}
// Do sandbox checks here, instead of waiting until nsIDocShell::LoadURI. // 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 // 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. // 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); CSSToDesktopScale cssToDesktopScale(1.0);
if (nsCOMPtr<nsIBaseWindow> win = do_QueryInterface(parentDocShell)) { if (nsCOMPtr<nsIBaseWindow> win = do_QueryInterface(parentDocShell)) {
cssToDesktopScale = win->GetUnscaledCSSToDesktopScale(); cssToDesktopScale = win->GetUnscaledCSSToDesktopScale();
@ -1713,6 +1694,7 @@ nsWindowWatcher::GetChromeForWindow(mozIDOMWindowProxy* aWindow,
NS_IMETHODIMP NS_IMETHODIMP
nsWindowWatcher::GetWindowByName(const nsAString& aTargetName, nsWindowWatcher::GetWindowByName(const nsAString& aTargetName,
mozIDOMWindowProxy* aCurrentWindow,
mozIDOMWindowProxy** aResult) { mozIDOMWindowProxy** aResult) {
if (!aResult) { if (!aResult) {
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
@ -1720,22 +1702,17 @@ nsWindowWatcher::GetWindowByName(const nsAString& aTargetName,
*aResult = nullptr; *aResult = nullptr;
// We won't be able to find any windows with a special or empty name. BrowsingContext* currentContext =
if (aTargetName.IsEmpty() || nsContentUtils::IsSpecialName(aTargetName)) { aCurrentWindow
return NS_OK; ? nsPIDOMWindowOuter::From(aCurrentWindow)->GetBrowsingContext()
} : nullptr;
// Search each toplevel in the chrome BrowsingContextGroup for a window with RefPtr<BrowsingContext> context =
// the given name. GetBrowsingContextByName(aTargetName, false, currentContext);
for (const RefPtr<BrowsingContext>& toplevel :
BrowsingContextGroup::GetChromeGroup()->Toplevels()) { if (context) {
BrowsingContext* context = *aResult = do_AddRef(context->GetDOMWindow()).take();
toplevel->FindWithNameInSubtree(aTargetName, nullptr); MOZ_ASSERT(*aResult);
if (context) {
*aResult = do_AddRef(context->GetDOMWindow()).take();
MOZ_ASSERT(*aResult);
return NS_OK;
}
} }
return NS_OK; return NS_OK;
@ -2052,6 +2029,36 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForSystem(
return chromeFlags; 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 // public static
bool nsWindowWatcher::HaveSpecifiedSize(const WindowFeatures& features) { bool nsWindowWatcher::HaveSpecifiedSize(const WindowFeatures& features) {
return CalcSizeSpec(features, false, CSSToDesktopScale()).SizeSpecified(); return CalcSizeSpec(features, false, CSSToDesktopScale()).SizeSpecified();

View File

@ -56,6 +56,13 @@ class nsWindowWatcher : public nsIWindowWatcher,
uint32_t aChromeFlags, uint32_t aChromeFlags,
bool aCalledFromJS, bool aIsForPrinting); 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); static bool HaveSpecifiedSize(const mozilla::dom::WindowFeatures& features);
protected: protected:

View File

@ -381,7 +381,7 @@ const AbuseReporter = {
* @returns {Window?} * @returns {Window?}
*/ */
getOpenDialog() { 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). // Returns the currently open abuse report dialog window (if any).
getReportDialog() { 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). // Returns the parameters related to the report dialog (if any).