Bug 1189428 - Add edit options to the password manager context menu. r=rittme,rchtara
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Fri, 07 Aug 2015 19:54:26 -0700
changeset 256939 3f90176ca5ca198b7dafcfc21ff60854d142500c
parent 256938 023218047be543bafd11bed9c14c4f65ddb28d9c
child 256940 b236dcce4e2b29f74d330a727ddd81bb19dec4cd
push id29195
push userphilringnalda@gmail.com
push dateSun, 09 Aug 2015 01:37:55 +0000
treeherdermozilla-central@fd63d8ed9d2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrittme, rchtara
bugs1189428
milestone42.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 1189428 - Add edit options to the password manager context menu. r=rittme,rchtara
toolkit/components/passwordmgr/content/passwordManager.js
toolkit/components/passwordmgr/content/passwordManager.xul
toolkit/components/passwordmgr/test/browser/browser.ini
toolkit/components/passwordmgr/test/browser/browser_passwordmgr_contextmenu.js
toolkit/components/passwordmgr/test/browser/browser_passwordmgrcopypwd.js
toolkit/locales/en-US/chrome/passwordmgr/passwordManager.dtd
--- a/toolkit/components/passwordmgr/content/passwordManager.js
+++ b/toolkit/components/passwordmgr/content/passwordManager.js
@@ -406,26 +406,54 @@ function CopyUsername() {
   var clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"].
                   getService(Components.interfaces.nsIClipboardHelper);
   var row = document.getElementById("signonsTree").currentIndex;
   var username = signonsTreeView.getCellText(row, {id : "userCol" });
   clipboard.copyString(username);
   Services.telemetry.getHistogramById("PWMGR_MANAGE_COPIED_USERNAME").add(1);
 }
 
