closes #522: rel=noopener M1222516 M1358469 M1419960

This commit is contained in:
Cameron Kaiser 2018-09-20 21:09:22 -07:00
parent 46bf256a1f
commit 6abcdc2cc2
10 changed files with 147 additions and 27 deletions

View File

@ -138,6 +138,8 @@ ImageAccessible::DoAction(uint8_t aIndex)
nsCOMPtr<nsPIDOMWindow> tmp;
return NS_SUCCEEDED(piWindow->Open(spec, EmptyString(), EmptyString(),
/* aLoadInfo = */ nullptr,
/* aForceNoOpener = */ false,
getter_AddRefs(tmp)));
}

View File

@ -9592,9 +9592,20 @@ nsDocShell::InternalLoad(nsIURI* aURI,
if (aWindowTarget && *aWindowTarget) {
// Locate the target DocShell.
nsCOMPtr<nsIDocShellTreeItem> targetItem;
rv = FindItemWithName(aWindowTarget, nullptr, this,
getter_AddRefs(targetItem));
NS_ENSURE_SUCCESS(rv, rv);
nsDependentString name(aWindowTarget);
// Only _self, _parent, and _top are supported in noopener case. But we
// have to be careful to not apply that to the noreferrer case. See bug
// 1358469.
bool allowNamedTarget = !(aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) ||
(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
if (allowNamedTarget ||
name.LowerCaseEqualsLiteral("_self") ||
name.LowerCaseEqualsLiteral("_parent") ||
name.LowerCaseEqualsLiteral("_top")) {
rv = FindItemWithName(aWindowTarget, nullptr, this,
getter_AddRefs(targetItem));
NS_ENSURE_SUCCESS(rv, rv);
}
targetDocShell = do_QueryInterface(targetItem);
// If the targetDocShell doesn't exist, then this is a new docShell
@ -9737,11 +9748,60 @@ nsDocShell::InternalLoad(nsIURI* aURI,
NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
nsDependentString name(aWindowTarget);
nsCOMPtr<nsIDOMWindow> newWin;
nsAutoCString spec;
if (aURI) {
aURI->GetSpec(spec);
}
// If we are a noopener load, we just hand the whole thing over to our
// window.
if (aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) {
// Various asserts that we know to hold because NO_OPENER loads can only
// happen for links.
MOZ_ASSERT(!aLoadReplace);
/* MOZ_ASSERT(aPrincipalToInherit == aTriggeringPrincipal); */
MOZ_ASSERT(aFlags == INTERNAL_LOAD_FLAGS_NO_OPENER ||
aFlags == (INTERNAL_LOAD_FLAGS_NO_OPENER |
INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
MOZ_ASSERT(!aPostData);
MOZ_ASSERT(!aHeadersData);
MOZ_ASSERT(aLoadType == LOAD_LINK);
MOZ_ASSERT(!aSHEntry);
MOZ_ASSERT(aFirstParty); // Windowwatcher will assume this.
nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
rv = CreateLoadInfo(getter_AddRefs(loadInfo));
if (NS_FAILED(rv)) {
return rv;
}
// Set up our loadinfo so it will do the load as much like we would have
// as possible.
loadInfo->SetReferrer(aReferrer);
loadInfo->SetReferrerPolicy(aReferrerPolicy);
loadInfo->SetSendReferrer(!(aFlags &
INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
loadInfo->SetOriginalURI(aOriginalURI);
loadInfo->SetLoadReplace(aLoadReplace);
loadInfo->SetOwner(loadingPrincipal); // SetTriggeringPrincipal
loadInfo->SetInheritOwner( /* SetInheritPrincipal _INHERIT_PRINCIPAL */
!!(aFlags & INTERNAL_LOAD_FLAGS_INHERIT_OWNER));
// Explicit principal because we do not want any guesses as to what the
// principal to inherit is: it should be aTriggeringPrincipal.
loadInfo->SetOwnerIsExplicit(true); // SetPrincipalIsExplicit(true);
loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(LOAD_LINK));
nsCOMPtr<nsPIDOMWindow> newWin;
rv = win->Open(NS_ConvertUTF8toUTF16(spec),
name, // window name
EmptyString(), // Features
loadInfo,
true, // aForceNoOpener
getter_AddRefs(newWin));
MOZ_ASSERT(!newWin);
return rv;
}
nsCOMPtr<nsIDOMWindow> newWin;
rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
name, // window name
EmptyString(), // Features
@ -9756,11 +9816,6 @@ nsDocShell::InternalLoad(nsIURI* aURI,
if (!newDoc || newDoc->IsInitialDocument()) {
isNewWindow = true;
aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
// set opener object to null for noreferrer
if (aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) {
piNewWin->SetOpenerWindow(nullptr, false);
}
}
}
@ -13497,11 +13552,16 @@ nsDocShell::OnLinkClickSync(nsIContent* aContent,
aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, referrer);
nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(referrer);
while (tok.hasMoreTokens()) {
if (tok.nextToken().LowerCaseEqualsLiteral("noreferrer")) {
const nsAString& token = tok.nextToken();
if (token.LowerCaseEqualsLiteral("noreferrer")) {
flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
INTERNAL_LOAD_FLAGS_NO_OPENER;
// We now have all the flags we could possibly have, so just stop.
break;
}
if (token.LowerCaseEqualsLiteral("noopener")) {
flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
}
}
}

View File

@ -7677,9 +7677,11 @@ nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName,
nsresult
nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName,
const nsAString& aOptions, nsPIDOMWindow **_retval)
const nsAString& aOptions, nsIDocShellLoadInfo* aLoadInfo,
bool aForceNoOpener, nsPIDOMWindow **_retval)
{
FORWARD_TO_OUTER(Open, (aUrl, aName, aOptions, _retval),
FORWARD_TO_OUTER(Open, (aUrl, aName, aOptions, aLoadInfo, aForceNoOpener,
_retval),
NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIDOMWindow> window;
nsresult rv = OpenInternal(aUrl, aName, aOptions,
@ -7689,6 +7691,8 @@ nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName,
false, // aDoJSFixups
true, // aNavigate
nullptr, nullptr, // No args
aLoadInfo,
aForceNoOpener,
GetPrincipal(), // aCalleePrincipal
nullptr, // aJSCallerContext
getter_AddRefs(window));
@ -7710,6 +7714,8 @@ nsGlobalWindow::OpenJS(const nsAString& aUrl, const nsAString& aName,
true, // aDoJSFixups
true, // aNavigate
nullptr, nullptr, // No args
nullptr, // aLoadInfo
false, // aForceNoOpener
GetPrincipal(), // aCalleePrincipal
nsContentUtils::GetCurrentJSContext(), // aJSCallerContext
_retval);
@ -7730,6 +7736,8 @@ nsGlobalWindow::OpenDialog(const nsAString& aUrl, const nsAString& aName,
false, // aDoJSFixups
true, // aNavigate
nullptr, aExtraArgument, // Arguments
nullptr, // aLoadInfo
false, // aForceNoOpener
GetPrincipal(), // aCalleePrincipal
nullptr, // aJSCallerContext
_retval);
@ -7750,6 +7758,8 @@ nsGlobalWindow::OpenNoNavigate(const nsAString& aUrl,
false, // aDoJSFixups
false, // aNavigate
nullptr, nullptr, // No args
nullptr, // aLoadInfo
false, // aForceNoOpener
GetPrincipal(), // aCalleePrincipal
nullptr, // aJSCallerContext
_retval);
@ -7780,6 +7790,8 @@ nsGlobalWindow::OpenDialogOuter(JSContext* aCx, const nsAString& aUrl,
false, // aDoJSFixups
true, // aNavigate
argvArray, nullptr, // Arguments
nullptr, // aLoadInfo
false, // aForceNoOpener
GetPrincipal(), // aCalleePrincipal
aCx, // aJSCallerContext
getter_AddRefs(dialog));
@ -8860,6 +8872,8 @@ nsGlobalWindow::ShowModalDialogOuter(const nsAString& aUrl, nsIVariant* aArgumen
true, // aDoJSFixups
true, // aNavigate
nullptr, argHolder, // args
nullptr, // aLoadInfo
false, // aForceNoOpener
GetPrincipal(), // aCalleePrincipal
nullptr, // aJSCallerContext
getter_AddRefs(dlgWin));
@ -11294,6 +11308,8 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
bool aDoJSFixups, bool aNavigate,
nsIArray *argv,
nsISupports *aExtraArgument,
nsIDocShellLoadInfo* aLoadInfo,
bool aForceNoOpener,
nsIPrincipal *aCalleePrincipal,
JSContext *aJSCallerContext,
nsIDOMWindow **aReturn)
@ -11335,16 +11351,24 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
nsIPrincipal::APP_STATUS_INSTALLED;
}
bool forceNoOpener = false;
nsAutoCString options;
bool forceNoOpener = aForceNoOpener;
// Unlike other window flags, "noopener" comes from splitting on commas with
// HTML whitespace trimming...
nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
aOptions, ',');
while (tok.hasMoreTokens()) {
if (tok.nextToken().EqualsLiteral("noopener")) {
auto nextTok = tok.nextToken();
if (nextTok.EqualsLiteral("noopener")) {
forceNoOpener = true;
break;
continue;
}
// Want to create a copy of the options without 'noopener' because having
// 'noopener' in the options affects other window features.
if (!options.IsEmpty()) {
options.Append(',');
}
AppendUTF16toUTF8(nextTok, options);
}
// XXXbz When this gets fixed to not use LegacyIsCallerNativeCode()
@ -11406,10 +11430,9 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
NS_ENSURE_TRUE(wwatch, rv);
NS_ConvertUTF16toUTF8 options(aOptions);
NS_ConvertUTF16toUTF8 name(aName);
const char *options_ptr = aOptions.IsEmpty() ? nullptr : options.get();
const char *options_ptr = options.IsEmpty() ? nullptr : options.get();
const char *name_ptr = aName.IsEmpty() ? nullptr : name.get();
nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
@ -11436,6 +11459,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
aDialog, aNavigate, nullptr, argv,
isPopupSpamWindow,
forceNoOpener,
aLoadInfo,
getter_AddRefs(domReturn));
} else {
// Force a system caller here so that the window watcher won't screw us
@ -11457,6 +11481,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
aDialog, aNavigate, nullptr, aExtraArgument,
isPopupSpamWindow,
forceNoOpener,
aLoadInfo,
getter_AddRefs(domReturn));
}

View File

@ -901,7 +901,10 @@ public:
const nsAString& aOptions,
mozilla::ErrorResult& aError);
nsresult Open(const nsAString& aUrl, const nsAString& aName,
const nsAString& aOptions, nsPIDOMWindow **_retval) override;
const nsAString& aOptions,
nsIDocShellLoadInfo* aLoadInfo,
bool aForceNoOpener,
nsPIDOMWindow **_retval) override;
mozilla::dom::Navigator* GetNavigator(mozilla::ErrorResult& aError);
nsIDOMNavigator* GetNavigator() override;
nsIDOMOfflineResourceList* GetApplicationCache(mozilla::ErrorResult& aError);
@ -1380,15 +1383,21 @@ private:
* three args, if present, will be aUrl, aName, and aOptions. So this
* param only matters if there are more than 3 arguments.
*
* @param argc The number of arguments in argv.
*
* @param aExtraArgument Another way to pass arguments in. This is mutually
* exclusive with the argv/argc approach.
* exclusive with the argv approach.
*
* @param aLoadInfo to be passed on along to the windowwatcher.
*
* @param aForceNoOpener if true, will act as if "noopener" were passed in
* aOptions, but without affecting any other window
* features.
*
* @param aJSCallerContext The calling script's context. This must be null
* when aCalledNoScript is true.
*
* @param aReturn [out] The window that was opened, if any.
* @param aReturn [out] The window that was opened, if any. Will be null if
* aForceNoOpener is true of if aOptions contains
* "noopener".
*
* Outer windows only.
*/
@ -1402,6 +1411,8 @@ private:
bool aNavigate,
nsIArray *argv,
nsISupports *aExtraArgument,
nsIDocShellLoadInfo* aLoadInfo,
bool aForceNoOpener,
nsIPrincipal *aCalleePrincipal,
JSContext *aJSCallerContext,
nsIDOMWindow **aReturn);

