Bug 1879820 - Consume user activation when submitting form opens a new window; r=smaug,zcorpan

Differential Revision: https://phabricator.services.mozilla.com/D228648
This commit is contained in:
Edgar Chen 2024-11-19 08:19:04 +00:00
parent 474755f54b
commit 1af7296123
7 changed files with 193 additions and 3 deletions

View File

@ -6767,9 +6767,18 @@ nsresult nsGlobalWindowOuter::OpenInternal(
// (indirectly) maybe we can nix the AutoJSAPI usage OnLinkClickEvent::Run.
// But note that if you change this to GetEntryGlobal(), say, then
// OnLinkClickEvent::Run will need a full-blown AutoEntryScript. (Bug 1930445)
const bool checkForPopup =
!nsContentUtils::LegacyIsCallerChromeOrNativeCode() && !aDialog &&
!windowExists;
const bool checkForPopup = [&]() {
if (aDialog) {
return false;
}
if (windowExists) {
return false;
}
if (aLoadState && aLoadState->IsFormSubmission()) {
return true;
}
return !nsContentUtils::LegacyIsCallerChromeOrNativeCode();
}();
nsCOMPtr<nsIURI> uri;

View File

@ -14,6 +14,9 @@ prefs = ["formhelper.autozoom.force-disable.test-only=true"]
["test_popup_blocker_async_callback.html"]
["test_popup_blocker_form_target_blank.html"]
support-files = ["file_self_close.html"]
["test_popup_blocker_link_target_blank.html"]
support-files = ["file_self_close.html"]

View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test for triggering the popup blocker by submitting a target=_blank form</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<form action="file_self_close.html" target="_blank"><button>Submit</button></form>
<script>
let { ContentTaskUtils } = SpecialPowers.ChromeUtils.importESModule(
"resource://testing-common/ContentTaskUtils.sys.mjs"
);
function testTargetBlankForm(aSubmitFun, aMsg) {
add_task(async () => {
info(aMsg);
let popupBlockedPromise = ContentTaskUtils.waitForEvent(document, "DOMPopupBlocked", false, () => {
ok(true, "received DOMPopupBlocked event");
return true;
});
let form = document.querySelector("form");
form.addEventListener("submit", () => {
// Consume the user activation to ensure the popup is blocked.
SpecialPowers.wrap(document).clearUserGestureActivation();
});
aSubmitFun();
await popupBlockedPromise;
});
}
add_setup(async function() {
await SpecialPowers.pushPrefEnv({"set": [
// Enbale popup blocker
["dom.disable_open_during_load", true],
]});
});
testTargetBlankForm(() => {
synthesizeMouseAtCenter(document.querySelector("button"), {});
}, "Submit form by clicking submit button");
testTargetBlankForm(() => {
// Bug 1930678, form.submit() throws an error when the popup is blocked.
try {
document.querySelector("form").submit();
} catch(e) {}
}, "Submit form by submit()");
testTargetBlankForm(() => {
document.querySelector("form").requestSubmit();
}, "Submit form by requestSubmit()");
</script>
</body>
</html>

View File

@ -0,0 +1,3 @@
# XXX(edgar): Need to enable popup blocker due to bug 1930458.
prefs: [dom.disable_open_during_load:true]

View File

@ -0,0 +1,3 @@
# XXX(edgar): Need to enable popup blocker due to bug 1930458.
prefs: [dom.disable_open_during_load:true]

View File

@ -0,0 +1,60 @@
<!DOCTYPE html>
<title>Multi-globals: which userActivation should be consumed when submitting a target=_blank form?</title>
<link rel="help" href="https://html.spec.whatwg.org/#concept-form-submit">
<link rel="help" href="https://html.spec.whatwg.org/#the-rules-for-choosing-a-navigable">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<div id=log></div>
<script>
function waitForNewWindow(aChannelName) {
return new Promise(resolve => {
let channel = new BroadcastChannel(aChannelName);
channel.addEventListener("message", () => {
assert_true(true, "new window is opened");
resolve(channel);
}, {once: true});
});
}
function testFormSubmission(aChannelName, aSubmitFun, aMsg) {
promise_test(async (t) => {
await test_driver.bless('active main test window to open popup test window');
let popupPromise = waitForNewWindow("popup");
let popup = window.open("resources/endpoint.html?channelname=popup");
t.add_cleanup(() => { popup.close(); });
await popupPromise;
popup.document.documentElement.innerHTML += `
<form action=endpoint.html target=_blank>
<input type=hidden name=channelname value=${aChannelName}>
</form>
`;
await test_driver.bless('active main test window again');
assert_true(navigator.userActivation.isActive, 'main test window should have user activation');
await test_driver.bless('active popup test window', () => {}, popup);
assert_true(popup.navigator.userActivation.isActive, 'popup test window should have user activation');
let newWindowPromise = waitForNewWindow(aChannelName);
aSubmitFun(popup.document.querySelector("form"));
let newWindowChannel = await newWindowPromise;
t.add_cleanup(() => { newWindowChannel.postMessage("close"); });
assert_true(navigator.userActivation.isActive, 'main test window should still have user activation');
assert_false(popup.navigator.userActivation.isActive, 'popup test window should not have user activation');
}, aMsg);
}
testFormSubmission(`${Date.now()}_script_submit`, (form) => {
form.submit();
}, `<form target=_blank>.submit()`);
testFormSubmission(`${Date.now()}_script_requestSubmit`, (form) => {
form.requestSubmit();
}, `<form target=_blank>.requestSubmit()`);
</script>

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<title>Test that submitting a target=_blank form consumes userActivation</title>
<link rel="help" href="https://html.spec.whatwg.org/#concept-form-submit">
<link rel="help" href="https://html.spec.whatwg.org/#the-rules-for-choosing-a-navigable">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<div id=log></div>
<form action=resources/endpoint.html target=_blank>
<input type=hidden name=channelname>
<button>Submit</button>
</form>
<script>
function waitForNewWindow(aChannelName) {
return new Promise(resolve => {
let channel = new BroadcastChannel(aChannelName);
channel.addEventListener("message", () => {
assert_true(true, "new window is opened");
channel.postMessage("close");
resolve();
}, {once: true});
});
}
function testFormSubmission(aChannelName, aSubmitFun, aMsg) {
promise_test(async () => {
document.querySelector("input").value = aChannelName;
await test_driver.bless('transient activation');
assert_true(navigator.userActivation.isActive, 'should have user activation');
let newWindowPromise = waitForNewWindow(aChannelName);
aSubmitFun();
await newWindowPromise;
assert_false(navigator.userActivation.isActive, 'navigator.userActivation.isActive after opening a new window');
}, aMsg);
}
testFormSubmission(`${Date.now()}_script_submit`, () => {
document.querySelector("form").submit();
}, `<form target=_blank>.submit()`);
testFormSubmission(`${Date.now()}_script_requestSubmit`, () => {
document.querySelector("form").requestSubmit();
}, `<form target=_blank>.requestSubmit()`);
testFormSubmission(`${Date.now()}_click_submit`, () => {
test_driver.click(document.querySelector("button"));
}, `<form target=_blank> click submit`);
</script>