Bug 1371149 - Part 1. Show insecure field in credit card autofill dropdown instead of result when the connection is not secure. r=MattN, seanlee
MozReview-Commit-ID: APjaTedWUz9
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -22,16 +22,18 @@ Cu.import("resource://formautofill/FormA
XPCOMUtils.defineLazyModuleGetter(this, "AddressResult",
"resource://formautofill/ProfileAutoCompleteResult.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CreditCardResult",
"resource://formautofill/ProfileAutoCompleteResult.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillHandler",
"resource://formautofill/FormAutofillHandler.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormLikeFactory",
"resource://gre/modules/FormLikeFactory.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "InsecurePasswordUtils",
+ "resource://gre/modules/InsecurePasswordUtils.jsm");
const formFillController = Cc["@mozilla.org/satchel/form-fill-controller;1"]
.getService(Ci.nsIFormFillController);
// Register/unregister a constructor as a factory.
function AutocompleteFactory() {}
AutocompleteFactory.prototype = {
register(targetConstructor) {
@@ -95,17 +97,17 @@ AutofillProfileAutoCompleteSearch.protot
let savedFieldNames = FormAutofillContent.savedFieldNames;
let focusedInput = formFillController.focusedInput;
let info = FormAutofillContent.getInputDetails(focusedInput);
let isAddressField = FormAutofillUtils.isAddressField(info.fieldName);
let handler = FormAutofillContent.getFormHandler(focusedInput);
let allFieldNames = handler.allFieldNames;
- let filledRecordGUID = isAddressField ? handler.address.filledRecordGUID : handler.creditCards.filledRecordGUID;
+ let filledRecordGUID = isAddressField ? handler.address.filledRecordGUID : handler.creditCard.filledRecordGUID;
// Fallback to form-history if ...
// - no profile can fill the currently-focused input.
// - the current form has already been populated.
// - (address only) less than 3 inputs are covered by all saved fields in the storage.
if (!savedFieldNames.has(info.fieldName) || filledRecordGUID || (isAddressField &&
allFieldNames.filter(field => savedFieldNames.has(field)).length < FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD)) {
let formHistory = Cc["@mozilla.org/autocomplete/search;1?name=form-history"]
@@ -132,21 +134,23 @@ AutofillProfileAutoCompleteSearch.protot
let result = null;
if (isAddressField) {
result = new AddressResult(searchString,
info.fieldName,
allFieldNames,
adaptedRecords,
{});
} else {
+ let isSecure = InsecurePasswordUtils.isFormSecure(handler.form);
+
result = new CreditCardResult(searchString,
info.fieldName,
allFieldNames,
adaptedRecords,
- {});
+ {isSecure});
}
listener.onSearchResult(this, result);
ProfileAutocomplete.setProfileAutoCompleteResult(result);
});
},
/**
* Stops an asynchronous search that is in progress
--- a/browser/extensions/formautofill/FormAutofillUtils.jsm
+++ b/browser/extensions/formautofill/FormAutofillUtils.jsm
@@ -341,8 +341,12 @@ this.FormAutofillUtils = {
element.textContent = bundle.GetStringFromName(element.getAttribute("data-localization"));
element.removeAttribute("data-localization");
}
},
};
this.log = null;
this.FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
+
+XPCOMUtils.defineLazyGetter(FormAutofillUtils, "stringBundle", function() {
+ return Services.strings.createBundle("chrome://formautofill/locale/formautofill.properties");
+});
--- a/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
+++ b/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
@@ -3,24 +3,33 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = ["AddressResult", "CreditCardResult"]; /* exported AddressResult, CreditCardResult */
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
+XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
+ return Services.strings.createBundle("chrome://branding/locale/brand.properties");
+});
+XPCOMUtils.defineLazyPreferenceGetter(this, "insecureWarningEnabled", "security.insecure_field_warning.contextual.enabled");
+
this.log = null;
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
class ProfileAutoCompleteResult {
- constructor(searchString, focusedFieldName, allFieldNames, matchingProfiles, {resultCode = null}) {
+ constructor(searchString, focusedFieldName, allFieldNames, matchingProfiles, {
+ resultCode = null,
+ isSecure = true,
+ }) {
log.debug("Constructing new ProfileAutoCompleteResult:", [...arguments]);
// nsISupports
this.QueryInterface = XPCOMUtils.generateQI([Ci.nsIAutoCompleteResult]);
// The user's query string
this.searchString = searchString;
// The field name of the focused input.
@@ -28,16 +37,18 @@ class ProfileAutoCompleteResult {
// All field names in the form which contains the focused input.
this._allFieldNames = allFieldNames;
// The matching profiles contains the information for filling forms.
this._matchingProfiles = matchingProfiles;
// The default item that should be entered if none is selected
this.defaultIndex = 0;
// The reason the search failed
this.errorDescription = "";
+ // The value used to determine whether the form is secure or not.
+ this._isSecure = isSecure;
// The result code of this result object.
if (resultCode) {
this.searchResult = resultCode;
} else if (matchingProfiles.length > 0) {
this.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
} else {
this.searchResult = Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
@@ -142,27 +153,16 @@ class ProfileAutoCompleteResult {
removeValueAt(index, removeFromDatabase) {
// There is no plan to support removing profiles via autocomplete.
}
}
class AddressResult extends ProfileAutoCompleteResult {
constructor(...args) {
super(...args);
-
- // Add an empty result entry for footer. Its content will come from
- // the footer binding, so don't assign any value to it.
- // The additional properties: categories and focusedCategory are required of
- // the popup to generate autofill hint on the footer.
- this._popupLabels.push({
- primary: "",
- secondary: "",
- categories: FormAutofillUtils.getCategoriesFromFieldNames(this._allFieldNames),
- focusedCategory: FormAutofillUtils.getCategoryFromFieldName(this._focusedFieldName),
- });
}
_getSecondaryLabel(focusedFieldName, allFieldNames, profile) {
// We group similar fields into the same field name so we won't pick another
// field in the same group as the secondary label.
const GROUP_FIELDS = {
"name": [
"name",
@@ -228,42 +228,49 @@ class AddressResult extends ProfileAutoC
}
}
return ""; // Nothing matched.
}
_generateLabels(focusedFieldName, allFieldNames, profiles) {
// Skip results without a primary label.
- return profiles.filter(profile => {
+ let labels = profiles.filter(profile => {
return !!profile[focusedFieldName];
}).map(profile => {
let primaryLabel = profile[focusedFieldName];
if (focusedFieldName == "street-address" &&
profile["-moz-street-address-one-line"]) {
primaryLabel = profile["-moz-street-address-one-line"];
}
return {
primary: primaryLabel,
secondary: this._getSecondaryLabel(focusedFieldName,
allFieldNames,
profile),
};
});
- }
+ // Add an empty result entry for footer. Its content will come from
+ // the footer binding, so don't assign any value to it.
+ // The additional properties: categories and focusedCategory are required of
+ // the popup to generate autofill hint on the footer.
+ labels.push({
+ primary: "",
+ secondary: "",
+ categories: FormAutofillUtils.getCategoriesFromFieldNames(this._allFieldNames),
+ focusedCategory: FormAutofillUtils.getCategoryFromFieldName(this._focusedFieldName),
+ });
-
+ return labels;
+ }
}
class CreditCardResult extends ProfileAutoCompleteResult {
constructor(...args) {
super(...args);
-
- // Add an empty result entry for footer.
- this._popupLabels.push({primary: "", secondary: ""});
}
_getSecondaryLabel(focusedFieldName, allFieldNames, profile) {
const GROUP_FIELDS = {
"cc-name": [
"cc-name",
"cc-given-name",
"cc-additional-name",
@@ -302,29 +309,65 @@ class CreditCardResult extends ProfileAu
return profile[currentFieldName];
}
}
return ""; // Nothing matched.
}
_generateLabels(focusedFieldName, allFieldNames, profiles) {
+ if (!this._isSecure) {
+ if (!insecureWarningEnabled) {
+ return [];
+ }
+ let brandName = gBrandBundle.GetStringFromName("brandShortName");
+
+ return [FormAutofillUtils.stringBundle.formatStringFromName("insecureFieldWarningDescription", [brandName], 1)];
+ }
+
// Skip results without a primary label.
- return profiles.filter(profile => {
+ let labels = profiles.filter(profile => {
return !!profile[focusedFieldName];
}).map(profile => {
return {
primary: profile[focusedFieldName],
secondary: this._getSecondaryLabel(focusedFieldName,
allFieldNames,
profile),
};
});
+ // Add an empty result entry for footer.
+ labels.push({primary: "", secondary: ""});
+
+ return labels;
}
// Always return empty string for credit card result. Since the decryption might
- // be required of users' input, we have to to suppress AutoCompleteController
+ // be required of users' input, we have to suppress AutoCompleteController
// from filling encrypted data directly.
getValueAt(index) {
this._checkIndexBounds(index);
return "";
}
+
+ getLabelAt(index) {
+ this._checkIndexBounds(index);
+
+ let label = this._popupLabels[index];
+ if (typeof label == "string") {
+ return label;
+ }
+ return JSON.stringify(label);
+ }
+
+ getStyleAt(index) {
+ this._checkIndexBounds(index);
+ if (!this._isSecure && insecureWarningEnabled) {
+ return "insecureWarning";
+ }
+
+ if (index == this.matchCount - 1) {
+ return "autofill-footer";
+ }
+
+ return "autofill-profile";
+ }
}
--- a/browser/extensions/formautofill/content/formautofill.css
+++ b/browser/extensions/formautofill/content/formautofill.css
@@ -14,16 +14,20 @@
#PopupAutoComplete > richlistbox > richlistitem[originaltype="autofill-profile"] {
-moz-binding: url("chrome://formautofill/content/formautofill.xml#autocomplete-profile-listitem");
}
#PopupAutoComplete > richlistbox > richlistitem[originaltype="autofill-footer"] {
-moz-binding: url("chrome://formautofill/content/formautofill.xml#autocomplete-profile-listitem-footer");
}
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] {
+ -moz-binding: url("chrome://formautofill/content/formautofill.xml#autocomplete-creditcard-insecure-field");
+}
+
/* Treat @collpased="true" as display: none similar to how it is for XUL elements.
* https://developer.mozilla.org/en-US/docs/Web/CSS/visibility#Values */
#PopupAutoComplete > richlistbox > richlistitem[originaltype="autofill-profile"][collapsed="true"],
#PopupAutoComplete > richlistbox > richlistitem[originaltype="autofill-footer"][collapsed="true"] {
display: none;
}
#PopupAutoComplete[firstresultstyle="autofill-profile"] {
--- a/browser/extensions/formautofill/content/formautofill.xml
+++ b/browser/extensions/formautofill/content/formautofill.xml
@@ -53,16 +53,20 @@
<method name="_onOverflow">
<body></body>
</method>
<method name="_onUnderflow">
<body></body>
</method>
+ <method name="handleOverUnderflow">
+ <body></body>
+ </method>
+
<method name="_adjustAutofillItemLayout">
<body>
<![CDATA[
let outerBoxRect = this.parentNode.getBoundingClientRect();
// Make item fit in popup as XUL box could not constrain
// item's width
this._itemBox.style.width = outerBoxRect.width + "px";
@@ -271,9 +275,36 @@
this._itemBox.setAttribute("no-warning", "true");
}
]]>
</body>
</method>
</implementation>
</binding>
+ <binding id="autocomplete-creditcard-insecure-field" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-richlistitem-insecure-field">
+ <implementation implements="nsIDOMXULSelectControlItemElement">
+ <constructor>
+ <![CDATA[
+ this.setAttribute("formautofillattached", "true");
+ ]]>
+ </constructor>
+
+ <method name="_cleanup">
+ <body>
+ <![CDATA[
+ this.removeAttribute("formautofillattached");
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="click" button="0" phase="capturing"><![CDATA[
+ // Suppress any other handlers from its inheritance chain.
+ event.stopPropagation();
+
+ // Leave empty here until we're sure where to redirect for "Learn more" link
+ ]]></handler>
+ </handlers>
+ </binding>
+
</bindings>
--- a/browser/extensions/formautofill/locales/en-US/formautofill.properties
+++ b/browser/extensions/formautofill/locales/en-US/formautofill.properties
@@ -51,8 +51,10 @@ state = State
postalCode = Postal Code
zip = Zip Code
country = Country or Region
tel = Phone
email = Email
cancel = Cancel
save = Save
countryWarningMessage = Autofill is currently available only for US addresses
+
+insecureFieldWarningDescription = %S has detected an insecure site. Credit card autofill is temporarily disabled