зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1648551: Treat form in <iframe> "pagehide" event as form submission r=zbraniecki
Differential Revision: https://phabricator.services.mozilla.com/D81931
This commit is contained in:
Родитель
bfb49cc697
Коммит
f676613244
|
@ -519,9 +519,15 @@ var FormAutofillContent = {
|
|||
* 3. Number of filled fields is less than autofill threshold
|
||||
*
|
||||
* @param {HTMLElement} formElement Root element which receives submit event.
|
||||
* @param {Window} domWin Content window only passed for unit tests
|
||||
* @param {Window} domWin Content window; passed for unit tests and when
|
||||
* invoked by the FormAutofillSection
|
||||
* @param {Object} handler FormAutofillHander, if known by caller
|
||||
*/
|
||||
formSubmitted(formElement, domWin = formElement.ownerGlobal) {
|
||||
formSubmitted(
|
||||
formElement,
|
||||
domWin = formElement.ownerGlobal,
|
||||
handler = undefined
|
||||
) {
|
||||
this.debug("Handling form submission");
|
||||
|
||||
if (!FormAutofill.isAutofillEnabled) {
|
||||
|
@ -535,7 +541,7 @@ var FormAutofillContent = {
|
|||
return;
|
||||
}
|
||||
|
||||
let handler = this._formsDetails.get(formElement);
|
||||
handler = handler ?? this._formsDetails.get(formElement);
|
||||
if (!handler) {
|
||||
this.debug("Form element could not map to an existing handler");
|
||||
return;
|
||||
|
@ -724,7 +730,10 @@ var FormAutofillContent = {
|
|||
let formHandler = this._getFormHandler(element);
|
||||
if (!formHandler) {
|
||||
let formLike = FormLikeFactory.createFromField(element);
|
||||
formHandler = new FormAutofillHandler(formLike);
|
||||
formHandler = new FormAutofillHandler(
|
||||
formLike,
|
||||
this.formSubmitted.bind(this)
|
||||
);
|
||||
} else if (!formHandler.updateFormIfNeeded(element)) {
|
||||
this.debug("No control is removed or inserted since last collection.");
|
||||
return;
|
||||
|
|
|
@ -885,8 +885,46 @@ class FormAutofillAddressSection extends FormAutofillSection {
|
|||
}
|
||||
|
||||
class FormAutofillCreditCardSection extends FormAutofillSection {
|
||||
constructor(fieldDetails, winUtils) {
|
||||
/**
|
||||
* Credit Card Section Constructor
|
||||
*
|
||||
* @param {Object} fieldDetails
|
||||
* The fieldDetail objects for the fields in this section
|
||||
* @param {Object} winUtils
|
||||
* A WindowUtils reference for the Window the section appears in
|
||||
* @param {Object} handler
|
||||
* The FormAutofillHandler responsible for this section
|
||||
*/
|
||||
constructor(fieldDetails, winUtils, handler) {
|
||||
super(fieldDetails, winUtils);
|
||||
|
||||
this.handler = handler;
|
||||
|
||||
// For valid sections, check whether the section is in an
|
||||
// <iframe>; and, if so, watch for the <iframe> to pagehide.
|
||||
// If the section is invalid, then the superclass constructor
|
||||
// will have cleared out `this.fieldDetails`.
|
||||
if (this.fieldDetails.length) {
|
||||
if (handler.window.location != handler.window.parent?.location) {
|
||||
log.debug(
|
||||
"Credit card form is in an iframe -- watching for pagehide",
|
||||
fieldDetails
|
||||
);
|
||||
handler.window.addEventListener(
|
||||
"pagehide",
|
||||
this._handlePageHide.bind(this)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_handlePageHide(event) {
|
||||
this.handler.window.removeEventListener(
|
||||
"pagehide",
|
||||
this._handlePageHide.bind(this)
|
||||
);
|
||||
log.debug("Credit card subframe is pagehideing", this.handler.form);
|
||||
this.handler.onFormSubmitted();
|
||||
}
|
||||
|
||||
isValidSection() {
|
||||
|
@ -1087,19 +1125,37 @@ class FormAutofillHandler {
|
|||
* Initialize the form from `FormLike` object to handle the section or form
|
||||
* operations.
|
||||
* @param {FormLike} form Form that need to be auto filled
|
||||
* @param {function} onFormSubmitted Function that can be invoked
|
||||
* to simulate form submission. Function is passed
|
||||
* three arguments: (1) a FormLike for the form being
|
||||
* submitted, (2) the corresponding Window, and (3) the
|
||||
* responsible FormAutofillHandler.
|
||||
*/
|
||||
constructor(form) {
|
||||
constructor(form, onFormSubmitted = () => {}) {
|
||||
this._updateForm(form);
|
||||
|
||||
/**
|
||||
* The window to which this form belongs
|
||||
*/
|
||||
this.window = this.form.rootElement.ownerGlobal;
|
||||
|
||||
/**
|
||||
* A WindowUtils reference of which Window the form belongs
|
||||
*/
|
||||
this.winUtils = this.form.rootElement.ownerGlobal.windowUtils;
|
||||
this.winUtils = this.window.windowUtils;
|
||||
|
||||
/**
|
||||
* Time in milliseconds since epoch when a user started filling in the form.
|
||||
*/
|
||||
this.timeStartedFillingMS = null;
|
||||
|
||||
/**
|
||||
* This function is used if the form handler (or one of its sections)
|
||||
* determines that it needs to act as if the form had been submitted.
|
||||
*/
|
||||
this.onFormSubmitted = () => {
|
||||
onFormSubmitted(this.form, this.window, this);
|
||||
};
|
||||
}
|
||||
|
||||
set focusedInput(element) {
|
||||
|
@ -1214,7 +1270,8 @@ class FormAutofillHandler {
|
|||
} else if (type == FormAutofillUtils.SECTION_TYPES.CREDIT_CARD) {
|
||||
section = new FormAutofillCreditCardSection(
|
||||
fieldDetails,
|
||||
this.winUtils
|
||||
this.winUtils,
|
||||
this
|
||||
);
|
||||
} else {
|
||||
throw new Error("Unknown field type.");
|
||||
|
|
|
@ -194,7 +194,7 @@ add_task(async function test_submit_untouched_creditCard_form_iframe() {
|
|||
|
||||
creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "Still 1 credit card");
|
||||
is(creditCards[0].timesUsed, 1, "timesUsed field set to 1");
|
||||
is(creditCards[0].timesUsed, 2, "timesUsed field set to 2");
|
||||
is(
|
||||
SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF),
|
||||
3,
|
||||
|
@ -204,6 +204,59 @@ add_task(async function test_submit_untouched_creditCard_form_iframe() {
|
|||
await removeAllRecords();
|
||||
});
|
||||
|
||||
add_task(async function test_iframe_unload_save_card() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [[CREDITCARDS_USED_STATUS_PREF, 0]],
|
||||
});
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{ gBrowser, url: CREDITCARD_FORM_IFRAME_URL },
|
||||
async function(browser) {
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
"popupshown"
|
||||
);
|
||||
let iframeBC = browser.browsingContext.children[0];
|
||||
let onChanged = TestUtils.topicObserved("formautofill-storage-changed");
|
||||
await SpecialPowers.spawn(iframeBC, [], async function() {
|
||||
let form = content.document.getElementById("form");
|
||||
let name = form.querySelector("#cc-name");
|
||||
name.focus();
|
||||
name.setUserInput("User 1");
|
||||
|
||||
form.querySelector("#cc-number").setUserInput("4556194630960970");
|
||||
form.querySelector("#cc-exp-month").setUserInput("10");
|
||||
form.querySelector("#cc-exp-year").setUserInput("2024");
|
||||
form.querySelector("#cc-type").value = "visa";
|
||||
|
||||
// Wait 1000ms before submission to make sure the input value applied
|
||||
await new Promise(resolve => content.setTimeout(resolve, 1000));
|
||||
});
|
||||
|
||||
info("Removing iframe without submitting");
|
||||
await SpecialPowers.spawn(browser, [], async function() {
|
||||
let frame = content.document.querySelector("iframe");
|
||||
frame.remove();
|
||||
});
|
||||
|
||||
await promiseShown;
|
||||
await clickDoorhangerButton(MAIN_BUTTON);
|
||||
await onChanged;
|
||||
}
|
||||
);
|
||||
|
||||
let creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "1 credit card in storage");
|
||||
is(creditCards[0]["cc-name"], "User 1", "Verify the name field");
|
||||
is(creditCards[0]["cc-type"], "visa", "Verify the cc-type field");
|
||||
is(
|
||||
SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF),
|
||||
2,
|
||||
"User has seen the doorhanger"
|
||||
);
|
||||
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
|
||||
await removeAllRecords();
|
||||
});
|
||||
|
||||
add_task(async function test_submit_changed_subset_creditCard_form() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [[CREDITCARDS_USED_STATUS_PREF, 0]],
|
||||
|
@ -685,6 +738,7 @@ add_task(async function test_update_autofill_form_exp_date() {
|
|||
await openPopupOn(browser, "form #cc-name");
|
||||
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
|
||||
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
|
||||
await osKeyStoreLoginShown;
|
||||
await SpecialPowers.spawn(browser, [], async function() {
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
let form = content.document.getElementById("form");
|
||||
|
@ -693,13 +747,12 @@ add_task(async function test_update_autofill_form_exp_date() {
|
|||
}, "Credit card detail never fills");
|
||||
let form = content.document.getElementById("form");
|
||||
let year = form.querySelector("#cc-exp-year");
|
||||
year.setUserInput("2020");
|
||||
year.setUserInput("2019");
|
||||
|
||||
// Wait 1000ms before submission to make sure the input value applied
|
||||
await new Promise(resolve => content.setTimeout(resolve, 1000));
|
||||
form.querySelector("input[type=submit]").click();
|
||||
});
|
||||
|
||||
await promiseShown;
|
||||
await clickDoorhangerButton(MAIN_BUTTON);
|
||||
await osKeyStoreLoginShown;
|
||||
|
@ -709,7 +762,7 @@ add_task(async function test_update_autofill_form_exp_date() {
|
|||
|
||||
creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "Still 1 credit card");
|
||||
is(creditCards[0]["cc-exp-year"], "2020", "cc-exp-year field is updated");
|
||||
is(creditCards[0]["cc-exp-year"], "2019", "cc-exp-year field is updated");
|
||||
is(
|
||||
creditCards[0]["cc-number"],
|
||||
"************1111",
|
||||
|
|
|
@ -312,16 +312,20 @@ async function waitForPopupEnabled(browser) {
|
|||
async function openPopupOn(browser, selector) {
|
||||
await SimpleTest.promiseFocus(browser);
|
||||
await focusAndWaitForFieldsIdentified(browser, selector);
|
||||
info("openPopupOn: before VK_DOWN");
|
||||
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
|
||||
if (!selector.includes("cc-")) {
|
||||
info(`openPopupOn: before VK_DOWN on ${selector}`);
|
||||
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
|
||||
}
|
||||
await expectPopupOpen(browser);
|
||||
}
|
||||
|
||||
async function openPopupForSubframe(browser, frameBrowsingContext, selector) {
|
||||
await SimpleTest.promiseFocus(browser);
|
||||
await focusAndWaitForFieldsIdentified(frameBrowsingContext, selector);
|
||||
info("openPopupForSubframe: before VK_DOWN");
|
||||
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, frameBrowsingContext);
|
||||
if (!selector.includes("cc-")) {
|
||||
info(`openPopupForSubframe: before VK_DOWN on ${selector}`);
|
||||
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, frameBrowsingContext);
|
||||
}
|
||||
await expectPopupOpen(browser);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче