Bug 1567175, add a test that verifies that a cross process iframe with a password field saves the password on submit and page navigation, r=MattN

Differential Revision: https://phabricator.services.mozilla.com/D49704

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Neil Deakin 2019-10-21 18:18:38 +00:00
Родитель fc3aa270bf
Коммит ca34a940cf
5 изменённых файлов: 221 добавлений и 1 удалений

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

@ -247,7 +247,9 @@ class LoginManagerParent extends JSWindowActorParent {
formid: data.formid,
});
if (gListenerForTests) {
gListenerForTests("FormProcessed", {});
gListenerForTests("FormProcessed", {
browsingContext: this.browsingContext,
});
}
break;
}

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

@ -32,6 +32,10 @@ support-files =
subtst_notifications_9.html
subtst_notifications_10.html
subtst_notifications_change_p.html
[browser_capture_doorhanger_crossframe.js]
support-files =
form_crossframe.html
form_crossframe_inner.html
[browser_capture_doorhanger_empty_password.js]
skip-if = true # Bug 1272849
[browser_capture_doorhanger_httpsUpgrade.js]
@ -93,3 +97,4 @@ support-files =
subtst_privbrowsing_1.html
form_password_change.html
[browser_promptToChangePassword.js]

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

@ -0,0 +1,187 @@
const OUTER_URL =
"https://test1.example.com:443" + DIRECTORY_PATH + "form_crossframe.html";
async function acceptPasswordSave() {
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
let promiseNewSavedPassword = TestUtils.topicObserved(
"LoginStats:NewSavedPassword",
(subject, data) => subject == gBrowser.selectedBrowser
);
clickDoorhangerButton(notif, REMEMBER_BUTTON);
await promiseNewSavedPassword;
}
function checkFormFields(browsingContext, prefix, username, password) {
return SpecialPowers.spawn(
browsingContext,
[prefix, username, password],
(formPrefix, expectedUsername, expectedPassword) => {
let doc = content.document;
Assert.equal(
doc.getElementById(formPrefix + "-username").value,
expectedUsername,
"username matches"
);
Assert.equal(
doc.getElementById(formPrefix + "-password").value,
expectedPassword,
"password matches"
);
}
);
}
function listenForNotifications(count) {
return new Promise(resolve => {
let notifications = [];
LoginManagerParent.setListenerForTests((msg, data) => {
if (msg == "FormProcessed") {
notifications.push("FormProcessed: " + data.browsingContext.id);
} else if (msg == "FormSubmit") {
notifications.push("FormSubmit: " + data.usernameField.name);
}
if (notifications.length == count) {
resolve(notifications);
}
});
});
}
async function verifyNotifications(notifyPromise, expected) {
let actual = await notifyPromise;
is(actual.length, expected.length, "Extra notification(s) sent");
let expectedItem;
while ((expectedItem = expected.pop())) {
let index = actual.indexOf(expectedItem);
if (index >= 0) {
actual.splice(index, 1);
} else {
ok(false, "Expected notification '" + expectedItem + "' not sent");
}
}
}
/*
* In this test, a frame is loaded with a document that contains a username
* and password field. This frame also contains another child iframe that
* itself contains a username and password field. This inner frame is loaded
* from a different domain than the first.
*
* locationMode should be false to submit forms, or true to click a button
* which changes the location instead. The latter should still save the
* username and password.
*/
async function submitSomeCrossSiteFrames(locationMode) {
info("Check with location mode " + locationMode);
let notifyPromise = listenForNotifications(2);
let firsttab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
OUTER_URL
);
let outerFrameBC = firsttab.linkedBrowser.browsingContext;
let innerFrameBC = outerFrameBC.getChildren()[0];
await verifyNotifications(notifyPromise, [
"FormProcessed: " + outerFrameBC.id,
"FormProcessed: " + innerFrameBC.id,
]);
// Fill in the username and password for both the outer and inner frame
// and submit the inner frame.
notifyPromise = listenForNotifications(1);
info("submit page after changing inner form");
await SpecialPowers.spawn(outerFrameBC, [], () => {
let doc = content.document;
doc.getElementById("outer-username").value = "outer";
doc.getElementById("outer-password").value = "outerpass";
});
await SpecialPowers.spawn(innerFrameBC, [locationMode], doClick => {
let doc = content.document;
doc.getElementById("inner-username").value = "inner";
doc.getElementById("inner-password").value = "innerpass";
if (doClick) {
doc.getElementById("inner-gobutton").click();
} else {
doc.getElementById("inner-form").submit();
}
});
await acceptPasswordSave();
await verifyNotifications(notifyPromise, ["FormSubmit: username"]);
// Next, open a second tab with the same page in it to verify that the data gets filled properly.
notifyPromise = listenForNotifications(2);
let secondtab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
OUTER_URL
);
let outerFrameBC2 = secondtab.linkedBrowser.browsingContext;
let innerFrameBC2 = outerFrameBC2.getChildren()[0];
await verifyNotifications(notifyPromise, [
"FormProcessed: " + outerFrameBC2.id,
"FormProcessed: " + innerFrameBC2.id,
]);
await checkFormFields(outerFrameBC2, "outer", "", "");
await checkFormFields(innerFrameBC2, "inner", "inner", "innerpass");
// Next, change the username and password fields in the outer frame and submit.
notifyPromise = listenForNotifications(1);
info("submit page after changing outer form");
await SpecialPowers.spawn(outerFrameBC2, [locationMode], doClick => {
let doc = content.document;
doc.getElementById("outer-username").value = "outer2";
doc.getElementById("outer-password").value = "outerpass2";
if (doClick) {
doc.getElementById("outer-gobutton").click();
} else {
doc.getElementById("outer-form").submit();
}
doc.getElementById("outer-form").submit();
});
await acceptPasswordSave();
await verifyNotifications(notifyPromise, ["FormSubmit: outer-username"]);
// Finally, open a third tab with the same page in it to verify that the data gets filled properly.
notifyPromise = listenForNotifications(2);
let thirdtab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
OUTER_URL
);
let outerFrameBC3 = thirdtab.linkedBrowser.browsingContext;
let innerFrameBC3 = outerFrameBC3.getChildren()[0];
await verifyNotifications(notifyPromise, [
"FormProcessed: " + outerFrameBC3.id,
"FormProcessed: " + innerFrameBC3.id,
]);
await checkFormFields(outerFrameBC3, "outer", "outer2", "outerpass2");
await checkFormFields(innerFrameBC3, "inner", "inner", "innerpass");
LoginManagerParent.setListenerForTests(null);
await BrowserTestUtils.removeTab(firsttab);
await BrowserTestUtils.removeTab(secondtab);
await BrowserTestUtils.removeTab(thirdtab);
LoginTestUtils.clearData();
}
add_task(async function cross_site_frames_submit() {
await submitSomeCrossSiteFrames(false);
});
add_task(async function cross_site_frames_changelocation() {
await submitSomeCrossSiteFrames(true);
});

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

@ -0,0 +1,13 @@
<html><body>
<button onclick="window.location = 'about:blank'">Next</button>
<iframe src="https://test2.example.org:443/browser/toolkit/components/passwordmgr/test/browser/form_crossframe_inner.html"
width="300" height="300"></iframe>
<form id="outer-form" action="formsubmit.sjs">
<input id="outer-username" name="outer-username">
<input id="outer-password" name="outer-password" type="password">
<input id="outer-submit" type="submit">
<button id="outer-gobutton" onclick="this.location = 'about:blank'">Go</button>
</form>
</body></html>

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

@ -0,0 +1,13 @@
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<!-- Form for use inside a frame. -->
<form id="inner-form" action="formsubmit.sjs">
<input id="inner-username" name="username">
<input id="inner-password" name="password" type="password">
<input id="inner-submit" type="submit">
</form>
<button id="inner-gobutton" onclick="document.location = 'about:blank'">Go</button>
</body></html>