Bug 1576407: Part 3 - Return window opener as a WindowProxyHolder or BrowsingContext. r=farre

Also adds a legacy `GetSameProcessOpener()` method for callers which can only
deal with in-process windows and may need to be updated for Fission.

Differential Revision: https://phabricator.services.mozilla.com/D43692

--HG--
extra : source : 0ddbc451dcab431c2a1d934fa9baa5e8efc71545
This commit is contained in:
Kris Maglione 2019-08-28 18:07:38 +00:00
parent fcd9f00395
commit 99bc492498
8 changed files with 62 additions and 75 deletions

View File

@ -16,6 +16,7 @@
#include "nsError.h" #include "nsError.h"
#include "nsContentSecurityManager.h" #include "nsContentSecurityManager.h"
#include "nsDocShellLoadTypes.h" #include "nsDocShellLoadTypes.h"
#include "nsGlobalWindowOuter.h"
#include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestor.h"
#include "nsIMultiPartChannel.h" #include "nsIMultiPartChannel.h"
@ -48,10 +49,11 @@ nsIInterfaceRequestor* MaybeCloseWindowHelper::MaybeCloseWindow() {
if (mShouldCloseWindow) { if (mShouldCloseWindow) {
// Reset the window context to the opener window so that the dependent // Reset the window context to the opener window so that the dependent
// dialogs have a parent // dialogs have a parent
nsCOMPtr<nsPIDOMWindowOuter> opener = window->GetOpener(); nsCOMPtr<nsPIDOMWindowOuter> opener =
nsGlobalWindowOuter::Cast(window)->GetSameProcessOpener();
if (opener && !opener->Closed()) { if (opener && !opener->Closed()) {
mContentContext = do_GetInterface(opener); mContentContext = do_QueryInterface(opener);
// Now close the old window. Do it on a timer so that we don't run // Now close the old window. Do it on a timer so that we don't run
// into issues trying to close the window before it has fully opened. // into issues trying to close the window before it has fully opened.

View File

@ -14863,12 +14863,12 @@ void Document::MaybeAllowStorageForOpenerAfterUserInteraction() {
return; return;
} }
nsCOMPtr<nsPIDOMWindowOuter> outer = inner->GetOuterWindow(); auto* outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
if (NS_WARN_IF(!outer)) { if (NS_WARN_IF(!outer)) {
return; return;
} }
nsCOMPtr<nsPIDOMWindowOuter> outerOpener = outer->GetOpener(); nsCOMPtr<nsPIDOMWindowOuter> outerOpener = outer->GetSameProcessOpener();
if (!outerOpener) { if (!outerOpener) {
return; return;
} }

View File

@ -3034,20 +3034,23 @@ nsresult nsGlobalWindowInner::GetControllers(nsIControllers** aResult) {
return rv.StealNSResult(); return rv.StealNSResult();
} }
nsPIDOMWindowOuter* nsGlobalWindowInner::GetOpenerWindow(ErrorResult& aError) { Nullable<WindowProxyHolder> nsGlobalWindowInner::GetOpenerWindow(
ErrorResult& aError) {
FORWARD_TO_OUTER_OR_THROW(GetOpenerWindowOuter, (), aError, nullptr); FORWARD_TO_OUTER_OR_THROW(GetOpenerWindowOuter, (), aError, nullptr);
} }
void nsGlobalWindowInner::GetOpener(JSContext* aCx, void nsGlobalWindowInner::GetOpener(JSContext* aCx,
JS::MutableHandle<JS::Value> aRetval, JS::MutableHandle<JS::Value> aRetval,
ErrorResult& aError) { ErrorResult& aError) {
nsCOMPtr<nsPIDOMWindowOuter> opener = GetOpenerWindow(aError); Nullable<WindowProxyHolder> opener = GetOpenerWindow(aError);
if (aError.Failed() || !opener) { if (aError.Failed() || opener.IsNull()) {
aRetval.setNull(); aRetval.setNull();
return; return;
} }
aError = nsContentUtils::WrapNative(aCx, opener, aRetval); if (!ToJSValue(aCx, opener.Value(), aRetval)) {
aError.NoteJSContextException(aCx);
}
} }
void nsGlobalWindowInner::SetOpener(JSContext* aCx, void nsGlobalWindowInner::SetOpener(JSContext* aCx,

View File

@ -622,7 +622,8 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
void InitWasOffline(); void InitWasOffline();
public: public:
nsPIDOMWindowOuter* GetOpenerWindow(mozilla::ErrorResult& aError); mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> GetOpenerWindow(
mozilla::ErrorResult& aError);
void GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval, void GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
mozilla::ErrorResult& aError); mozilla::ErrorResult& aError);
void SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener, void SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,

View File

@ -3290,63 +3290,44 @@ nsresult nsGlobalWindowOuter::GetControllers(nsIControllers** aResult) {
FORWARD_TO_INNER(GetControllers, (aResult), NS_ERROR_UNEXPECTED); FORWARD_TO_INNER(GetControllers, (aResult), NS_ERROR_UNEXPECTED);
} }
nsPIDOMWindowOuter* nsGlobalWindowOuter::GetSanitizedOpener( already_AddRefed<BrowsingContext>
nsPIDOMWindowOuter* aOpener) { nsGlobalWindowOuter::GetOpenerBrowsingContext() {
if (!aOpener) { RefPtr<BrowsingContext> opener = GetBrowsingContext()->GetOpener();
MOZ_DIAGNOSTIC_ASSERT(!opener ||
opener->Group() == GetBrowsingContext()->Group());
if (!opener || opener->Group() != GetBrowsingContext()->Group()) {
return nullptr; return nullptr;
} }
nsGlobalWindowOuter* win = nsGlobalWindowOuter::Cast(aOpener); // Catch the case where we're chrome but the opener is not...
if (nsContentUtils::LegacyIsCallerChromeOrNativeCode() &&
// First, ensure that we're not handing back a chrome window to content: GetPrincipal() == nsContentUtils::GetSystemPrincipal()) {
if (win->IsChromeWindow()) { auto* openerWin = nsGlobalWindowOuter::Cast(opener->GetDOMWindow());
return nullptr; if (!openerWin ||
} openerWin->GetPrincipal() != nsContentUtils::GetSystemPrincipal()) {
return nullptr;
// We don't want to reveal the opener if the opener is a mail window,
// because opener can be used to spoof the contents of a message (bug 105050).
// So, we look in the opener's root docshell to see if it's a mail window.
nsCOMPtr<nsIDocShell> openerDocShell = aOpener->GetDocShell();
if (openerDocShell) {
nsCOMPtr<nsIDocShellTreeItem> openerRootItem;
openerDocShell->GetInProcessRootTreeItem(getter_AddRefs(openerRootItem));
nsCOMPtr<nsIDocShell> openerRootDocShell(do_QueryInterface(openerRootItem));
if (openerRootDocShell) {
nsIDocShell::AppType appType = openerRootDocShell->GetAppType();
if (appType != nsIDocShell::APP_TYPE_MAIL) {
return aOpener;
}
} }
} }
return opener.forget();
}
nsPIDOMWindowOuter* nsGlobalWindowOuter::GetSameProcessOpener() {
if (RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext()) {
return opener->GetDOMWindow();
}
return nullptr; return nullptr;
} }
nsPIDOMWindowOuter* nsGlobalWindowOuter::GetOpenerWindowOuter() { Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetOpenerWindowOuter() {
nsCOMPtr<nsPIDOMWindowOuter> opener = do_QueryReferent(mOpener); if (RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext()) {
return WindowProxyHolder(opener.forget());
if (!opener) {
return nullptr;
} }
return nullptr;
// First, check if we were called from a privileged chrome script
if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
// Catch the case where we're chrome but the opener is not...
if (GetPrincipal() == nsContentUtils::GetSystemPrincipal() &&
nsGlobalWindowOuter::Cast(opener)->GetPrincipal() !=
nsContentUtils::GetSystemPrincipal()) {
return nullptr;
}
return opener;
}
return GetSanitizedOpener(opener);
} }
already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetOpener() { Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetOpener() {
nsCOMPtr<nsPIDOMWindowOuter> opener = GetOpenerWindowOuter(); return GetOpenerWindowOuter();
return opener.forget();
} }
void nsGlobalWindowOuter::GetStatusOuter(nsAString& aStatus) { void nsGlobalWindowOuter::GetStatusOuter(nsAString& aStatus) {
@ -4962,13 +4943,15 @@ void nsGlobalWindowOuter::FocusOuter() {
nsCOMPtr<nsPIDOMWindowInner> caller = do_QueryInterface(GetEntryGlobal()); nsCOMPtr<nsPIDOMWindowInner> caller = do_QueryInterface(GetEntryGlobal());
nsPIDOMWindowOuter* callerOuter = caller ? caller->GetOuterWindow() : nullptr; nsPIDOMWindowOuter* callerOuter = caller ? caller->GetOuterWindow() : nullptr;
nsCOMPtr<nsPIDOMWindowOuter> opener = GetOpener(); BrowsingContext* callerBC =
callerOuter ? callerOuter->GetBrowsingContext() : nullptr;
RefPtr<BrowsingContext> openerBC = GetOpenerBrowsingContext();
// Enforce dom.disable_window_flip (for non-chrome), but still allow the // Enforce dom.disable_window_flip (for non-chrome), but still allow the
// window which opened us to raise us at times when popups are allowed // window which opened us to raise us at times when popups are allowed
// (bugs 355482 and 369306). // (bugs 355482 and 369306).
bool canFocus = CanSetProperty("dom.disable_window_flip") || bool canFocus = CanSetProperty("dom.disable_window_flip") ||
(opener == callerOuter && (openerBC == callerBC &&
RevisePopupAbuseLevel(PopupBlocker::GetPopupControlState()) < RevisePopupAbuseLevel(PopupBlocker::GetPopupControlState()) <
PopupBlocker::openBlocked); PopupBlocker::openBlocked);
@ -7771,11 +7754,11 @@ mozilla::dom::TabGroup* nsGlobalWindowOuter::TabGroupOuter() {
// because a document is getting its NodePrincipal, and asking for the // because a document is getting its NodePrincipal, and asking for the
// TabGroup to determine its DocGroup. // TabGroup to determine its DocGroup.
if (!mTabGroup) { if (!mTabGroup) {
// Get mOpener ourselves, instead of relying on GetOpenerWindowOuter, // Get the opener ourselves, instead of relying on GetOpenerWindowOuter,
// because that way we dodge the LegacyIsCallerChromeOrNativeCode() call // because that way we dodge the LegacyIsCallerChromeOrNativeCode() call
// which we want to return false. // which we want to return false.
nsCOMPtr<nsPIDOMWindowOuter> piOpener = do_QueryReferent(mOpener); RefPtr<BrowsingContext> openerBC = GetBrowsingContext()->GetOpener();
nsPIDOMWindowOuter* opener = GetSanitizedOpener(piOpener); nsPIDOMWindowOuter* opener = openerBC ? openerBC->GetDOMWindow() : nullptr;
nsPIDOMWindowOuter* parent = GetInProcessScriptableParentOrNull(); nsPIDOMWindowOuter* parent = GetInProcessScriptableParentOrNull();
MOZ_ASSERT(!parent || !opener, MOZ_ASSERT(!parent || !opener,
"Only one of parent and opener may be provided"); "Only one of parent and opener may be provided");
@ -7813,9 +7796,11 @@ mozilla::dom::TabGroup* nsGlobalWindowOuter::TabGroupOuter() {
// Sanity check that our tabgroup matches our opener or parent. // Sanity check that our tabgroup matches our opener or parent.
RefPtr<nsPIDOMWindowOuter> parent = GetInProcessScriptableParentOrNull(); RefPtr<nsPIDOMWindowOuter> parent = GetInProcessScriptableParentOrNull();
MOZ_ASSERT_IF(parent, parent->TabGroup() == mTabGroup); MOZ_ASSERT_IF(parent, parent->TabGroup() == mTabGroup);
nsCOMPtr<nsPIDOMWindowOuter> piOpener = do_QueryReferent(mOpener);
nsPIDOMWindowOuter* opener = GetSanitizedOpener(piOpener); RefPtr<BrowsingContext> openerBC = GetBrowsingContext()->GetOpener();
MOZ_ASSERT_IF(opener && nsGlobalWindowOuter::Cast(opener) != this, nsPIDOMWindowOuter* opener =
openerBC ? openerBC->GetDOMWindow() : nullptr;
MOZ_ASSERT_IF(opener && Cast(opener) != this,
opener->TabGroup() == mTabGroup); opener->TabGroup() == mTabGroup);
} }
mIsValidatingTabGroup = false; mIsValidatingTabGroup = false;

View File

@ -550,14 +550,15 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget,
RefPtr<mozilla::ThrottledEventQueue> mPostMessageEventQueue; RefPtr<mozilla::ThrottledEventQueue> mPostMessageEventQueue;
protected: protected:
nsPIDOMWindowOuter* GetOpenerWindowOuter(); mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder>
GetOpenerWindowOuter();
// Initializes the mWasOffline member variable // Initializes the mWasOffline member variable
void InitWasOffline(); void InitWasOffline();
public: public:
nsPIDOMWindowOuter* GetSanitizedOpener(nsPIDOMWindowOuter* aOpener); nsPIDOMWindowOuter* GetSameProcessOpener();
already_AddRefed<mozilla::dom::BrowsingContext> GetOpenerBrowsingContext();
already_AddRefed<nsPIDOMWindowOuter> GetOpener() override; mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> GetOpener() override;
mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> GetParentOuter(); mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> GetParentOuter();
already_AddRefed<nsPIDOMWindowOuter> GetInProcessParent() override; already_AddRefed<nsPIDOMWindowOuter> GetInProcessParent() override;
nsPIDOMWindowOuter* GetInProcessScriptableParent() override; nsPIDOMWindowOuter* GetInProcessScriptableParent() override;

View File

@ -1047,7 +1047,8 @@ class nsPIDOMWindowOuter : public mozIDOMWindowProxy {
virtual nsresult GetPrompter(nsIPrompt** aPrompt) = 0; virtual nsresult GetPrompter(nsIPrompt** aPrompt) = 0;
virtual nsresult GetControllers(nsIControllers** aControllers) = 0; virtual nsresult GetControllers(nsIControllers** aControllers) = 0;
virtual already_AddRefed<mozilla::dom::Selection> GetSelection() = 0; virtual already_AddRefed<mozilla::dom::Selection> GetSelection() = 0;
virtual already_AddRefed<nsPIDOMWindowOuter> GetOpener() = 0; virtual mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder>
GetOpener() = 0;
// aLoadState will be passed on through to the windowwatcher. // aLoadState will be passed on through to the windowwatcher.
// aForceNoOpener will act just like a "noopener" feature in aOptions except // aForceNoOpener will act just like a "noopener" feature in aOptions except

View File

@ -2701,14 +2701,8 @@ nsresult WebSocketImpl::GetLoadingPrincipal(nsIPrincipal** aPrincipal) {
// We are at the top. Let's see if we have an opener window. // We are at the top. Let's see if we have an opener window.
if (innerWindow == currentInnerWindow) { if (innerWindow == currentInnerWindow) {
ErrorResult error; parentWindow = nsGlobalWindowOuter::Cast(innerWindow->GetOuterWindow())
parentWindow = ->GetSameProcessOpener();
nsGlobalWindowInner::Cast(innerWindow)->GetOpenerWindow(error);
if (NS_WARN_IF(error.Failed())) {
error.SuppressException();
return NS_ERROR_DOM_SECURITY_ERR;
}
if (!parentWindow) { if (!parentWindow) {
break; break;
} }