-function UpdateCopyPassword() {
-  var singleSelection = (signonsTreeView.selection.count == 1);
-  var passwordMenuitem = document.getElementById("context-copypassword");
-  var usernameMenuitem = document.getElementById("context-copyusername");
-  if (singleSelection) {
-    usernameMenuitem.removeAttribute("disabled");
-    passwordMenuitem.removeAttribute("disabled");
+function EditCellInSelectedRow(columnName) {
+  let row = signonsTree.currentIndex;
+  let columnElement = getColumnByName(columnName);
+  signonsTree.startEditing(row, signonsTree.columns.getColumnFor(columnElement));
+}
+
+function UpdateContextMenu() {
+  let singleSelection = (signonsTreeView.selection.count == 1);
+  let menuItems = new Map();
+  let menupopup = document.getElementById("signonsTreeContextMenu");
+  for (let menuItem of menupopup.querySelectorAll("menuitem")) {
+    menuItems.set(menuItem.id, menuItem);
+  }
+
+  if (!singleSelection) {
+    for (let menuItem of menuItems.values()) {
+      menuItem.setAttribute("disabled", "true");
+    }
+    return;
+  }
+
+  let selectedRow = signonsTree.currentIndex;
+
+  // Disable "Copy Username" if the username is empty.
+  if (signonsTreeView.getCellText(selectedRow, { id: "userCol" }) != "") {
+    menuItems.get("context-copyusername").removeAttribute("disabled");
   } else {
-    usernameMenuitem.setAttribute("disabled", "true");
-    passwordMenuitem.setAttribute("disabled", "true");
+    menuItems.get("context-copyusername").setAttribute("disabled", "true");
+  }
+
+  menuItems.get("context-editusername").removeAttribute("disabled");
+  menuItems.get("context-copypassword").removeAttribute("disabled");
+
+  // Disable "Edit Password" if the password column isn't showing.
+  if (!document.getElementById("passwordCol").hidden) {
+    menuItems.get("context-editpassword").removeAttribute("disabled");
+  } else {
+    menuItems.get("context-editpassword").setAttribute("disabled", "true");
   }
 }
 
 function masterPasswordLogin(noPasswordCallback) {
   // This doesn't harm if passwords are not encrypted
   var tokendb = Components.classes["@mozilla.org/security/pk11tokendb;1"]
                     .createInstance(Components.interfaces.nsIPK11TokenDB);
   var token = tokendb.getInternalKeyToken();
--- a/toolkit/components/passwordmgr/content/passwordManager.xul
+++ b/toolkit/components/passwordmgr/content/passwordManager.xul
@@ -26,25 +26,34 @@
     <key keycode="VK_ESCAPE" oncommand="window.close();"/>
     <key key="&windowClose.key;" modifiers="accel" oncommand="escapeKeyHandler();"/>
     <key key="&focusSearch1.key;" modifiers="accel" oncommand="FocusFilterBox();"/>
     <key key="&focusSearch2.key;" modifiers="accel" oncommand="FocusFilterBox();"/>
   </keyset>
 
   <popupset id="signonsTreeContextSet">
     <menupopup id="signonsTreeContextMenu"
-           onpopupshowing="UpdateCopyPassword()">
+               onpopupshowing="UpdateContextMenu()">
       <menuitem id="context-copyusername"
                 label="&copyUsernameCmd.label;"
                 accesskey="&copyUsernameCmd.accesskey;"
                 oncommand="CopyUsername()"/>
+      <menuitem id="context-editusername"
+                label="&editUsernameCmd.label;"
+                accesskey="&editUsernameCmd.accesskey;"
+                oncommand="EditCellInSelectedRow('username')"/>
+      <menuseparator/>
       <menuitem id="context-copypassword"
                 label="&copyPasswordCmd.label;"
                 accesskey="&copyPasswordCmd.accesskey;"
                 oncommand="CopyPassword()"/>
+      <menuitem id="context-editpassword"
+                label="&editPasswordCmd.label;"
+                accesskey="&editPasswordCmd.accesskey;"
+                oncommand="EditCellInSelectedRow('password')"/>
     </menupopup>
   </popupset>
 
   <!-- saved signons -->
   <vbox id="savedsignons" class="contentPane" flex="1">
     <!-- filter -->
     <hbox align="center">
       <label accesskey="&filter.accesskey;" control="filter">&filter.label;</label>
--- a/toolkit/components/passwordmgr/test/browser/browser.ini
+++ b/toolkit/components/passwordmgr/test/browser/browser.ini
@@ -7,14 +7,14 @@ support-files =
 [browser_DOMFormHasPassword.js]
 [browser_DOMInputPasswordAdded.js]
 [browser_filldoorhanger.js]
 [browser_notifications.js]
 skip-if = true # Intermittent failures: Bug 1182296, bug 1148771
 [browser_passwordmgr_editing.js]
 skip-if = os == "linux"
 [browser_context_menu.js]
+[browser_passwordmgr_contextmenu.js]
 [browser_passwordmgr_fields.js]
 [browser_passwordmgr_observers.js]
 [browser_passwordmgr_sort.js]
 [browser_passwordmgr_switchtab.js]
-[browser_passwordmgrcopypwd.js]
 [browser_passwordmgrdlg.js]
rename from toolkit/components/passwordmgr/test/browser/browser_passwordmgrcopypwd.js
rename to toolkit/components/passwordmgr/test/browser/browser_passwordmgr_contextmenu.js
--- a/toolkit/components/passwordmgr/test/browser/browser_passwordmgrcopypwd.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_passwordmgr_contextmenu.js
@@ -1,80 +1,100 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 function test() {
     waitForExplicitFinish();
 
-    let pwmgr = Cc["@mozilla.org/login-manager;1"].
-                getService(Ci.nsILoginManager);
-    pwmgr.removeAllLogins();
+    Services.logins.removeAllLogins();
 
     // Add some initial logins
     let urls = [
         "http://example.com/",
         "http://mozilla.org/",
         "http://spreadfirefox.com/",
         "https://developer.mozilla.org/",
         "http://hg.mozilla.org/"
     ];
     let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
                                                  Ci.nsILoginInfo, "init");
     let logins = [
-        new nsLoginInfo(urls[0], urls[0], null, "o", "hai", "u1", "p1"),
+        new nsLoginInfo(urls[0], urls[0], null, "", "o hai", "u1", "p1"),
         new nsLoginInfo(urls[1], urls[1], null, "ehsan", "coded", "u2", "p2"),
         new nsLoginInfo(urls[2], urls[2], null, "this", "awesome", "u3", "p3"),
         new nsLoginInfo(urls[3], urls[3], null, "array of", "logins", "u4", "p4"),
         new nsLoginInfo(urls[4], urls[4], null, "then", "i wrote the test", "u5", "p5")
     ];
-    logins.forEach(function (login) pwmgr.addLogin(login));
+    logins.forEach(function (login) Services.logins.addLogin(login));
 
     // Open the password manager dialog
     const PWMGR_DLG = "chrome://passwordmgr/content/passwordManager.xul";
     let pwmgrdlg = window.openDialog(PWMGR_DLG, "Toolkit:PasswordManager", "");
     SimpleTest.waitForFocus(doTest, pwmgrdlg);
 
     // Test if "Copy Username" and "Copy Password" works
     function doTest() {
         let doc = pwmgrdlg.document;
         let selection = doc.getElementById("signonsTree").view.selection;
         let menuitem = doc.getElementById("context-copyusername");
 
         function copyField() {
+            info("Select all");
             selection.selectAll();
-            is(isMenuitemEnabled(), false, "Copy should be disabled");
+            assertMenuitemEnabled("copyusername", false);
+            assertMenuitemEnabled("editusername", false);
+            assertMenuitemEnabled("copypassword", false);
+            assertMenuitemEnabled("editpassword", false);
 
+            info("Select the first row (with an empty username)");
             selection.select(0);
-            is(isMenuitemEnabled(), true, "Copy should be enabled");
+            assertMenuitemEnabled("copyusername", false, "empty username");
+            assertMenuitemEnabled("editusername", true);
+            assertMenuitemEnabled("copypassword", true);
+            assertMenuitemEnabled("editpassword", false, "password column hidden");
 
+            info("Clear the selection");
             selection.clearSelection();
-            is(isMenuitemEnabled(), false, "Copy should be disabled");
+            assertMenuitemEnabled("copyusername", false);
+            assertMenuitemEnabled("editusername", false);
+            assertMenuitemEnabled("copypassword", false);
+            assertMenuitemEnabled("editpassword", false);
 
+            info("Select the third row and making the password column visible");
             selection.select(2);
-            is(isMenuitemEnabled(), true, "Copy should be enabled");
+            doc.getElementById("passwordCol").hidden = false;
+            assertMenuitemEnabled("copyusername", true);
+            assertMenuitemEnabled("editusername", true);
+            assertMenuitemEnabled("copypassword", true);
+            assertMenuitemEnabled("editpassword", true, "password column visible");
             menuitem.doCommand();
         }
 
-        function isMenuitemEnabled() {
-            doc.defaultView.UpdateCopyPassword();
-            return !menuitem.getAttribute("disabled");
+        function assertMenuitemEnabled(idSuffix, expected, reason = "") {
+            doc.defaultView.UpdateContextMenu();
+            let actual = !doc.getElementById("context-" + idSuffix).getAttribute("disabled");
+            is(actual, expected, idSuffix + " should be " + (expected ? "enabled" : "disabled") +
+               (reason ? ": " + reason : ""));
         }
 
         function cleanUp() {
             Services.ww.registerNotification(function (aSubject, aTopic, aData) {
                 Services.ww.unregisterNotification(arguments.callee);
-                pwmgr.removeAllLogins();
+                Services.logins.removeAllLogins();
+                doc.getElementById("passwordCol").hidden = true;
                 finish();
             });
             pwmgrdlg.close();
         }
-        
+
         function testPassword() {
-            menuitem = doc.getElementById("context-copypassword");
             info("Testing Copy Password");
-            waitForClipboard("coded", copyField, cleanUp, cleanUp);
+            waitForClipboard("coded", function copyPassword() {
+                menuitem = doc.getElementById("context-copypassword");
+                menuitem.doCommand();
+            }, cleanUp, cleanUp);
         }
-        
+
         info("Testing Copy Username");
         waitForClipboard("ehsan", copyField, testPassword, testPassword);
     }
 }
--- a/toolkit/locales/en-US/chrome/passwordmgr/passwordManager.dtd
+++ b/toolkit/locales/en-US/chrome/passwordmgr/passwordManager.dtd
@@ -32,8 +32,14 @@
 <!ENTITY      focusSearch1.key                "f">
 <!ENTITY      focusSearch2.key                "k">
 
 <!ENTITY      copyPasswordCmd.label           "Copy Password">
 <!ENTITY      copyPasswordCmd.accesskey       "C">
 
 <!ENTITY      copyUsernameCmd.label           "Copy Username">
 <!ENTITY      copyUsernameCmd.accesskey       "U">
+
+<!ENTITY      editPasswordCmd.label           "Edit Password">
+<!ENTITY      editPasswordCmd.accesskey       "E">
+
+<!ENTITY      editUsernameCmd.label           "Edit Username">
+<!ENTITY      editUsernameCmd.accesskey       "d">