View File

@ -25,6 +25,7 @@ class nsIArray;
class nsIContent;
class nsICSSDeclaration;
class nsIDocShell;
class nsIDocShellLoadInfo;
class nsIDocument;
class nsIIdleObserver;
class nsIScriptTimeoutHandler;
@ -795,8 +796,14 @@ public:
virtual already_AddRefed<nsISelection> GetSelection() = 0;
virtual already_AddRefed<nsPIDOMWindow> GetOpener() = 0;
virtual already_AddRefed<nsIDOMWindowCollection> GetFrames() = 0;
// aLoadInfo will be passed on through to the windowwatcher.
// aForceNoOpener will act just like a "noopener" feature in aOptions except
// will not affect any other window features.
virtual nsresult Open(const nsAString& aUrl, const nsAString& aName,
const nsAString& aOptions, nsPIDOMWindow **_retval) = 0;
const nsAString& aOptions,
nsIDocShellLoadInfo* aLoadInfo,
bool aForceNoOpener,
nsPIDOMWindow **_retval) = 0;
virtual nsresult OpenDialog(const nsAString& aUrl, const nsAString& aName,
const nsAString& aOptions,
nsISupports* aExtraArgument, nsIDOMWindow** _retval) = 0;

View File

@ -42,6 +42,7 @@ ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
// static
const DOMTokenListSupportedToken HTMLAnchorElement::sSupportedRelValues[] = {
"noreferrer",
"noopener",
nullptr
};

