Bug 386005: passwords deleted from drop down menu in gmail.com apear to be deleted but are still saved. r=gavin
authordolske@mozilla.com
Thu, 23 Aug 2007 12:07:20 -0700
changeset 5213 9c97cc88f7d3915b06f375fa3f5e87739365f8be
parent 5212 76d14b73fb1fc5eaa5a456b24009dc0df14ef890
child 5214 73df6a56e7ca2a0eb93afa1dfbbb4f2913f44994
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgavin
bugs386005
milestone1.9a8pre
Bug 386005: passwords deleted from drop down menu in gmail.com apear to be deleted but are still saved. r=gavin
toolkit/components/passwordmgr/src/nsLoginManager.js
toolkit/components/passwordmgr/test/Makefile.in
toolkit/components/passwordmgr/test/test_0init.html
toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html
--- a/toolkit/components/passwordmgr/src/nsLoginManager.js
+++ b/toolkit/components/passwordmgr/src/nsLoginManager.js
@@ -1236,20 +1236,20 @@ LoginManager.prototype = {
 
 
 // nsIAutoCompleteResult implementation
 function UserAutoCompleteResult (aSearchString, matchingLogins) {
     function loginSort(a,b) {
         var userA = a.username.toLowerCase();
         var userB = b.username.toLowerCase();
 
-        if (a < b)
+        if (userA < userB)
             return -1;
 
-        if (b > a)
+        if (userB > userA)
             return  1;
 
         return 0;
     };
 
     this.searchString = aSearchString;
     this.logins = matchingLogins.sort(loginSort);
     this.matchCount = matchingLogins.length;
@@ -1292,17 +1292,18 @@ UserAutoCompleteResult.prototype = {
     getImageAt : function (index) {
         return "";
     },
 
     removeValueAt : function (index, removeFromDB) {
         if (index < 0 || index >= this.logins.length)
             throw "Index out of range.";
 
-        var removedLogin = this.logins.splice(index, 1);
+        var [removedLogin] = this.logins.splice(index, 1);
+
         this.matchCount--;
         if (this.defaultIndex > this.logins.length)
             this.defaultIndex--;
 
         if (removeFromDB) {
             var pwmgr = Cc["@mozilla.org/login-manager;1"]
                             .getService(Ci.nsILoginManager);
             pwmgr.removeLogin(removedLogin);
--- a/toolkit/components/passwordmgr/test/Makefile.in
+++ b/toolkit/components/passwordmgr/test/Makefile.in
@@ -51,16 +51,17 @@ MOCHI_TESTS = \
 		pwmgr_common.js \
 		test_0init.html \
 		test_basic_form.html \
 		test_basic_form_0pw.html \
 		test_basic_form_1pw.html \
 		test_basic_form_2pw_1.html \
 		test_basic_form_2pw_2.html \
 		test_basic_form_3pw_1.html \
+		test_basic_form_autocomplete.html \
 		test_bug_227640.html \
 		test_bug_242956.html \
 		test_bug_360493_1.html \
 		test_bug_360493_2.html \
 		$(NULL)
 
 XPCSHELL_TESTS  = unit
 
--- a/toolkit/components/passwordmgr/test/test_0init.html
+++ b/toolkit/components/passwordmgr/test/test_0init.html
@@ -16,16 +16,25 @@ Login Manager test: initialization.
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Login Manager: initialization **/
 
 netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
 
 
+// Enable debug pref (for console output)
+if (false) {
+    var prefs = Components.classes["@mozilla.org/preferences-service;1"].
+                        getService(Components.interfaces.nsIPrefService);
+    prefs = prefs.getBranch("signon.");
+    prefs.setBoolPref("debug", true);
+}
+
+
 // Get the pwmgr service
 var Cc_pwmgr = Components.classes["@mozilla.org/login-manager;1"];
 ok(Cc_pwmgr != null, "Access Cc[@mozilla.org/login-manager;1]");
 
 var Ci_pwmgr = Components.interfaces.nsILoginManager;
 ok(Ci_pwmgr != null, "Access Ci.nsILoginManager");
 
 var pwmgr = Cc_pwmgr.getService(Ci_pwmgr);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html
@@ -0,0 +1,430 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Login Manager</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>  
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="text/javascript" src="pwmgr_common.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+Login Manager test: multiple login autocomplete
+<p id="display"></p>
+
+<!-- we presumably can't hide the content for this test. -->
+<div id="content">
+
+  <form id="form1" action="http://autocomplete:8888/formtest.js" onsubmit="return false;">
+    <input  type="text"       name="uname">
+    <input  type="password"   name="pword">
+
+    <button type="submit">Submit</button>
+    <button type="reset"> Reset </button>
+  </form>
+
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Login Manager: multiple login autocomplete. **/
+
+netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+var uname = $_(1, "uname");
+var pword = $_(1, "pword");
+const shiftModifier = Components.interfaces.nsIDOMNSEvent.SHIFT_MASK;
+
+// Window utils for sending fake sey events.
+var wutils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+                      getInterface(Components.interfaces.nsIDOMWindowUtils);
+
+// Get the pwmgr service
+var pwmgr = Components.classes["@mozilla.org/login-manager;1"]
+            .getService(Components.interfaces.nsILoginManager);
+ok(pwmgr != null, "nsLoginManager service");
+
+// Create some logins just for this form, since we'll be deleting them.
+var nsLoginInfo =
+Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
+                       Components.interfaces.nsILoginInfo, "init");
+ok(nsLoginInfo != null, "nsLoginInfo constructor");
+
+
+var login1 = new nsLoginInfo(
+    "http://localhost:8888", "http://autocomplete:8888", null,
+    "tempuser1", "temppass1", "uname", "pword");
+
+var login2 = new nsLoginInfo(
+    "http://localhost:8888", "http://autocomplete:8888", null,
+    "testuser2", "testpass2", "uname", "pword");
+
+var login3 = new nsLoginInfo(
+    "http://localhost:8888", "http://autocomplete:8888", null,
+    "testuser3", "testpass3", "uname", "pword");
+
+var login4 = new nsLoginInfo(
+    "http://localhost:8888", "http://autocomplete:8888", null,
+    "zzzuser4", "zzzpass4", "uname", "pword");
+
+// try/catch in case someone runs the tests manually, twice.
+try {
+    pwmgr.addLogin(login1);
+    pwmgr.addLogin(login2);
+    pwmgr.addLogin(login3);
+    pwmgr.addLogin(login4);
+} catch (e) {
+    ok(false, "addLogin threw: " + e);
+}
+
+
+// Restore the form to the default state.
+function restoreForm() {
+    uname.value = "";
+    pword.value = "";
+    uname.focus();
+}
+
+
+// Check for expected username/password in form.
+function checkForm(expectedUsername, expectedPassword) {
+  is(uname.value, expectedUsername, "Checking form username");
+  is(pword.value, expectedPassword, "Checking form password");
+}
+
+
+// Mochitest gives us a sendKey(), but it's targeted to a specific element.
+// This basically sends an untargeted key event, to whatever's focused.
+function doKey(aKey, modifier) {
+    // Seems we need to enable this again, or sendKeyEvent() complaints.
+    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+    var keyName = "DOM_VK_" + aKey.toUpperCase();
+    var key = Components.interfaces.nsIDOMKeyEvent[keyName];
+
+    // undefined --> null
+    if (!modifier)
+        modifier = null;
+
+    wutils.sendKeyEvent("keydown",  key, 0, modifier);
+    wutils.sendKeyEvent("keypress", key, 0, modifier);
+    wutils.sendKeyEvent("keyup",    key, 0, modifier);
+}
+
+
+
+/*
+ * Main section of test...
+ *
+ * This is a bit hacky, because the events are either being sent or
+ * processes asynchronously, so we need to interrupt our flow with lots of
+ * setTimeout() calls. The case statements are executed in order, one per
+ * timeout.
+ */
+function runTest(testNum) {
+  // Seems we need to enable this again, or sendKeyEvent() complaints.
+  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+  ok(true, "Starting test #" + testNum);
+
+  switch(testNum) {
+    case 1:
+        // Make sure initial form is empty.
+        checkForm("", "");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 2:
+        // Check first entry
+        doKey("down");
+        checkForm("", ""); // value shouldn't update
+        doKey("return"); // not "enter"!
+        checkForm("tempuser1", "temppass1");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 3:
+        // Check second entry
+        doKey("down");
+        doKey("down");
+        doKey("return"); // not "enter"!
+        checkForm("testuser2", "testpass2");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 4:
+        // Check third entry
+        doKey("down");
+        doKey("down");
+        doKey("down");
+        doKey("return");
+        checkForm("testuser3", "testpass3");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 5:
+        // Check fourth entry
+        doKey("down");
+        doKey("down");
+        doKey("down");
+        doKey("down");
+        doKey("return");
+        checkForm("zzzuser4", "zzzpass4");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 6:
+        // Check first entry (wraparound)
+        doKey("down");
+        doKey("down");
+        doKey("down");
+        doKey("down");
+        doKey("down"); // deselects
+        doKey("down");
+        doKey("return");
+        checkForm("tempuser1", "temppass1");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 7:
+        // Check the last entry via arrow-up
+        doKey("up");
+        doKey("return");
+        checkForm("zzzuser4", "zzzpass4");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 8:
+        // Check the last entry via arrow-up
+        doKey("down"); // select first entry
+        doKey("up");   // selects nothing!
+        doKey("up");   // select last entry
+        doKey("return");
+        checkForm("zzzuser4", "zzzpass4");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 9:
+        // Check the last entry via arrow-up (wraparound)
+        doKey("down");
+        doKey("up"); // deselects
+        doKey("up"); // last entry
+        doKey("up");
+        doKey("up");
+        doKey("up"); // first entry
+        doKey("up"); // deselects
+        doKey("up"); // last entry
+        doKey("return");
+        checkForm("zzzuser4", "zzzpass4");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 10:
+        // Set first entry w/o triggering autocomplete
+        doKey("down");
+        doKey("right");
+        checkForm("tempuser1", ""); // empty password
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 11:
+        // Set first entry w/o triggering autocomplete
+        doKey("down");
+        doKey("left");
+        checkForm("tempuser1", ""); // empty password
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 12:
+        // Check first entry (page up)
+        doKey("down");
+        doKey("down");
+        doKey("page_up");
+        doKey("return");
+        checkForm("tempuser1", "temppass1");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 13:
+        // Check last entry (page down)
+        doKey("down");
+        doKey("page_down");
+        doKey("return");
+        checkForm("zzzuser4", "zzzpass4");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        testNum = 49;
+        break;
+
+    case 14:
+        // Abort with ESC
+// XXX This isn't working in the test suite, seems to fubar any later tests.
+//        doKey("down");
+//        doKey("down");
+//        doKey("escape");
+//        checkForm("", "");
+
+        // Trigger autocomplete popup
+//        restoreForm();
+//        doKey("down");
+        break;
+
+    // XXX tried sending character "t" before/during dropdown to test
+    // filtering, but had no luck. Seemed like the character was getting lost.
+    // Setting uname.value didn't seem to work either. This works with a human
+    // driver, so I'm not sure what's up.
+
+
+    case 50:
+        // Delete the first entry (of 4), "tempuser1"
+        doKey("down");
+        // XXX Just "delete", and on Mac also shift-backspace?
+        var numLogins;
+        numLogins = pwmgr.countLogins("http://localhost:8888", "http://autocomplete:8888", null);
+        is(numLogins, 4, "Correct number of logins before deleting one");
+
+        doKey("back_space", shiftModifier);
+
+        checkForm("zzzuser4", ""); // why does a value get set here?
+        numLogins = pwmgr.countLogins("http://localhost:8888", "http://autocomplete:8888", null);
+        is(numLogins, 3, "Correct number of logins after deleting one");
+        doKey("return");
+        checkForm("testuser2", "testpass2");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 51:
+        // Check the new first entry (of 3)
+        doKey("down");
+        doKey("return");
+        checkForm("testuser2", "testpass2");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 52:
+        // Delete the second entry (of 3), "testuser3"
+        doKey("down");
+        doKey("down");
+        doKey("back_space", shiftModifier);
+        checkForm("testuser2", ""); // XXX why does a value get set here?
+        numLogins = pwmgr.countLogins("http://localhost:8888", "http://autocomplete:8888", null);
+        is(numLogins, 2, "Correct number of logins after deleting one");
+        doKey("return");
+        checkForm("zzzuser4", "zzzpass4");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 53:
+        // Check the new second entry (of 2)
+        doKey("down");
+        doKey("return");
+        checkForm("testuser2", "testpass2");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 54:
+        // Delete the last entry (of 2), "zzzuser4"
+        doKey("down");
+        doKey("down");
+        doKey("back_space", shiftModifier);
+        checkForm("testuser2", ""); // XXX why does a value get set here?
+        numLogins = pwmgr.countLogins("http://localhost:8888", "http://autocomplete:8888", null);
+        is(numLogins, 1, "Correct number of logins after deleting one");
+        doKey("return");
+        checkForm("testuser2", "testpass2");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 55:
+        // Check the new second entry (of 2)
+        doKey("down");
+        doKey("return");
+        checkForm("testuser2", "testpass2");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 56:
+        // Delete the only remaining entry, "testuser2"
+        doKey("down");
+        doKey("back_space", shiftModifier);
+        //doKey("return");
+        checkForm("", "");
+        numLogins = pwmgr.countLogins("http://localhost:8888", "http://autocomplete:8888", null);
+        is(numLogins, 0, "Correct number of logins after deleting one");
+
+    default:
+        SimpleTest.finish();
+        return;
+  }
+
+  setTimeout(runTest, 50, testNum + 1); // XXX 40ms was too slow, why?
+}
+
+
+function startTest() {
+    runTest(1);
+}
+
+window.onload = startTest;
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+