--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -77,16 +77,17 @@ FormAutofillParent.prototype = {
/**
* Initializes ProfileStorage and registers the message handler.
*/
async init() {
Services.obs.addObserver(this, "advanced-pane-loaded");
Services.ppmm.addMessageListener("FormAutofill:InitStorage", this);
Services.ppmm.addMessageListener("FormAutofill:GetRecords", this);
Services.ppmm.addMessageListener("FormAutofill:SaveAddress", this);
+ Services.ppmm.addMessageListener("FormAutofill:SaveCreditCard", this);
Services.ppmm.addMessageListener("FormAutofill:RemoveAddresses", this);
Services.ppmm.addMessageListener("FormAutofill:OpenPreferences", this);
Services.mm.addMessageListener("FormAutofill:OnFormSubmit", this);
// Observing the pref and storage changes
Services.prefs.addObserver(ENABLED_PREF, this);
Services.obs.addObserver(this, "formautofill-storage-changed");
},
@@ -188,16 +189,20 @@ FormAutofillParent.prototype = {
case "FormAutofill:SaveAddress": {
if (data.guid) {
this.profileStorage.addresses.update(data.guid, data.address);
} else {
this.profileStorage.addresses.add(data.address);
}
break;
}
+ case "FormAutofill:SaveCreditCard": {
+ this.profileStorage.creditCards.add(data.creditcard);
+ break;
+ }
case "FormAutofill:RemoveAddresses": {
data.guids.forEach(guid => this.profileStorage.addresses.remove(guid));
break;
}
case "FormAutofill:OnFormSubmit": {
this._onFormSubmit(data, target);
break;
}
@@ -214,16 +219,17 @@ FormAutofillParent.prototype = {
* @private
*/
_uninit() {
this.profileStorage._saveImmediately();
Services.ppmm.removeMessageListener("FormAutofill:InitStorage", this);
Services.ppmm.removeMessageListener("FormAutofill:GetRecords", this);
Services.ppmm.removeMessageListener("FormAutofill:SaveAddress", this);
+ Services.ppmm.removeMessageListener("FormAutofill:SaveCreditCard", this);
Services.ppmm.removeMessageListener("FormAutofill:RemoveAddresses", this);
Services.obs.removeObserver(this, "advanced-pane-loaded");
Services.prefs.removeObserver(ENABLED_PREF, this);
},
/**
* Get the records from profile store and return results back to content
* process.
--- a/browser/extensions/formautofill/test/browser/browser.ini
+++ b/browser/extensions/formautofill/test/browser/browser.ini
@@ -1,16 +1,18 @@
[DEFAULT]
head = head.js
support-files =
../fixtures/autocomplete_basic.html
+ ../fixtures/autocomplete_creditcard_basic.html
[browser_autocomplete_footer.js]
[browser_autocomplete_marked_back_forward.js]
[browser_autocomplete_marked_detached_tab.js]
[browser_check_installed.js]
[browser_editAddressDialog.js]
[browser_first_time_use_doorhanger.js]
+[browser_insecure_form.js]
+[browser_manageAddressesDialog.js]
[browser_privacyPreferences.js]
-[browser_manageAddressesDialog.js]
[browser_submission_in_private_mode.js]
[browser_update_doorhanger.js]
--- a/browser/extensions/formautofill/test/browser/browser_autocomplete_footer.js
+++ b/browser/extensions/formautofill/test/browser/browser_autocomplete_footer.js
@@ -16,72 +16,60 @@ async function expectWarningText(browser
add_task(async function setup_storage() {
await saveAddress(TEST_ADDRESS_1);
await saveAddress(TEST_ADDRESS_2);
await saveAddress(TEST_ADDRESS_3);
});
add_task(async function test_click_on_footer() {
await BrowserTestUtils.withNewTab({gBrowser, url: URL}, async function(browser) {
- const {autoCompletePopup, autoCompletePopup: {richlistbox: itemsBox}} = browser;
+ const {autoCompletePopup: {richlistbox: itemsBox}} = browser;
await openPopupOn(browser, "#organization");
// Click on the footer
const optionButton = itemsBox.querySelector(".autocomplete-richlistitem:last-child")._optionButton;
const prefTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, PRIVACY_PREF_URL);
await EventUtils.synthesizeMouseAtCenter(optionButton, {});
await BrowserTestUtils.removeTab(await prefTabPromise);
ok(true, "Tab: preferences#privacy was successfully opened by clicking on the footer");
- // Ensure the popup is closed before entering the next test.
- await ContentTask.spawn(browser, {}, async function() {
- content.document.getElementById("organization").blur();
- });
- await BrowserTestUtils.waitForCondition(() => !autoCompletePopup.popupOpen);
+ await closePopup(browser);
});
});
add_task(async function test_press_enter_on_footer() {
await BrowserTestUtils.withNewTab({gBrowser, url: URL}, async function(browser) {
- const {autoCompletePopup, autoCompletePopup: {richlistbox: itemsBox}} = browser;
+ const {autoCompletePopup: {richlistbox: itemsBox}} = browser;
await openPopupOn(browser, "#organization");
// Navigate to the footer and press enter.
const listItemElems = itemsBox.querySelectorAll(".autocomplete-richlistitem");
const prefTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, PRIVACY_PREF_URL);
for (let i = 0; i < listItemElems.length; i++) {
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
}
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await BrowserTestUtils.removeTab(await prefTabPromise);
ok(true, "Tab: preferences#privacy was successfully opened by pressing enter on the footer");
- // Ensure the popup is closed before entering the next test.
- await ContentTask.spawn(browser, {}, async function() {
- content.document.getElementById("organization").blur();
- });
- await BrowserTestUtils.waitForCondition(() => !autoCompletePopup.popupOpen);
+ await closePopup(browser);
});
});
add_task(async function test_phishing_warning() {
await BrowserTestUtils.withNewTab({gBrowser, url: URL}, async function(browser) {
- const {autoCompletePopup, autoCompletePopup: {richlistbox: itemsBox}} = browser;
+ const {autoCompletePopup: {richlistbox: itemsBox}} = browser;
await openPopupOn(browser, "#street-address");
const warningBox = itemsBox.querySelector(".autocomplete-richlistitem:last-child")._warningTextBox;
ok(warningBox, "Got phishing warning box");
await expectWarningText(browser, "Also autofills company, phone, email");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await expectWarningText(browser, "Also autofills company, phone, email");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await expectWarningText(browser, "Autofills address");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await expectWarningText(browser, "Also autofills company, phone, email");
- // Ensure the popup is closed before entering the next test.
- await ContentTask.spawn(browser, {}, async function() {
- content.document.getElementById("street-address").blur();
- });
- await BrowserTestUtils.waitForCondition(() => !autoCompletePopup.popupOpen);
+ await closePopup(browser);
});
});
--- a/browser/extensions/formautofill/test/browser/browser_autocomplete_marked_back_forward.js
+++ b/browser/extensions/formautofill/test/browser/browser_autocomplete_marked_back_forward.js
@@ -46,16 +46,11 @@ add_task(async function test_back_forwar
// Check after hitting forward to the second page
stoppedPromise = BrowserTestUtils.browserStopped(browser);
browser.goForward();
await stoppedPromise;
await openPopupOn(browser, "#street-address");
checkPopup(autoCompletePopup);
- // Ensure the popup is closed before entering the next test.
- await ContentTask.spawn(browser, {}, async function() {
- content.document.getElementById("street-address").blur();
- });
- await BrowserTestUtils.waitForCondition(() => !autoCompletePopup.popupOpen,
- "popup should have closed");
+ await closePopup(browser);
});
});
--- a/browser/extensions/formautofill/test/browser/browser_autocomplete_marked_detached_tab.js
+++ b/browser/extensions/formautofill/test/browser/browser_autocomplete_marked_detached_tab.js
@@ -38,17 +38,12 @@ add_task(async function test_detach_tab_
let newBrowser = newWin.gBrowser.selectedBrowser;
ok(newBrowser, "Found new <browser>");
let newAutoCompletePopup = newBrowser.autoCompletePopup;
ok(newAutoCompletePopup, "Found new autocomplete popup");
await openPopupOn(newBrowser, "#street-address");
checkPopup(newAutoCompletePopup);
- // Ensure the popup is closed before entering the next test.
- await ContentTask.spawn(newBrowser, {}, async function() {
- content.document.getElementById("street-address").blur();
- });
- await BrowserTestUtils.waitForCondition(() => !newAutoCompletePopup.popupOpen,
- "popup should have closed");
+ await closePopup(newBrowser);
await BrowserTestUtils.closeWindow(newWin);
});
});
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/browser/browser_insecure_form.js
@@ -0,0 +1,94 @@
+"use strict";
+
+const TEST_URL_PATH_CC = "://example.org/browser/browser/extensions/formautofill/test/browser/autocomplete_creditcard_basic.html";
+const TEST_URL_PATH = "://example.org/browser/browser/extensions/formautofill/test/browser/autocomplete_basic.html";
+
+add_task(async function setup_storage() {
+ await saveAddress(TEST_ADDRESS_1);
+ await saveAddress(TEST_ADDRESS_2);
+ await saveAddress(TEST_ADDRESS_3);
+
+ await saveCreditCard(TEST_CREDIT_CARD_1);
+ await saveCreditCard(TEST_CREDIT_CARD_2);
+ await saveCreditCard(TEST_CREDIT_CARD_3);
+});
+
+add_task(async function test_insecure_form() {
+ async function runTest({urlPath, protocol, focusInput, expectedType, expectedResultLength}) {
+ await BrowserTestUtils.withNewTab({gBrowser, url: protocol + urlPath}, async function(browser) {
+ await openPopupOn(browser, focusInput);
+
+ const items = getDisplayedPopupItems(browser);
+ is(items.length, expectedResultLength, `Should show correct amount of results in "${protocol}"`);
+ const firstItem = items[0];
+ is(firstItem.getAttribute("originaltype"), expectedType, `Item should attach with correct binding in "${protocol}"`);
+
+ await closePopup(browser);
+ });
+ }
+
+ const testSets = [{
+ urlPath: TEST_URL_PATH,
+ protocol: "https",
+ focusInput: "#organization",
+ expectedType: "autofill-profile",
+ expectedResultLength: 2,
+ }, {
+ urlPath: TEST_URL_PATH,
+ protocol: "http",
+ focusInput: "#organization",
+ expectedType: "autofill-profile",
+ expectedResultLength: 2,
+ }, {
+ urlPath: TEST_URL_PATH_CC,
+ protocol: "https",
+ focusInput: "#cc-name",
+ expectedType: "autofill-profile",
+ expectedResultLength: 3,
+ }, {
+ urlPath: TEST_URL_PATH_CC,
+ protocol: "http",
+ focusInput: "#cc-name",
+ expectedType: "autofill-insecureWarning", // insecure warning field
+ expectedResultLength: 1,
+ }];
+
+ await runTest(testSets[0]);
+ await runTest(testSets[1]);
+ await runTest(testSets[2]);
+ await runTest(testSets[3]);
+});
+
+add_task(async function test_click_on_insecure_warning() {
+ await BrowserTestUtils.withNewTab({gBrowser, url: "http" + TEST_URL_PATH_CC}, async function(browser) {
+ await openPopupOn(browser, "#cc-name");
+
+ const insecureItem = getDisplayedPopupItems(browser)[0];
+ await EventUtils.synthesizeMouseAtCenter(insecureItem, {});
+ // Check input's value after popup closed to ensure the completion of autofilling.
+ await expectPopupClose(browser);
+ const inputValue = await ContentTask.spawn(browser, {}, async function() {
+ return content.document.querySelector("#cc-name").value;
+ });
+ is(inputValue, "");
+
+ await closePopup(browser);
+ });
+});
+
+add_task(async function test_press_enter_on_insecure_warning() {
+ await BrowserTestUtils.withNewTab({gBrowser, url: "http" + TEST_URL_PATH_CC}, async function(browser) {
+ await openPopupOn(browser, "#cc-name");
+
+ await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
+ await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
+ // Check input's value after popup closed to ensure the completion of autofilling.
+ await expectPopupClose(browser);
+ const inputValue = await ContentTask.spawn(browser, {}, async function() {
+ return content.document.querySelector("#cc-name").value;
+ });
+ is(inputValue, "");
+
+ await closePopup(browser);
+ });
+});
--- a/browser/extensions/formautofill/test/browser/head.js
+++ b/browser/extensions/formautofill/test/browser/head.js
@@ -1,12 +1,14 @@
/* exported MANAGE_ADDRESSES_DIALOG_URL, EDIT_ADDRESS_DIALOG_URL, BASE_URL,
TEST_ADDRESS_1, TEST_ADDRESS_2, TEST_ADDRESS_3,
- sleep, expectPopupOpen, openPopupOn, clickDoorhangerButton,
- getAddresses, saveAddress, removeAddresses */
+ TEST_CREDIT_CARD_1, TEST_CREDIT_CARD_2, TEST_CREDIT_CARD_3,
+ sleep, expectPopupOpen, openPopupOn, expectPopupClose, closePopup, clickDoorhangerButton,
+ getAddresses, saveAddress, removeAddresses, saveCreditCard,
+ getDisplayedPopupItems */
"use strict";
const MANAGE_ADDRESSES_DIALOG_URL = "chrome://formautofill/content/manageAddresses.xhtml";
const EDIT_ADDRESS_DIALOG_URL = "chrome://formautofill/content/editAddress.xhtml";
const BASE_URL = "http://mochi.test:8888/browser/browser/extensions/formautofill/test/browser/";
const TEST_ADDRESS_1 = {
@@ -28,49 +30,103 @@ const TEST_ADDRESS_2 = {
country: "US",
};
const TEST_ADDRESS_3 = {
"street-address": "Other Address",
"postal-code": "12345",
};
+const TEST_CREDIT_CARD_1 = {
+ "cc-name": "John Doe",
+ "cc-number": "1234567812345678",
+ // "cc-number-encrypted": "",
+ "cc-exp-month": 4,
+ "cc-exp-year": 2017,
+};
+
+const TEST_CREDIT_CARD_2 = {
+ "cc-name": "Timothy Berners-Lee",
+ "cc-number": "1111222233334444",
+ "cc-exp-month": 12,
+ "cc-exp-year": 2022,
+};
+
+const TEST_CREDIT_CARD_3 = {
+ "cc-number": "9999888877776666",
+ "cc-exp-month": 1,
+ "cc-exp-year": 2000,
+};
+
const MAIN_BUTTON_INDEX = 0;
const SECONDARY_BUTTON_INDEX = 1;
+function getDisplayedPopupItems(browser, selector = ".autocomplete-richlistitem") {
+ const {autoCompletePopup: {richlistbox: itemsBox}} = browser;
+ const listItemElems = itemsBox.querySelectorAll(selector);
+
+ return [...listItemElems].filter(item => item.getAttribute("collapsed") != "true");
+}
+
async function sleep(ms = 500) {
await new Promise(resolve => setTimeout(resolve, ms));
}
async function expectPopupOpen(browser) {
- const {autoCompletePopup, autoCompletePopup: {richlistbox: itemsBox}} = browser;
- const listItemElems = itemsBox.querySelectorAll(".autocomplete-richlistitem");
+ const {autoCompletePopup} = browser;
+ const listItemElems = getDisplayedPopupItems(browser);
await BrowserTestUtils.waitForCondition(() => autoCompletePopup.popupOpen,
"popup should be open");
await BrowserTestUtils.waitForCondition(() => {
return [...listItemElems].every(item => {
return (item.getAttribute("originaltype") == "autofill-profile" ||
+ item.getAttribute("originaltype") == "insecureWarning" ||
item.getAttribute("originaltype") == "autofill-footer") &&
item.hasAttribute("formautofillattached");
});
}, "The popup should be a form autofill one");
}
async function openPopupOn(browser, selector) {
await SimpleTest.promiseFocus(browser);
/* eslint no-shadow: ["error", { "allow": ["selector"] }] */
- await ContentTask.spawn(browser, {selector}, async function({selector}) {
- content.document.querySelector(selector).focus();
+ const identified = await ContentTask.spawn(browser, {selector}, async function({selector}) {
+ const input = content.document.querySelector(selector);
+ const forms = content.document.getElementsByTagName("form");
+ const rootElement = [...forms].find(form => form.contains(input)) || content.document.body;
+
+ input.focus();
+ if (rootElement.hasAttribute("test-formautofill-identified")) {
+ return true;
+ }
+ rootElement.setAttribute("test-formautofill-identified", "true");
+ return false;
});
- await sleep(2000);
+ // Wait 2 seconds for identifyAutofillFields if the form hasn't been identified yet.
+ if (!identified) {
+ await sleep(2000);
+ }
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await expectPopupOpen(browser);
}
+async function expectPopupClose(browser) {
+ await BrowserTestUtils.waitForCondition(() => !browser.autoCompletePopup.popupOpen,
+ "popup should have closed");
+}
+
+async function closePopup(browser) {
+ await ContentTask.spawn(browser, {}, async function() {
+ content.document.activeElement.blur();
+ });
+
+ await expectPopupClose(browser);
+}
+
function getRecords(data) {
return new Promise(resolve => {
Services.cpmm.addMessageListener("FormAutofill:Records", function getResult(result) {
Services.cpmm.removeMessageListener("FormAutofill:Records", getResult);
resolve(result.data);
});
Services.cpmm.sendAsyncMessage("FormAutofill:GetRecords", data);
});
@@ -80,16 +136,20 @@ function getAddresses() {
return getRecords({collectionName: "addresses"});
}
function saveAddress(address) {
Services.cpmm.sendAsyncMessage("FormAutofill:SaveAddress", {address});
return TestUtils.topicObserved("formautofill-storage-changed");
}
+function saveCreditCard(creditcard) {
+ Services.cpmm.sendAsyncMessage("FormAutofill:SaveCreditCard", {creditcard});
+ return TestUtils.topicObserved("formautofill-storage-changed");
+}
function removeAddresses(guids) {
Services.cpmm.sendAsyncMessage("FormAutofill:RemoveAddresses", {guids});
return TestUtils.topicObserved("formautofill-storage-changed");
}
/**
* Clicks the popup notification button and wait for popup hidden.
*
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/fixtures/autocomplete_creditcard_basic.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Form Autofill Credit Card Demo Page</title>
+</head>
+<body>
+ <h1>Form Autofill Credit Card Demo Page</h1>
+ <form id="form">
+ <p><label>Name: <input id="cc-name" autocomplete="cc-name"></label></p>
+ <p><label>Card Number: <input id="cc-number" autocomplete="cc-number"></label></p>
+ <p><label>Expiration month: <input id="cc-exp-month" autocomplete="cc-exp-month"></label></p>
+ <p><label>Expiration year: <input id="cc-exp-year" autocomplete="cc-exp-year"></label></p>
+ <p><label>CSC: <input id="cc-csc" autocomplete="cc-csc"></label></p>
+ <p>
+ <input type="submit" value="Submit">
+ <button type="reset">Reset</button>
+ </p>
+ </form>
+</body>
+</html>