View File

@ -614,6 +614,7 @@ private:
// opener anyway, and we _do_ want the returned
// window.
/* aForceNoOpener = */ false,
/* aLoadInfo = */ nullptr,
getter_AddRefs(newWindow));
nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(newWindow);
pwindow.forget(aWindow);

View File

@ -16,8 +16,9 @@ interface nsIWebBrowserChrome;
interface nsIDocShellTreeItem;
interface nsIArray;
interface nsITabParent;
interface nsIDocShellLoadInfo;
[uuid(0f2d9d75-c46b-4114-802e-83b4655e61d2)]
[uuid(0f2d9d75-c46b-4114-802e-83b4655e61d3)]
interface nsPIWindowWatcher : nsISupports
{
@ -62,6 +63,12 @@ interface nsPIWindowWatcher : nsISupports
looking for existing windows with the given name,
not setting an opener on the newly opened window,
and returning null from this method.
@param aLoadInfo if aNavigate is true, this allows the caller to pass in
an nsIDocShellLoadInfo to use for the navigation.
Callers can pass in null if they want the windowwatcher
to just construct a loadinfo itself. If aNavigate is
false, this argument is ignored.
@return the new window
@note This method may examine the JS context stack for purposes of
@ -78,7 +85,8 @@ interface nsPIWindowWatcher : nsISupports
in boolean aNavigate, in nsITabParent aOpeningTab,
in nsISupports aArgs,
in boolean aIsPopupSpam,
in boolean aForceNoOpener);
in boolean aForceNoOpener,
in nsIDocShellLoadInfo aLoadInfo);
/**
* Find a named docshell tree item amongst all windows registered

View File

@ -370,6 +370,7 @@ nsWindowWatcher::OpenWindow(nsIDOMWindow* aParent,
/* navigate = */ true, nullptr, argv,
/* aIsPopupSpam = */ false,
/* aForceNoOpener = */ false,
/* aLoadInfo = */ nullptr,
aResult);
}
@ -430,6 +431,7 @@ nsWindowWatcher::OpenWindow2(nsIDOMWindow* aParent,
nsISupports* aArguments,
bool aIsPopupSpam,
bool aForceNoOpener,
nsIDocShellLoadInfo* aLoadInfo,
nsIDOMWindow** aResult)
{
nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
@ -452,6 +454,7 @@ nsWindowWatcher::OpenWindow2(nsIDOMWindow* aParent,
aNavigate, aOpeningTab, argv,
aIsPopupSpam,
aForceNoOpener,
aLoadInfo,
aResult);
}
@ -467,6 +470,7 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
nsIArray* aArgv,
bool aIsPopupSpam,
bool aForceNoOpener,
nsIDocShellLoadInfo* aLoadInfo,
nsIDOMWindow** aResult)
{
nsresult rv = NS_OK;
@ -967,8 +971,8 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
}
}
nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
if (uriToLoad && aNavigate) {
nsCOMPtr<nsIDocShellLoadInfo> loadInfo = aLoadInfo;
if (uriToLoad && aNavigate && !loadInfo) {
newDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);

View File

@ -88,6 +88,7 @@ protected:
nsIArray* aArgv,
bool aIsPopupSpam,
bool aForceNoOpener,
nsIDocShellLoadInfo* aLoadInfo,
nsIDOMWindow** aResult);
static nsresult URIfromURL(const char* aURL,