Bug 1548381 - Password generation autocomplete UI. r=sfoster
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Tue, 21 May 2019 00:24:20 +0000
changeset 474668 2a70e4e43c5ef18bfe4727339b656e5abff20afe
parent 474667 e92a0032aa2af1bdf34a30dc72860ad8a3b2108a
child 474669 34bbe924602e6a465a34498f6e6e2f3f767ccddc
push id36043
push userrmaries@mozilla.com
push dateTue, 21 May 2019 09:44:47 +0000
treeherdermozilla-central@b74e5737da64 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfoster
bugs1548381
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1548381 - Password generation autocomplete UI. r=sfoster Differential Revision: https://phabricator.services.mozilla.com/D31211
browser/themes/shared/autocomplete.inc.css
toolkit/components/passwordmgr/LoginAutoCompleteResult.jsm
toolkit/components/passwordmgr/test/unit/test_login_autocomplete_result.js
toolkit/content/widgets/autocomplete-popup.js
toolkit/content/widgets/autocomplete.xml
--- a/browser/themes/shared/autocomplete.inc.css
+++ b/browser/themes/shared/autocomplete.inc.css
@@ -66,35 +66,45 @@
   white-space: nowrap;
 }
 
 #PopupAutoComplete > richlistbox > richlistitem > .two-line-wrapper > .labels-wrapper > .line2-label {
   padding-top: 2px !important;
   opacity: .6;
 }
 
-/* Login form autocompletion with and without origin showing */
+/* Login form autocompletion (with and without origin showing) and generated passwords */
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="generatedPassword"] > .two-line-wrapper > .ac-site-icon,
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"] > .two-line-wrapper > .ac-site-icon,
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="login"] > .ac-site-icon {
   display: initial;
   list-style-image: url(chrome://browser/skin/login.svg);
   -moz-context-properties: fill;
   fill: GrayText;
 }
 
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="generatedPassword"][selected] > .two-line-wrapper > .ac-site-icon,
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"][selected] > .two-line-wrapper > .ac-site-icon,
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="login"] > .ac-site-icon[selected] {
   fill: HighlightText;
 }
 
-/* Login form autocompletion with origin showing */
+/* Login form autocompletion with origin showing and generated passwords */
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="generatedPassword"],
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"] {
   padding: 4px;
 }
 
+
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="login"] + richlistitem[originaltype="generatedPassword"],
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"] + richlistitem[originaltype="generatedPassword"] {
+  /* Separator between logins and generated passwords */
+  border-top: 1px solid var(--panel-separator-color);
+}
+
 /* Insecure field warning */
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] {
   background-color: var(--arrowpanel-dimmed);
   border-bottom: 1px solid var(--panel-separator-color);
   padding-bottom: 4px;
   padding-top: 4px;
 }
 
--- a/toolkit/components/passwordmgr/LoginAutoCompleteResult.jsm
+++ b/toolkit/components/passwordmgr/LoginAutoCompleteResult.jsm
@@ -142,16 +142,17 @@ class LoginAutocompleteItem extends Auto
       Services.logins.removeLogin(this._login);
     }
   }
 }
 
 class GeneratedPasswordAutocompleteItem extends AutocompleteItem {
   constructor(generatedPassword) {
     super("generatedPassword");
+    this.comment = generatedPassword;
     this.value = generatedPassword;
 
     XPCOMUtils.defineLazyGetter(this, "label", () => {
       return getLocalizedString("useGeneratedPassword");
     });
   }
 }
 
