Bug 1684490 - Check an in-process window context for user interaction. r=smaug,edgar

In the case we click a link from inside the frame, we don't want to
check for activation from the parent window but ourselves.

It feels like using the sourceWindowContext for these checks is always
the right thing to do, but it's not always possible if we set the
location.href from an out-of-process parent. In this case, we fall back
to consuming user activation on the iframe itself. This is tested in
browbrowser_protocol_ask_dialog.js when run with fission enabled.

We could consider making the user activation stuff more
multiprocess-friendly, but it's not clear to me it's worth it for this
particular edge case.

Differential Revision: https://phabricator.services.mozilla.com/D100578
This commit is contained in:
Emilio Cobos Álvarez
2021-01-04 20:54:07 +00:00
parent 63f58ff8b8
commit 2ca4fbcf9b
2 changed files with 66 additions and 8 deletions

View File

@@ -9916,9 +9916,32 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
&doesNotReturnData);
if (doesNotReturnData) {
WindowContext* parentContext =
mBrowsingContext->GetParentWindowContext();
MOZ_ASSERT(parentContext);
// The context to check user-interaction with for the purposes of
// popup-blocking.
//
// We generally want to check the context that initiated the navigation.
WindowContext* sourceWindowContext = [&] {
const MaybeDiscardedBrowsingContext& sourceBC =
aLoadState->SourceBrowsingContext();
if (!sourceBC.IsNullOrDiscarded()) {
if (WindowContext* wc = sourceBC.get()->GetCurrentWindowContext()) {
return wc;
}
}
return mBrowsingContext->GetParentWindowContext();
}();
MOZ_ASSERT(sourceWindowContext);
// FIXME: We can't check user-interaction against an OOP window. This is
// the next best thing we can really do. The load state keeps whether
// the navigation had a user interaction in process
// (aLoadState->HasValidUserGestureActivation()), but we can't really
// consume it, which we want to prevent popup-spamming from the same
// click event.
WindowContext* context =
sourceWindowContext->IsInProcess()
? sourceWindowContext
: mBrowsingContext->GetCurrentWindowContext();
const bool popupBlocked = [&] {
const bool active = mBrowsingContext->IsActive();
@@ -9927,15 +9950,15 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
//
// We consume the flag now even if there's no user activation.
const bool hasFreePass = [&] {
if (!active || !parentContext->SameOriginWithTop()) {
if (!active || !context->SameOriginWithTop()) {
return false;
}
nsGlobalWindowInner* win =
parentContext->TopWindowContext()->GetInnerWindow();
context->TopWindowContext()->GetInnerWindow();
return win && win->TryOpenExternalProtocolIframe();
}();
if (parentContext->ConsumeTransientUserGestureActivation()) {
if (context->ConsumeTransientUserGestureActivation()) {
// If the user has interacted with the page, consume it.
return false;
}
@@ -9948,7 +9971,7 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
return false;
}
if (parentContext->CanShowPopup()) {
if (sourceWindowContext->CanShowPopup()) {
return false;
}
@@ -9968,7 +9991,7 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
if (NS_SUCCEEDED(rv)) {
nsContentUtils::ReportToConsoleByWindowID(
message, nsIScriptError::warningFlag, "DOM"_ns,
parentContext->InnerWindowId());
context->InnerWindowId());
}
return NS_OK;
}