#522: basic window.open=noopener, refactor popup blocker M1267338 M1267339

This commit is contained in:
Cameron Kaiser
2018-09-19 19:01:21 -07:00
parent 3660eb7e23
commit 46bf256a1f
7 changed files with 132 additions and 58 deletions
@@ -56,6 +56,12 @@ interface nsPIWindowWatcher : nsISupports
nsITabParent is a remote tab belonging to aParent. Can
be nullptr if this window is not being opened from a tab.
@param aArgs Window argument
@param aIsPopupSpam true if the window is a popup spam window; used for
popup blocker internals.
@param aForceNoOpener If true, force noopener behavior. This means not
looking for existing windows with the given name,
not setting an opener on the newly opened window,
and returning null from this method.
@return the new window
@note This method may examine the JS context stack for purposes of
@@ -70,7 +76,9 @@ interface nsPIWindowWatcher : nsISupports
in string aName, in string aFeatures,
in boolean aCalledFromScript, in boolean aDialog,
in boolean aNavigate, in nsITabParent aOpeningTab,
in nsISupports aArgs);
in nsISupports aArgs,
in boolean aIsPopupSpam,
in boolean aForceNoOpener);
/**
* Find a named docshell tree item amongst all windows registered
@@ -18,6 +18,7 @@
#include "nsJSUtils.h"
#include "plstr.h"
#include "nsGlobalWindow.h"
#include "nsIBaseWindow.h"
#include "nsIBrowserDOMWindow.h"
#include "nsIDocShell.h"
@@ -366,7 +367,10 @@ nsWindowWatcher::OpenWindow(nsIDOMWindow* aParent,
return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
/* calledFromJS = */ false, dialog,
/* navigate = */ true, nullptr, argv, aResult);
/* navigate = */ true, nullptr, argv,
/* aIsPopupSpam = */ false,
/* aForceNoOpener = */ false,
aResult);
}
struct SizeSpec
@@ -424,6 +428,8 @@ nsWindowWatcher::OpenWindow2(nsIDOMWindow* aParent,
bool aNavigate,
nsITabParent* aOpeningTab,
nsISupports* aArguments,
bool aIsPopupSpam,
bool aForceNoOpener,
nsIDOMWindow** aResult)
{
nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
@@ -443,7 +449,10 @@ nsWindowWatcher::OpenWindow2(nsIDOMWindow* aParent,
return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
aCalledFromScript, dialog,
aNavigate, aOpeningTab, argv, aResult);
aNavigate, aOpeningTab, argv,
aIsPopupSpam,
aForceNoOpener,
aResult);
}
nsresult
@@ -456,6 +465,8 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
bool aNavigate,
nsITabParent* aOpeningTab,
nsIArray* aArgv,
bool aIsPopupSpam,
bool aForceNoOpener,
nsIDOMWindow** aResult)
{
nsresult rv = NS_OK;
@@ -531,7 +542,8 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
// know or care about names - unless we're opening named windows from chrome.
if (!aOpeningTab) {
// try to find an extant window with the given name
nsCOMPtr<nsIDOMWindow> foundWindow = SafeGetWindowByName(name, aParent);
nsCOMPtr<nsIDOMWindow> foundWindow =
SafeGetWindowByName(name, aForceNoOpener, aParent);
GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem));
}
@@ -828,7 +840,8 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
}
}
rv = ReadyOpenedDocShellItem(newDocShellItem, aParent, windowIsNew, aResult);
rv = ReadyOpenedDocShellItem(newDocShellItem, aParent, windowIsNew,
aForceNoOpener, aResult);
if (NS_FAILED(rv)) {
return rv;
}
@@ -900,6 +913,16 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
// SetInitialPrincipalToSubject is safe to call multiple times.
if (newWindow) {
newWindow->SetInitialPrincipalToSubject();
if (aIsPopupSpam) {
nsGlobalWindow* globalWin = static_cast<nsGlobalWindow*>(newWindow.get());
MOZ_ASSERT(!globalWin->IsPopupSpamWindow(),
"Who marked it as popup spam already???");
if (!globalWin->IsPopupSpamWindow()) { // Make sure we don't mess up our
// counter even if the above
// assert fails.
globalWin->SetIsPopupSpamWindow(true);
}
}
}
}
@@ -1069,6 +1092,10 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
}
}
if (aForceNoOpener && windowIsNew) {
NS_RELEASE(*aResult);
}
return NS_OK;
}
@@ -1857,8 +1884,18 @@ nsWindowWatcher::GetCallerTreeItem(nsIDocShellTreeItem* aParentItem)
nsPIDOMWindow*
nsWindowWatcher::SafeGetWindowByName(const nsAString& aName,
bool aForceNoOpener,
nsIDOMWindow* aCurrentWindow)
{
if (aForceNoOpener) {
if (!aName.LowerCaseEqualsLiteral("_self") &&
!aName.LowerCaseEqualsLiteral("_top") &&
!aName.LowerCaseEqualsLiteral("_parent")) {
// Ignore all other names in the noopener case.
return nullptr;
}
}
nsCOMPtr<nsIDocShellTreeItem> startItem;
GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
@@ -1887,6 +1924,7 @@ nsresult
nsWindowWatcher::ReadyOpenedDocShellItem(nsIDocShellTreeItem* aOpenedItem,
nsIDOMWindow* aParent,
bool aWindowIsNew,
bool aForceNoOpener,
nsIDOMWindow** aOpenedWindow)
{
nsresult rv = NS_ERROR_FAILURE;
@@ -1897,7 +1935,9 @@ nsWindowWatcher::ReadyOpenedDocShellItem(nsIDocShellTreeItem* aOpenedItem,
nsCOMPtr<nsPIDOMWindow> piOpenedWindow = aOpenedItem->GetWindow();
if (piOpenedWindow) {
if (aParent) {
piOpenedWindow->SetOpenerWindow(aParent, aWindowIsNew); // damnit
if (!aForceNoOpener) {
piOpenedWindow->SetOpenerWindow(aParent, aWindowIsNew); // damnit
}
if (aWindowIsNew) {
#ifdef DEBUG
@@ -70,7 +70,9 @@ protected:
// Unlike GetWindowByName this will look for a caller on the JS
// stack, and then fall back on aCurrentWindow if it can't find one.
// It also knows to not look for things if aForceNoOpener is set.
nsPIDOMWindow* SafeGetWindowByName(const nsAString& aName,
bool aForceNoOpener,
nsIDOMWindow* aCurrentWindow);
// Just like OpenWindowJS, but knows whether it got called via OpenWindowJS
@@ -84,6 +86,8 @@ protected:
bool aNavigate,
nsITabParent* aOpeningTab,
nsIArray* aArgv,
bool aIsPopupSpam,
bool aForceNoOpener,
nsIDOMWindow** aResult);
static nsresult URIfromURL(const char* aURL,
@@ -105,6 +109,7 @@ protected:
static nsresult ReadyOpenedDocShellItem(nsIDocShellTreeItem* aOpenedItem,
nsIDOMWindow* aParent,
bool aWindowIsNew,
bool aForceNoOpener,
nsIDOMWindow** aOpenedWindow);
static void SizeOpenedDocShellItem(nsIDocShellTreeItem* aDocShellItem,
nsIDOMWindow* aParent,