--- a/toolkit/components/passwordmgr/test/unit/test_login_autocomplete_result.js
+++ b/toolkit/components/passwordmgr/test/unit/test_login_autocomplete_result.js
@@ -68,32 +68,34 @@ let expectedResults = [
     insecureFieldWarningEnabled: true,
     isSecure: false,
     isPasswordField: false,
     matchingLogins: [],
     items: [{
       value: "",
       label: "This connection is not secure. Logins entered here could be compromised. Learn More",
       style: "insecureWarning",
+      comment: "",
     }, {
       value: "",
       label: "View Saved Logins",
       style: "loginsFooter",
       comment: "mochi.test",
     }],
   },
   {
     insecureFieldWarningEnabled: true,
     isSecure: false,
     isPasswordField: false,
     matchingLogins,
     items: [{
       value: "",
       label: "This connection is not secure. Logins entered here could be compromised. Learn More",
       style: "insecureWarning",
+      comment: "",
     }, {
       value: "",
       label: LABEL_NO_USERNAME,
       style: "loginWithOrigin",
       comment: "mochi.test:8888",
     }, {
       value: "tempuser1",
       label: "tempuser1",
@@ -162,16 +164,17 @@ let expectedResults = [
     insecureFieldWarningEnabled: true,
     isSecure: false,
     isPasswordField: true,
     matchingLogins,
     items: [{
       value: "",
       label: "This connection is not secure. Logins entered here could be compromised. Learn More",
       style: "insecureWarning",
+      comment: "",
     }, {
       value: "emptypass1",
       label: LABEL_NO_USERNAME,
       style: "loginWithOrigin",
       comment: "mochi.test:8888",
     }, {
       value: "temppass1",
       label: "tempuser1",
@@ -388,16 +391,17 @@ let expectedResults = [
     insecureFieldWarningEnabled: true,
     isSecure: false,
     isPasswordField: false,
     matchingLogins,
     items: [{
       value: "",
       label: "This connection is not secure. Logins entered here could be compromised. Learn More",
       style: "insecureWarning",
+      comment: "",
     }, {
       value: "",
       label: LABEL_NO_USERNAME,
       style: "loginWithOrigin",
       comment: "mochi.test:8888",
     }, {
       value: "tempuser1",
       label: "tempuser1",
@@ -466,16 +470,17 @@ let expectedResults = [
     insecureFieldWarningEnabled: true,
     isSecure: false,
     isPasswordField: true,
     matchingLogins,
     items: [{
       value: "",
       label: "This connection is not secure. Logins entered here could be compromised. Learn More",
       style: "insecureWarning",
+      comment: "",
     }, {
       value: "emptypass1",
       label: LABEL_NO_USERNAME,
       style: "loginWithOrigin",
       comment: "mochi.test:8888",
     }, {
       value: "temppass1",
       label: "tempuser1",
@@ -701,16 +706,17 @@ let expectedResults = [
     insecureFieldWarningEnabled: true,
     isSecure: true,
     isPasswordField: true,
     matchingLogins: [],
     items: [{
       value: "9ljgfd4shyktb45",
       label: "Use Generated Password",
       style: "generatedPassword",
+      comment: "9ljgfd4shyktb45",
     }, {
       value: "",
       label: "View Saved Logins",
       style: "loginsFooter",
       comment: "mochi.test",
     }],
   },
   {
@@ -739,17 +745,17 @@ add_task(async function test_all_pattern
       isSecure: pattern.isSecure,
       isPasswordField: pattern.isPasswordField,
     });
     equal(actual.matchCount, pattern.items.length, "Check matching row count");
     pattern.items.forEach((item, index) => {
       equal(actual.getValueAt(index), item.value, `Value ${index}`);
       equal(actual.getLabelAt(index), item.label, `Label ${index}`);
       equal(actual.getStyleAt(index), item.style, `Style ${index}`);
-      equal(actual.getCommentAt(index), item.comment || "", `Comment ${index}`);
+      equal(actual.getCommentAt(index), item.comment, `Comment ${index}`);
     });
 
     if (pattern.items.length != 0) {
       Assert.throws(() => actual.getValueAt(pattern.items.length),
                     /Index out of range\./);
 
       Assert.throws(() => actual.getLabelAt(pattern.items.length),
                     /Index out of range\./);
--- a/toolkit/content/widgets/autocomplete-popup.js
+++ b/toolkit/content/widgets/autocomplete-popup.js
@@ -372,16 +372,17 @@ MozElements.MozAutocompleteRichlistboxPo
 
         // The styles on the list which have different <content> structure and overrided
         // _adjustAcItem() are unreusable.
         const UNREUSEABLE_STYLES = [
           "autofill-profile",
           "autofill-footer",
           "autofill-clear-button",
           "autofill-insecureWarning",
+          "generatedPassword",
           "insecureWarning",
           "loginsFooter",
           "loginWithOrigin",
         ];
         // Reuse the item when its style is exactly equal to the previous style or
         // neither of their style are in the UNREUSEABLE_STYLES.
         reusable = originalType === style ||
           !(UNREUSEABLE_STYLES.includes(style) || UNREUSEABLE_STYLES.includes(originalType));
@@ -404,16 +405,17 @@ MozElements.MozAutocompleteRichlistboxPo
             options = { is: "autocomplete-creditcard-insecure-field" };
             break;
           case "insecureWarning":
             options = { is: "autocomplete-richlistitem-insecure-warning" };
             break;
           case "loginsFooter":
             options = { is: "autocomplete-richlistitem-logins-footer" };
             break;
+          case "generatedPassword":
           case "loginWithOrigin":
             options = { is: "autocomplete-two-line-richlistitem" };
             break;
           default:
             options = { is: "autocomplete-richlistitem" };
         }
         item = document.createXULElement("richlistitem", options);
         item.className = "autocomplete-richlistitem";
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -1010,16 +1010,17 @@
 
               // The styles on the list which have different <content> structure and overrided
               // _adjustAcItem() are unreusable.
               const UNREUSEABLE_STYLES = [
                 "autofill-profile",
                 "autofill-footer",
                 "autofill-clear-button",
                 "autofill-insecureWarning",
+                "generatedPassword",
                 "insecureWarning",
                 "loginsFooter",
                 "loginWithOrigin",
               ];
               // Reuse the item when its style is exactly equal to the previous style or
               // neither of their style are in the UNREUSEABLE_STYLES.
               reusable = originalType === style ||
                 !(UNREUSEABLE_STYLES.includes(style) || UNREUSEABLE_STYLES.includes(originalType));
@@ -1042,16 +1043,17 @@
                   options = { is: "autocomplete-creditcard-insecure-field" };
                   break;
                 case "insecureWarning":
                   options = { is: "autocomplete-richlistitem-insecure-warning" };
                   break;
                 case "loginsFooter":
                   options = { is: "autocomplete-richlistitem-logins-footer" };
                   break;
+                case "generatedPassword":
                 case "loginWithOrigin":
                   options = { is: "autocomplete-two-line-richlistitem" };
                   break;
                 default:
                   options = { is: "autocomplete-richlistitem" };
               }
               item = document.createXULElement("richlistitem", options);
               item.className = "autocomplete-richlistitem";