Bug 521467 - Automatically log in to proxy. r=dolske, r=zpao
authorBen Bucksch <ben.bucksch@beonex.com>
Tue, 06 Apr 2010 15:28:00 -0700
changeset 40578 8c6006adbb00f7206602abb5cc4170d9045eebc3
parent 40577 dae132705f35b3a3ad6dee7dc9381360385bc8c8
child 40579 f5dcc84e887c9ec91972a0ba32c7c6a78f91d8a8
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)
reviewersdolske, zpao
bugs521467
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 521467 - Automatically log in to proxy. r=dolske, r=zpao
modules/libpref/src/init/all.js
toolkit/components/passwordmgr/src/nsLoginManagerPrompter.js
toolkit/components/passwordmgr/test/test_prompt.html
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -2807,18 +2807,19 @@ pref("print.print_command", "lp -c -s ${
 # Solaris
 #endif
 
 // Login Manager prefs
 pref("signon.rememberSignons",              true);
 pref("signon.SignonFileName",               "signons.txt"); // obsolete 
 pref("signon.SignonFileName2",              "signons2.txt"); // obsolete
 pref("signon.SignonFileName3",              "signons3.txt"); // obsolete
-pref("signon.autofillForms",                true); 
-pref("signon.debug",                        false); // logs to Error Console
+pref("signon.autofillForms",                true);
+pref("signon.autologin.proxy",              false);
+pref("signon.debug",                        false);
 
 // Satchel (Form Manager) prefs
 pref("browser.formfill.debug",            false);
 pref("browser.formfill.enable",           true);
 pref("browser.formfill.agedWeight",       2);
 pref("browser.formfill.bucketSize",       1);
 pref("browser.formfill.maxTimeGroupings", 25);
 pref("browser.formfill.timeGroupingSize", 604800);
--- a/toolkit/components/passwordmgr/src/nsLoginManagerPrompter.js
+++ b/toolkit/components/passwordmgr/src/nsLoginManagerPrompter.js
@@ -523,16 +523,17 @@ LoginManagerPrompter.prototype = {
      * int        aLevel
      * nsIAuthInformation aAuthInfo
      */
     promptAuth : function (aChannel, aLevel, aAuthInfo) {
         var selectedLogin = null;
         var checkbox = { value : false };
         var checkboxLabel = null;
         var epicfail = false;
+        var canAutologin = false;
 
         try {
 
             this.log("===== promptAuth called =====");
 
             // If the user submits a login but it fails, we need to remove the
             // notification bar that was displayed. Conveniently, the user will
             // be prompted for authentication again, which brings us here.
@@ -548,16 +549,27 @@ LoginManagerPrompter.prototype = {
                                         hostname, null, httpRealm);
             this.log("found " + foundLogins.length + " matching logins.");
 
             // XXX Can't select from multiple accounts yet. (bug 227632)
             if (foundLogins.length > 0) {
                 selectedLogin = foundLogins[0];
                 this._SetAuthInfo(aAuthInfo, selectedLogin.username,
                                              selectedLogin.password);
+
+                // Allow automatic proxy login
+                if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY &&
+                    !(aAuthInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED) &&
+                    Services.prefs.getBoolPref("signon.autologin.proxy") &&
+                    !this._inPrivateBrowsing) {
+
+                    this.log("Autologin enabled, skipping auth prompt.");
+                    canAutologin = true;
+                }
+
                 checkbox.value = true;
             }
 
             var canRememberLogin = this._pwmgr.getLoginSavingEnabled(hostname);
             if (this._inPrivateBrowsing)
               canRememberLogin = false;
         
             // if checkboxLabel is null, the checkbox won't be shown at all.
@@ -565,18 +577,20 @@ LoginManagerPrompter.prototype = {
                 checkboxLabel = this._getLocalizedString("rememberPassword");
         } catch (e) {
             // Ignore any errors and display the prompt anyway.
             epicfail = true;
             Components.utils.reportError("LoginManagerPrompter: " +
                 "Epic fail in promptAuth: " + e + "\n");
         }
 
-        var ok = this._promptService.promptAuth(this._window, aChannel,
-                                aLevel, aAuthInfo, checkboxLabel, checkbox);
+        var ok = canAutologin ||
+                 this._promptService.promptAuth(this._window,
+                                                aChannel, aLevel, aAuthInfo,
+                                                checkboxLabel, checkbox);
 
         // If there's a notification box, use it to allow the user to
         // determine if the login should be saved. If there isn't a
         // notification box, only save the login if the user set the
         // checkbox to do so.
         var rememberLogin = notifyBox ? canRememberLogin : checkbox.value;
         if (!ok || !rememberLogin || epicfail)
             return ok;
--- a/toolkit/components/passwordmgr/test/test_prompt.html
+++ b/toolkit/components/passwordmgr/test/test_prompt.html
@@ -18,21 +18,32 @@ Login Manager test: username/password pr
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Login Manager: username / password prompts. **/
 netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
 
-var pwmgr, tmplogin, login1, login2A, login2B, login2C, login2D, login2E, login3A, login3B, login4;
+var pwmgr, ioService
+var tmplogin, login1, login2A, login2B, login2C, login2D, login2E, login3A, login3B, login4, proxyLogin;
+var mozproxy, proxiedHost = "http://mochi.test:8888";
 
 function initLogins() {
   pwmgr = Cc["@mozilla.org/login-manager;1"].
           getService(Ci.nsILoginManager);
+  ioService = Cc["@mozilla.org/network/io-service;1"].
+              getService(Ci.nsIIOService);
+
+  // Figure out what our proxy is set to -- can't just hardcode this, because
+  // mobile platforms don't use localhost.
+  var pps = Cc["@mozilla.org/network/protocol-proxy-service;1"].getService();
+  var uri = ioService.newURI(proxiedHost, null, null);
+  var pi = pps.resolve(uri, 0);
+  mozproxy = "moz-proxy://" + pi.host + ":" + pi.port;
 
   tmpLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
              createInstance(Ci.nsILoginInfo);
 
   login1  = Cc["@mozilla.org/login-manager/loginInfo;1"].
             createInstance(Ci.nsILoginInfo);
   login2A = Cc["@mozilla.org/login-manager/loginInfo;1"].
             createInstance(Ci.nsILoginInfo);
@@ -45,16 +56,18 @@ function initLogins() {
   login2E = Cc["@mozilla.org/login-manager/loginInfo;1"].
             createInstance(Ci.nsILoginInfo);
   login3A = Cc["@mozilla.org/login-manager/loginInfo;1"].
             createInstance(Ci.nsILoginInfo);
   login3B = Cc["@mozilla.org/login-manager/loginInfo;1"].
             createInstance(Ci.nsILoginInfo);
   login4  = Cc["@mozilla.org/login-manager/loginInfo;1"].
             createInstance(Ci.nsILoginInfo);
+  proxyLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
+               createInstance(Ci.nsILoginInfo);
 
   login1.init("http://example.com", null, "http://example.com",
               "", "examplepass", "", "");
   login2A.init("http://example2.com", null, "http://example2.com",
                "user1name", "user1pass", "", "");
   login2B.init("http://example2.com", null, "http://example2.com",
                "user2name", "user2pass", "", "");
   login2C.init("http://example2.com", null, "http://example2.com",
@@ -64,39 +77,43 @@ function initLogins() {
   login2E.init("http://example2.com", null, "http://example2.com",
                "100%beef", "user3pass", "", "");
   login3A.init("http://mochi.test:8888", null, "mochitest",
                "mochiuser1", "mochipass1", "", "");
   login3B.init("http://mochi.test:8888", null, "mochitest2",
                "mochiuser2", "mochipass2", "", "");
   login4.init("http://mochi.test:8888", null, "mochitest3",
                "mochiuser3", "mochipass3-old", "", "");
+  proxyLogin.init(mozproxy, null, "Proxy Realm",
+                  "proxuser", "proxpass", "", "");
 
   pwmgr.addLogin(login1);
   pwmgr.addLogin(login2A);
   pwmgr.addLogin(login2B);
   pwmgr.addLogin(login2C);
   pwmgr.addLogin(login2D);
   pwmgr.addLogin(login2E);
   pwmgr.addLogin(login3A);
   pwmgr.addLogin(login3B);
   pwmgr.addLogin(login4);
+  pwmgr.addLogin(proxyLogin);
 }
 
 function finishTest() {
   ok(true, "finishTest removing testing logins...");
   pwmgr.removeLogin(login1);
   pwmgr.removeLogin(login2A);
   pwmgr.removeLogin(login2B);
   pwmgr.removeLogin(login2C);
   pwmgr.removeLogin(login2D);
   pwmgr.removeLogin(login2E);
   pwmgr.removeLogin(login3A);
   pwmgr.removeLogin(login3B);
   pwmgr.removeLogin(login4);
+  pwmgr.removeLogin(proxyLogin);
 
   SimpleTest.finish();
 }
 
 /*
  * handleDialog
  *
  * Invoked a short period of time after calling startCallbackTimer(), and
@@ -260,16 +277,33 @@ function handleDialog(doc, testNum) {
         // either of the two logins might have been filled in
         ok(username == "user1name" || username == "user2name", "Checking filled username");
         ok(password == "user1pass" || password == "user2pass", "Checking filled password");
         // force to user2, and change the password back
         userfield.setAttribute("value", "user2name");
         passfield.setAttribute("value", "user2pass");
         break;
 
+    case 508:
+        is(username, "proxuser", "Checking filled username");
+        is(password, "proxpass", "Checking filled password");
+        break;
+
+    // No case 509, it's unprompted.
+
+    case 510:
+        is(username, "proxuser", "Checking filled username");
+        is(password, "proxpass", "Checking filled password");
+        break;
+
+    case 511:
+        is(username, "proxuser", "Checking filled username");
+        is(password, "proxpass", "Checking filled password");
+        break;
+
     case 1000:
         is(username, "mochiuser1", "Checking filled username");
         is(password, "mochipass1", "Checking filled password");
         break;
 
     case 1001:
         is(username, "mochiuser2", "Checking filled username");
         is(password, "mochipass2", "Checking filled password");
@@ -344,24 +378,22 @@ function handleLoad() {
     case 1001:
         testNum++;
         is(authok, "PASS", "Checking for successful authentication");
         is(username, "mochiuser2", "Checking for echoed username");
         is(password, "mochipass2", "Checking for echoed password");
         // Now make a load that requests the realm from test 1000. It was
         // already provided there, so auth will *not* be prompted for -- the
         // networking layer already knows it!
-        didDialog = false; // [normally reset by startCallbackTimer()]
         iframe.src = "authenticate.sjs?user=mochiuser1&pass=mochipass1";
         break;
 
 
     case 1002:
         testNum++;
-        ok(!didDialog, "handleDialog was NOT invoked");
         is(authok, "PASS", "Checking for successful authentication");
         is(username, "mochiuser1", "Checking for echoed username");
         is(password, "mochipass1", "Checking for echoed password");
 
         // Same realm we've already authenticated to, but with a different
         // expected password (to trigger an auth prompt, and change-password
         // notification bar).
         startCallbackTimer();
@@ -436,26 +468,43 @@ function handleLoad() {
         break;
   }
 
 }
 
 initLogins();
 
 var authinfo = {
-    // QueryInterface : ... ?
     username : "",
     password : "",
     domain   : "",
 
     flags : Ci.nsIAuthInformation.AUTH_HOST,
     authenticationScheme : "basic",
     realm : ""
 };
 
+var proxyAuthinfo = {
+    username : "",
+    password : "",
+    domain   : "",
+
+    flags : Ci.nsIAuthInformation.AUTH_PROXY,
+    authenticationScheme : "basic",
+    realm : ""
+};
+
+var prefs = Cc["@mozilla.org/preferences-service;1"].
+            getService(Ci.nsIPrefBranch);
+var pb;
+try {
+    pb = Cc["@mozilla.org/privatebrowsing;1"].
+         getService(Ci.nsIPrivateBrowsingService);
+} catch (e) {}
+
 const Cc_promptFac= Cc["@mozilla.org/passwordmanager/authpromptfactory;1"];
 ok(Cc_promptFac != null, "Access Cc[@mozilla.org/passwordmanager/authpromptfactory;1]");
 
 const Ci_promptFac = Ci.nsIPromptFactory;
 ok(Ci_promptFac != null, "Access Ci.nsIPromptFactory");
 
 const promptFac = Cc_promptFac.getService(Ci_promptFac);
 ok(promptFac != null, "promptFac getService()");
@@ -481,54 +530,59 @@ notifyBox.removeAllNotifications(true);
 
 // ===== test 1 ===== 
 var testNum = 1;
 startCallbackTimer();
 isOk = prompter1.prompt(dialogTitle(), dialogText, "http://example.com",
                         Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, "abc", result);
 
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(result.value, "xyz", "Checking prompt() returned value");
 
 
 // ===== test 2 =====
 testNum++;
 startCallbackTimer();
 isOk = prompter1.prompt(dialogTitle(), dialogText, "http://example.com",
                         Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, "abc", result);
+ok(didDialog, "handleDialog was invoked");
 ok(!isOk, "Checking dialog return value (cancel)");
 
 
 // ===== test 10 =====
 // Default password provided, existing logins are ignored.
 testNum = 10;
 pword.value = "inputpw";
 startCallbackTimer();
 isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://example.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(pword.value, "secret", "Checking returned password");
 
 // ===== test 11 =====
 // Default password provided, existing logins are ignored.
 testNum++;
 pword.value = "inputpw";
 startCallbackTimer();
 isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://example.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
 ok(!isOk, "Checking dialog return value (cancel)");
+ok(didDialog, "handleDialog was invoked");
 
 // ===== test 12 =====
 // No default password provided, realm does not match existing login.
 testNum++;
 pword.value = null;
 startCallbackTimer();
 isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://nonexample.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(pword.value, "secret", "Checking returned password");
 
 // ===== test 13 =====
 // No default password provided, matching login is returned w/o prompting.
 testNum++;
 pword.value = null;
 isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://example.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
@@ -539,16 +593,17 @@ is(pword.value, "examplepass", "Checking
 // No default password provided, none of the logins from this host are
 // password-only so the user is prompted.
 testNum++;
 pword.value = null;
 startCallbackTimer();
 isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://example2.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(pword.value, "secret", "Checking returned password");
 
 // ===== test 15 =====
 // No default password provided, matching login is returned w/o prompting.
 testNum++;
 pword.value = null;
 isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://user1name@example2.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
@@ -598,259 +653,373 @@ is(pword.value, "user3pass", "Checking r
 // ===== test 30 =====
 // We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
 testNum = 30;
 pword.value = null;
 startCallbackTimer();
 isOk = prompter1.promptPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(pword.value, "fill2pass", "Checking returned password");
 
 // ===== test 31 =====
 // We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
 testNum++;
 pword.value = null;
 startCallbackTimer();
 isOk = prompter1.promptPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(pword.value, "fill2pass", "Checking returned password");
 
 
 // ===== test 100 =====
 testNum = 100;
 uname.value = "inuser";
 pword.value = "inpass";
 startCallbackTimer();
 isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://nonexample.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(uname.value, "outuser", "Checking returned username");
 is(pword.value, "outpass", "Checking returned password");
 
 // ===== test 101 =====
 testNum++;
 uname.value = "inuser";
 pword.value = "inpass";
 startCallbackTimer();
 isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://nonexample.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword);
 ok(!isOk, "Checking dialog return value (cancel)");
+ok(didDialog, "handleDialog was invoked");
 
 // ===== test 102 =====
 // test filling in existing password-only login
 testNum++;
 uname.value = null;
 pword.value = null;
 startCallbackTimer();
 isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://example.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(uname.value, "", "Checking returned username");
 is(pword.value, "examplepass", "Checking returned password");
 
 // ===== test 103 =====
 // test filling in existing login (undetermined from multiple selection)
 testNum++;
 uname.value = null;
 pword.value = null;
 startCallbackTimer();
 isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://example2.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 ok(uname.value == "user1name" || uname.value == "user2name", "Checking returned username");
 ok(pword.value == "user1pass" || uname.value == "user2pass", "Checking returned password");
 
 // ===== test 104 =====
 // test filling in existing login (user1 from multiple selection)
 testNum++;
 uname.value = "user1name";
 pword.value = null;
 startCallbackTimer();
 isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://example2.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(uname.value, "user1name", "Checking returned username");
 is(pword.value, "user1pass", "Checking returned password");
 
 // ===== test 105 =====
 // test filling in existing login (user2 from multiple selection)
 testNum++;
 uname.value = "user2name";
 pword.value = null;
 startCallbackTimer();
 isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://example2.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(uname.value, "user2name", "Checking returned username");
 is(pword.value, "user2pass", "Checking returned password");
 
 // ===== test 106 =====
 // test changing password
 testNum++;
 uname.value = "user2name";
 pword.value = null;
 startCallbackTimer();
 isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://example2.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(uname.value, "user2name", "Checking returned username");
 is(pword.value, "NEWuser2pass", "Checking returned password");
 
 // ===== test 107 =====
 // test changing password (back to original value)
 testNum++;
 uname.value = "user2name";
 pword.value = null;
 startCallbackTimer();
 isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://example2.com",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(uname.value, "user2name", "Checking returned username");
 is(pword.value, "user2pass", "Checking returned password");
 
 // ===== test 120 =====
 // We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
 testNum = 120;
 uname.value = null;
 pword.value = null;
 startCallbackTimer();
 isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(uname.value, "fill2user", "Checking returned username");
 is(pword.value, "fill2pass", "Checking returned password");
 
 // ===== test 121 =====
 // We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
 testNum++;
 uname.value = null;
 pword.value = null;
 startCallbackTimer();
 isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)",
                                 Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(uname.value, "fill2user", "Checking returned username");
 is(pword.value, "fill2pass", "Checking returned password");
 
 
-var channel1 = Cc["@mozilla.org/network/io-service;1"].
-              getService(Ci.nsIIOService).
-              newChannel("http://example.com", null, null);
-var channel2 = Cc["@mozilla.org/network/io-service;1"].
-              getService(Ci.nsIIOService).
-              newChannel("http://example2.com", null, null);
+var channel1 = ioService.newChannel("http://example.com", null, null);
+var channel2 = ioService.newChannel("http://example2.com", null, null);
+
+// I'm cheating a bit here... We should probably do some magic foo to get
+// something implementing nsIProxiedProtocolHandler and then call
+// NewProxiedChannel(), so we have something that's definately a proxied
+// channel. But Mochitests use a proxy for a number of hosts, so just
+// requesting a normal channel will give us a channel that's proxied.
+var proxyChannel = ioService.newChannel(proxiedHost, null, null);
+
 var level = Ci.nsIAuthPrompt2.LEVEL_NONE;
 
 
 // ===== test 500 =====
 testNum = 500;
 authinfo.username = "inuser";
 authinfo.password = "inpass";
 authinfo.realm    = "some realm";
 
 startCallbackTimer();
 isOk = prompter2.promptAuth(channel1, level, authinfo);
 
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(authinfo.username, "outuser", "Checking returned username");
 is(authinfo.password, "outpass", "Checking returned password");
 
 // ===== test 501 =====
 testNum++;
 startCallbackTimer();
 isOk = prompter2.promptAuth(channel1, level, authinfo);
 
 ok(!isOk, "Checking dialog return value (cancel)");
+ok(didDialog, "handleDialog was invoked");
 
 // ===== test 502 =====
 // test filling in password-only login
 testNum++;
 authinfo.username = "";
 authinfo.password = "";
 authinfo.realm    = "http://example.com";
 
 startCallbackTimer();
 isOk = prompter2.promptAuth(channel1, level, authinfo);
 
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(authinfo.username, "", "Checking returned username");
 is(authinfo.password, "examplepass", "Checking returned password");
 
 // ===== test 503 =====
 // test filling in existing login (undetermined from multiple selection)
 testNum++;
 authinfo.username = "";
 authinfo.password = "";
 authinfo.realm    = "http://example2.com";
 
 startCallbackTimer();
 isOk = prompter2.promptAuth(channel2, level, authinfo);
 
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 ok(authinfo.username == "user1name" || authinfo.username == "user2name", "Checking returned username");
 ok(authinfo.password == "user1pass" || authinfo.password == "user2pass", "Checking returned password");
 
 // ===== test 504 =====
 // test filling in existing login (undetermined --> user1)
 testNum++;
 authinfo.username = "";
 authinfo.password = "";
 authinfo.realm    = "http://example2.com";
 
 startCallbackTimer();
 isOk = prompter2.promptAuth(channel2, level, authinfo);
 
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(authinfo.username, "user1name", "Checking returned username");
 is(authinfo.password, "user1pass", "Checking returned password");
 
 // ===== test 505 =====
 // test filling in existing login (undetermined --> user2)
 testNum++;
 authinfo.username = "";
 authinfo.password = "";
 authinfo.realm    = "http://example2.com";
 
 startCallbackTimer();
 isOk = prompter2.promptAuth(channel2, level, authinfo);
 
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(authinfo.username, "user2name", "Checking returned username");
 is(authinfo.password, "user2pass", "Checking returned password");
 
 // ===== test 506 =====
 // test changing a password (undetermined --> user2 w/ newpass)
 testNum++;
 authinfo.username = "";
 authinfo.password = "";
 authinfo.realm    = "http://example2.com";
 
 startCallbackTimer();
 isOk = prompter2.promptAuth(channel2, level, authinfo);
 
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(authinfo.username, "user2name", "Checking returned username");
 is(authinfo.password, "NEWuser2pass", "Checking returned password");
 
 // ===== test 507 =====
 // test changing a password (undetermined --> user2 w/ origpass)
 testNum++;
 authinfo.username = "";
 authinfo.password = "";
 authinfo.realm    = "http://example2.com";
 
 startCallbackTimer();
 isOk = prompter2.promptAuth(channel2, level, authinfo);
 
 ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
 is(authinfo.username, "user2name", "Checking returned username");
 is(authinfo.password, "user2pass", "Checking returned password");
 
+// ===== test 508 =====
+// test proxy login (default = no autologin), make sure it prompts.
+testNum++;
+proxyAuthinfo.username = "";
+proxyAuthinfo.password = "";
+proxyAuthinfo.realm    = "Proxy Realm";
+proxyAuthinfo.flags    = Ci.nsIAuthInformation.AUTH_PROXY;
+
+var time1 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
+startCallbackTimer();
+isOk = prompter2.promptAuth(proxyChannel, level, proxyAuthinfo);
+var time2 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
+
+ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
+isnot(time1, time2, "Checking that timeLastUsed was updated");
+is(proxyAuthinfo.username, "proxuser", "Checking returned username");
+is(proxyAuthinfo.password, "proxpass", "Checking returned password");
+
+// ===== test 509 =====
+// test proxy login (with autologin)
+testNum++;
+
+// Enable the autologin pref.
+prefs.setBoolPref("signon.autologin.proxy", true);
+
+proxyAuthinfo.username = "";
+proxyAuthinfo.password = "";
+proxyAuthinfo.realm    = "Proxy Realm";
+proxyAuthinfo.flags    = Ci.nsIAuthInformation.AUTH_PROXY;
+
+time1 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
+isOk = prompter2.promptAuth(proxyChannel, level, proxyAuthinfo);
+time2 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
+
+ok(isOk, "Checking dialog return value (accept)");
+isnot(time1, time2, "Checking that timeLastUsed was updated");
+is(proxyAuthinfo.username, "proxuser", "Checking returned username");
+is(proxyAuthinfo.password, "proxpass", "Checking returned password");
+
+// ===== test 510 =====
+// test proxy login (with autologin), ensure it prompts after a failed auth.
+testNum++;
+
+proxyAuthinfo.username = "";
+proxyAuthinfo.password = "";
+proxyAuthinfo.realm    = "Proxy Realm";
+proxyAuthinfo.flags    = (Ci.nsIAuthInformation.AUTH_PROXY | Ci.nsIAuthInformation.PREVIOUS_FAILED);
+
+time1 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
+startCallbackTimer();
+isOk = prompter2.promptAuth(proxyChannel, level, proxyAuthinfo);
+time2 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
+
+ok(isOk, "Checking dialog return value (accept)");
+ok(didDialog, "handleDialog was invoked");
+isnot(time1, time2, "Checking that timeLastUsed was updated");
+is(proxyAuthinfo.username, "proxuser", "Checking returned username");
+is(proxyAuthinfo.password, "proxpass", "Checking returned password");
+
+// ===== test 511 =====
+// test proxy login (with autologin), ensure it prompts in Private Browsing mode.
+testNum++;
+
+proxyAuthinfo.username = "";
+proxyAuthinfo.password = "";
+proxyAuthinfo.realm    = "Proxy Realm";
+proxyAuthinfo.flags    = Ci.nsIAuthInformation.AUTH_PROXY;
+
+if (pb) {
+  prefs.setBoolPref("browser.privatebrowsing.keep_current_session", true);
+  pb.privateBrowsingEnabled = true;
+
+  time1 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
+  startCallbackTimer();
+  isOk = prompter2.promptAuth(proxyChannel, level, proxyAuthinfo);
+  time2 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
+
+  ok(isOk, "Checking dialog return value (accept)");
+  ok(didDialog, "handleDialog was invoked");
+  is(time1, time2, "Checking that timeLastUsed was not updated");
+  is(proxyAuthinfo.username, "proxuser", "Checking returned username");
+  is(proxyAuthinfo.password, "proxpass", "Checking returned password");
+
+  pb.privateBrowsingEnabled = false;
+  prefs.clearUserPref("browser.privatebrowsing.keep_current_session");
+}
+
+
+prefs.clearUserPref("signon.autologin.proxy");
 
 // XXX check for and kill notification bar??
 // XXX check for checkbox / checkstate on old prompts?
 // XXX check NTLM domain stuff
 
 
 var iframe = document.getElementById("iframe");
 iframe.onload = handleLoad;