Bug 1700871 - Only allow focus move for links / form submission iff actually handling user input. r=smaug

The other navigation that allows focus moves (window.open) already goes
through the popup blocker, so that one is fine.

I think given how weird yet conservative other browsers are, this should
be a good trade-off to avoid false positives.

Differential Revision: https://phabricator.services.mozilla.com/D110196
This commit is contained in:
Emilio Cobos Álvarez 2021-03-31 01:51:46 +00:00
Родитель 2c09fd18ba
Коммит 168b2347b3
7 изменённых файлов: 102 добавлений и 37 удалений

Просмотреть файл

@ -12847,6 +12847,7 @@ nsresult nsDocShell::OnLinkClick(
aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal());
loadState->SetPrincipalToInherit(aContent->NodePrincipal());
loadState->SetCsp(aCsp ? aCsp : aContent->GetCsp());
loadState->SetAllowFocusMove(UserActivation::IsHandlingUserInput());
nsCOMPtr<nsIRunnable> ev =
new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied,
@ -13011,7 +13012,6 @@ nsresult nsDocShell::OnLinkClickSync(nsIContent* aContent,
aLoadState->SetTypeHint(NS_ConvertUTF16toUTF8(typeHint));
aLoadState->SetLoadType(loadType);
aLoadState->SetSourceBrowsingContext(mBrowsingContext);
aLoadState->SetAllowFocusMove(true);
aLoadState->SetHasValidUserGestureActivation(
context && context->HasValidTransientUserGestureActivation());

Просмотреть файл

@ -69,6 +69,7 @@ skip-if =
os == 'win' && bits == 64 # Bug 1692963
support-files =
file_bug1691214.html
file_bug1700871.html
[browser_inputStream_structuredClone.js]
[browser_multiple_popups.js]
skip-if = (os == 'win' && !debug) || (os == "mac" && !debug) # Bug 1505235, Bug 1661132 (osx)

Просмотреть файл

@ -291,19 +291,13 @@ function clickLink(
waitForLocationChange(targetsFrame, testBrowser, locationChangeNum)
);
}
info("BC children: " + browser.browsingContext.children.length);
promises.push(
SpecialPowers.spawn(
browser,
[[isFrame, linkId]],
([contentIsFrame, contentLinkId]) => {
let doc = content.document;
if (contentIsFrame) {
let frame = content.document.getElementById("frame");
doc = frame.contentDocument;
}
info("Clicking " + contentLinkId);
doc.querySelector(contentLinkId).click();
}
BrowserTestUtils.synthesizeMouseAtCenter(
linkId,
{},
isFrame ? browser.browsingContext.children[0] : browser
)
);
return Promise.all(promises);

Просмотреть файл

@ -6,25 +6,28 @@
const BASE_URL = "http://mochi.test:8888/browser/dom/base/test/";
add_task(async function() {
await BrowserTestUtils.withNewTab(
BASE_URL + "file_bug1691214.html",
async function(browser) {
let win;
{
let newWindow = BrowserTestUtils.domWindowOpenedAndLoaded();
async function newFocusedWindow() {
let delayedStartupPromise = BrowserTestUtils.waitForNewWindow();
await BrowserTestUtils.synthesizeMouseAtCenter("#link-1", {}, browser);
win = await newWindow;
ok(win, "First navigation should've opened the new window");
let win = await BrowserTestUtils.domWindowOpenedAndLoaded();
// New windows get focused after the first paint, see bug 1262946
await BrowserTestUtils.waitForContentEvent(
win.gBrowser.selectedBrowser,
"MozAfterPaint"
);
await delayedStartupPromise;
return win;
}
add_task(async function bug1691214() {
await BrowserTestUtils.withNewTab(
BASE_URL + "file_bug1691214.html",
async function(browser) {
let win;
{
let newWindow = newFocusedWindow();
await BrowserTestUtils.synthesizeMouseAtCenter("#link-1", {}, browser);
win = await newWindow;
is(Services.focus.focusedWindow, win, "New window should be focused");
}
@ -56,3 +59,42 @@ add_task(async function() {
}
);
});
// The tab has a form infinitely submitting to an iframe, and that shouldn't
// switch focus back. For that, we open a window from the tab, make sure it's
// focused, and then wait for three submissions (but since we need to listen to
// trusted events from chrome code, we use the iframe load event instead).
add_task(async function bug1700871() {
await BrowserTestUtils.withNewTab(
BASE_URL + "file_bug1700871.html",
async function(browser) {
let win;
{
let newWindow = newFocusedWindow();
await BrowserTestUtils.synthesizeMouseAtCenter("#link-1", {}, browser);
win = await newWindow;
is(Services.focus.focusedWindow, win, "New window should be focused");
}
info("waiting for three submit events and ensuring focus hasn't moved");
await SpecialPowers.spawn(browser, [], function() {
let pending = 3;
return new Promise(resolve => {
content.document
.querySelector("iframe")
.addEventListener("load", function(e) {
info("Got load on the frame: " + pending);
if (!--pending) {
resolve();
}
});
});
});
is(Services.focus.focusedWindow, win, "Focus shouldn't have moved");
win.close();
}
);
});

Просмотреть файл

@ -6,20 +6,29 @@ Tests for tab switching on link clicks.
<head>
<meta charset="utf-8">
<title>Tests for tab switching on link clicks.</title>
<style>
a {
display: inline-block;
width: 10px;
height: 10px;
overflow: hidden;
background-color: currentColor;
}
</style>
</head>
<body>
<a id="link-1" target="testTab" href="file_bug1303838_target_foo.html">Link 1</a><br>
<a id="link-2" target="testTab" href="file_bug1303838_target_bar.html">Link 2</a><br>
<a id="link-3" target="testTab" href="file_bug1303838_target_baz.html">Link 3</a><br>
<a id="link-4" href="#" onclick="testTab = window.open('file_bug1303838_target_foo.html', 'testTab'); return false;">Link 4</a><br>
<a id="link-5" href="#" onclick="testTab.location.href += '?1'; return false;">Link 5</a><br>
<a id="link-6" href="#" onclick="testTab.location.href += '#1'; return false;">Link 6</a><br>
<a id="link-7" target="testTab" href="file_bug1303838_target.html">Link 7</a><br>
<a id="anchor-link-1" target="testTab" href="file_bug1303838_target.html#foo">Anchor Link 1</a><br>
<a id="anchor-link-2" target="testTab" href="file_bug1303838_target.html#bar">Anchor Link 2</a><br>
<a id="anchor-link-3" target="testTab" href="file_bug1303838_target.html#baz">Anchor Link 3</a><br>
<a id="frame-link-1" target="testFrame" href="file_bug1303838_target_ifoo.html">Frame Link 1</a><br>
<a id="frame-link-2" target="testFrame" href="file_bug1303838_target_ibar.html">Frame Link 2</a><br>
<a id="frame-link-3" target="testFrame" href="file_bug1303838_target_ibaz.html">Frame Link 3</a><br>
<a id="link-1" target="testTab" href="file_bug1303838_target_foo.html">Link 1</a>,
<a id="link-2" target="testTab" href="file_bug1303838_target_bar.html">Link 2</a>,
<a id="link-3" target="testTab" href="file_bug1303838_target_baz.html">Link 3</a>,
<a id="link-4" href="#" onclick="testTab = window.open('file_bug1303838_target_foo.html', 'testTab'); return false;">Link 4</a>,
<a id="link-5" href="#" onclick="testTab.location.href += '?1'; return false;">Link 5</a>,
<a id="link-6" href="#" onclick="testTab.location.href += '#1'; return false;">Link 6</a>,
<a id="link-7" target="testTab" href="file_bug1303838_target.html">Link 7</a>,
<a id="anchor-link-1" target="testTab" href="file_bug1303838_target.html#foo">Anchor Link 1</a>,
<a id="anchor-link-2" target="testTab" href="file_bug1303838_target.html#bar">Anchor Link 2</a>,
<a id="anchor-link-3" target="testTab" href="file_bug1303838_target.html#baz">Anchor Link 3</a>,
<a id="frame-link-1" target="testFrame" href="file_bug1303838_target_ifoo.html">Frame Link 1</a>,
<a id="frame-link-2" target="testFrame" href="file_bug1303838_target_ibar.html">Frame Link 2</a>,
<a id="frame-link-3" target="testFrame" href="file_bug1303838_target_ibaz.html">Frame Link 3</a>,
</body>
</html>

Просмотреть файл

@ -0,0 +1,18 @@
<!doctype html>
<a id="link-1" href="?1" onclick="window.open(this.href, 'childWin', 'height=300,width=600'); return false;">Open in window.</a>
<form target="frame" method="get">
<input type=hidden name=counter value=0>
<iframe name="frame" href="file_bug1700871.html"></iframe>
</form>
<script>
(function submitForm() {
if (location.search) {
return; // Don't fork-bomb ourselves.
}
let counter = document.querySelector("input");
counter.value = ++counter.value;
document.querySelector("form").submit();
setTimeout(submitForm, 500);
}());
</script>

Просмотреть файл

@ -825,6 +825,7 @@ nsresult HTMLFormElement::SubmitSubmission(
loadState->SetTriggeringPrincipal(NodePrincipal());
loadState->SetPrincipalToInherit(NodePrincipal());
loadState->SetCsp(GetCsp());
loadState->SetAllowFocusMove(UserActivation::IsHandlingUserInput());
rv = nsDocShell::Cast(container)->OnLinkClickSync(this, loadState, false,
NodePrincipal());