Backed out 5 changesets (bug 993197, bug 949617, bug 995489, bug 1029128) for Android test failures.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 24 Jun 2014 15:36:18 -0400
changeset 211382 6f291b1b47c79117c8d6bfa4a9b6227b8fe8872f
parent 211381 1707f30aa4b9731fb242571c285c46be39fcb0b5
child 211383 a19e0434ea52cc41372bfc9aceca41133e69a73f
child 211385 f012527686c8ea349730d9d487884334e0e571cd
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs993197, 949617, 995489, 1029128
milestone33.0a1
backs out033d6271fd192e2de4de619f8110c30c94aa2417
f568b1dc98802eee6f52c8effc3ac5357793ca7e
daa9c46d19dc577fd034486dcd0b6003d0119beb
2baf5c61cbf5900ceb2b6508bd15e3a9d77fd2ac
654c1f0c88bc39f7ddb5589ee763f6689052c4e9
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
Backed out 5 changesets (bug 993197, bug 949617, bug 995489, bug 1029128) for Android test failures. Backed out changeset 033d6271fd19 (bug 1029128) Backed out changeset f568b1dc9880 (bug 1029128) Backed out changeset daa9c46d19dc (bug 949617) Backed out changeset 2baf5c61cbf5 (bug 995489) Backed out changeset 654c1f0c88bc (bug 993197)
browser/base/content/content.js
browser/components/nsBrowserGlue.js
toolkit/components/passwordmgr/LoginManagerContent.jsm
toolkit/components/passwordmgr/LoginManagerParent.jsm
toolkit/components/passwordmgr/LoginStore.jsm
toolkit/components/passwordmgr/moz.build
toolkit/components/passwordmgr/nsILoginManager.idl
toolkit/components/passwordmgr/nsLoginManager.js
toolkit/components/passwordmgr/storage-json.js
toolkit/components/passwordmgr/test/pwmgr_common.js
toolkit/components/passwordmgr/test/subtst_privbrowsing_3.html
toolkit/components/passwordmgr/test/subtst_privbrowsing_4.html
toolkit/components/passwordmgr/test/test_basic_form.html
toolkit/components/passwordmgr/test/test_basic_form_0pw.html
toolkit/components/passwordmgr/test/test_basic_form_1pw.html
toolkit/components/passwordmgr/test/test_basic_form_1pw_2.html
toolkit/components/passwordmgr/test/test_basic_form_2.html
toolkit/components/passwordmgr/test/test_basic_form_2pw_1.html
toolkit/components/passwordmgr/test/test_basic_form_2pw_2.html
toolkit/components/passwordmgr/test/test_basic_form_3pw_1.html
toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html
toolkit/components/passwordmgr/test/test_basic_form_html5.html
toolkit/components/passwordmgr/test/test_basic_form_observer_autocomplete.html
toolkit/components/passwordmgr/test/test_basic_form_observer_autofillForms.html
toolkit/components/passwordmgr/test/test_basic_form_observer_foundLogins.html
toolkit/components/passwordmgr/test/test_basic_form_pwevent.html
toolkit/components/passwordmgr/test/test_basic_form_pwonly.html
toolkit/components/passwordmgr/test/test_bug_227640.html
toolkit/components/passwordmgr/test/test_bug_242956.html
toolkit/components/passwordmgr/test/test_bug_360493_1.html
toolkit/components/passwordmgr/test/test_bug_360493_2.html
toolkit/components/passwordmgr/test/test_bug_391514.html
toolkit/components/passwordmgr/test/test_bug_427033.html
toolkit/components/passwordmgr/test/test_bug_444968.html
toolkit/components/passwordmgr/test/test_case_differences.html
toolkit/components/passwordmgr/test/test_master_password.html
toolkit/components/passwordmgr/test/test_privbrowsing_perwindowpb.html
toolkit/components/satchel/AutoCompleteE10S.jsm
toolkit/components/satchel/nsFormAutoComplete.js
toolkit/components/satchel/nsFormFillController.cpp
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -29,32 +29,32 @@ addMessageListener("Browser:HideSessionR
   let doc = content.document;
   let container;
   if (doc.documentURI.toLowerCase() == "about:home" &&
       (container = doc.getElementById("sessionRestoreContainer"))){
     container.hidden = true;
   }
 });
 
-addEventListener("DOMFormHasPassword", function(event) {
-  InsecurePasswordUtils.checkForInsecurePasswords(event.target);
-  LoginManagerContent.onFormPassword(event);
-});
-addEventListener("DOMAutoComplete", function(event) {
-  LoginManagerContent.onUsernameInput(event);
-});
-addEventListener("blur", function(event) {
-  LoginManagerContent.onUsernameInput(event);
-});
-
 if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
   addEventListener("contextmenu", function (event) {
     sendAsyncMessage("contextmenu", {}, { event: event });
   }, false);
 } else {
+  addEventListener("DOMFormHasPassword", function(event) {
+    InsecurePasswordUtils.checkForInsecurePasswords(event.target);
+    LoginManagerContent.onFormPassword(event);
+  });
+  addEventListener("DOMAutoComplete", function(event) {
+    LoginManagerContent.onUsernameInput(event);
+  });
+  addEventListener("blur", function(event) {
+    LoginManagerContent.onUsernameInput(event);
+  });
+
   addEventListener("mozUITour", function(event) {
     if (!Services.prefs.getBoolPref("browser.uitour.enabled"))
       return;
 
     let handled = UITour.onPageEvent(event);
     if (handled)
       addEventListener("pagehide", UITour);
   }, false, true);
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -88,19 +88,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource:///modules/sessionstore/SessionStore.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
                                   "resource:///modules/BrowserUITelemetry.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
                                   "resource://gre/modules/AsyncShutdown.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerParent",
-                                  "resource://gre/modules/LoginManagerParent.jsm");
-
 #ifdef NIGHTLY_BUILD
 XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteUX",
                                   "resource:///modules/SignInToWebsite.jsm");
 #endif
 
 XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
                                   "resource:///modules/ContentSearch.jsm");
 
@@ -505,18 +502,16 @@ BrowserGlue.prototype = {
     BrowserUITelemetry.init();
     ContentSearch.init();
 
     if (Services.appinfo.browserTabsRemote) {
       ContentClick.init();
       RemotePrompt.init();
     }
 
-    LoginManagerParent.init();
-
     Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
   },
 
   _checkForOldBuildUpdates: function () {
     // check for update if our build is old
     if (Services.prefs.getBoolPref("app.update.enabled") &&
         Services.prefs.getBoolPref("app.update.checkInstallTime")) {
 
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm
@@ -1,27 +1,22 @@
-/* vim: set ts=4 sts=4 sw=4 et tw=80: */
 /* 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/. */
 
-"use strict";
-
-this.EXPORTED_SYMBOLS = [ "LoginManagerContent",
-                          "UserAutoCompleteResult" ];
+this.EXPORTED_SYMBOLS = ["LoginManagerContent"];
 
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
-Cu.import("resource://gre/modules/Promise.jsm");
 
 // These mirror signon.* prefs.
 var gEnabled, gDebug, gAutofillForms, gStoreWhenAutocompleteOff;
 
 function log(...pieces) {
     function generateLogMessage(args) {
         let strings = ['Login Manager (content):'];
 
@@ -62,17 +57,17 @@ var observer = {
         log("observer notified for form submission.");
 
         // We're invoked before the content's |onsubmit| handlers, so we
         // can grab form data before it might be modified (see bug 257781).
 
         try {
             LoginManagerContent._onFormSubmit(formElement);
         } catch (e) {
-            log("Caught error in onFormSubmit(", e.lineNumber, "):", e.message);
+            log("Caught error in onFormSubmit:", e);
         }
 
         return true; // Always return true, or form submit will be canceled.
     },
 
     onPrefChange : function() {
         gDebug = Services.prefs.getBoolPref("signon.debug");
         gEnabled = Services.prefs.getBoolPref("signon.rememberSignons");
@@ -83,180 +78,85 @@ var observer = {
 
 Services.obs.addObserver(observer, "earlyformsubmit", false);
 var prefBranch = Services.prefs.getBranch("signon.");
 prefBranch.addObserver("", observer.onPrefChange, false);
 
 observer.onPrefChange(); // read initial values
 
 
-function messageManagerFromWindow(win) {
-    return win.QueryInterface(Ci.nsIInterfaceRequestor)
-              .getInterface(Ci.nsIWebNavigation)
-              .QueryInterface(Ci.nsIDocShell)
-              .QueryInterface(Ci.nsIInterfaceRequestor)
-              .getInterface(Ci.nsIContentFrameMessageManager)
-}
-
-// This object maps to the "child" process (even in the single-process case).
 var LoginManagerContent = {
 
     __formFillService : null, // FormFillController, for username autocompleting
     get _formFillService() {
         if (!this.__formFillService)
             this.__formFillService =
                             Cc["@mozilla.org/satchel/form-fill-controller;1"].
                             getService(Ci.nsIFormFillController);
         return this.__formFillService;
     },
 
-    _getRandomId: function() {
-        return Cc["@mozilla.org/uuid-generator;1"]
-                 .getService(Ci.nsIUUIDGenerator).generateUUID().toString();
-    },
-
-    _messages: [ "RemoteLogins:loginsFound",
-                 "RemoteLogins:loginsAutoCompleted" ],
-
-    // Map from form login requests to information about that request.
-    _requests: new Map(),
-
-    // Number of outstanding requests to each manager.
-    _managers: new Map(),
-
-    _takeRequest: function(msg) {
-        let data = msg.data;
-        let request = this._requests.get(data.requestId);
-
-        this._requests.delete(data.requestId);
-
-        let count = this._managers.get(msg.target);
-        if (--count === 0) {
-            this._managers.delete(msg.target);
-
-            for (let message of this._messages)
-                msg.target.removeMessageListener(message, this);
-        } else {
-            this._managers.set(msg.target, count);
-        }
-
-        return request;
-    },
-
-    _sendRequest: function(messageManager, requestData,
-                           name, messageData) {
-        let count;
-        if (!(count = this._managers.get(messageManager))) {
-            this._managers.set(messageManager, 1);
-
-            for (let message of this._messages)
-                messageManager.addMessageListener(message, this);
-        } else {
-            this._managers.set(messageManager, ++count);
-        }
-
-        let requestId = this._getRandomId();
-        messageData.requestId = requestId;
-
-        messageManager.sendAsyncMessage(name, messageData);
-
-        let deferred = Promise.defer();
-        requestData.promise = deferred;
-        this._requests.set(requestId, requestData);
-        return deferred.promise;
-    },
-
-    receiveMessage: function (msg) {
-        let request = this._takeRequest(msg);
-        switch (msg.name) {
-            case "RemoteLogins:loginsFound": {
-                request.promise.resolve({ form: request.form,
-                                          loginsFound: msg.data.logins });
-                break;
-            }
-
-            case "RemoteLogins:loginsAutoCompleted": {
-                request.promise.resolve(msg.data.logins);
-                break;
-            }
-        }
-    },
-
-    _asyncFindLogins: function(form, options) {
-      let doc = form.ownerDocument;
-      let win = doc.defaultView;
-
-      let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI);
-      let actionOrigin = LoginUtils._getActionOrigin(form);
-
-      let messageManager = messageManagerFromWindow(win);
-
-      // XXX Weak??
-      let requestData = { form: form };
-      let messageData = { formOrigin: formOrigin,
-                          actionOrigin: actionOrigin,
-                          options: options };
-
-      return this._sendRequest(messageManager, requestData,
-                               "RemoteLogins:findLogins",
-                               messageData);
-    },
-
-    _autoCompleteSearchAsync: function(aSearchString, aPreviousResult,
-                                       aElement, aRect) {
-        let doc = aElement.ownerDocument;
-        let form = aElement.form;
-        let win = doc.defaultView;
-
-        let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI);
-        let actionOrigin = LoginUtils._getActionOrigin(form);
-
-        let messageManager = messageManagerFromWindow(win);
-
-        let remote = (Services.appinfo.processType ===
-                      Services.appinfo.PROCESS_TYPE_CONTENT);
-
-        let requestData = {};
-        let messageData = { formOrigin: formOrigin,
-                            actionOrigin: actionOrigin,
-                            searchString: aSearchString,
-                            previousResult: aPreviousResult,
-                            rect: aRect,
-                            remote: remote };
-
-        return this._sendRequest(messageManager, requestData,
-                                 "RemoteLogins:autoCompleteLogins",
-                                 messageData);
-    },
-
     /*
      * onFormPassword
      *
      * Called when an <input type="password"> element is added to the page
      */
     onFormPassword: function (event) {
       if (!event.isTrusted)
           return;
 
       if (!gEnabled)
           return;
 
       let form = event.target;
-      log("onFormPassword for", form.ownerDocument.documentURI);
-      this._asyncFindLogins(form, { showMasterPassword: true })
-          .then(this.loginsFound.bind(this))
-          .then(null, Cu.reportError);
+      let doc = form.ownerDocument;
+
+      log("onFormPassword for", doc.documentURI);
+
+      // If there are no logins for this site, bail out now.
+      let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI);
+      if (!Services.logins.countLogins(formOrigin, "", null))
+          return;
+
+      // If we're currently displaying a master password prompt, defer
+      // processing this form until the user handles the prompt.
+      if (Services.logins.uiBusy) {
+        log("deferring onFormPassword for", doc.documentURI);
+        let self = this;
+        let observer = {
+            QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
+
+            observe: function (subject, topic, data) {
+                log("Got deferred onFormPassword notification:", topic);
+                // Only run observer once.
+                Services.obs.removeObserver(this, "passwordmgr-crypto-login");
+                Services.obs.removeObserver(this, "passwordmgr-crypto-loginCanceled");
+                if (topic == "passwordmgr-crypto-loginCanceled")
+                    return;
+                self.onFormPassword(event);
+            },
+            handleEvent : function (event) {
+                // Not expected to be called
+            }
+        };
+        // Trickyness follows: We want an observer, but don't want it to
+        // cause leaks. So add the observer with a weak reference, and use
+        // a dummy event listener (a strong reference) to keep it alive
+        // until the form is destroyed.
+        Services.obs.addObserver(observer, "passwordmgr-crypto-login", true);
+        Services.obs.addObserver(observer, "passwordmgr-crypto-loginCanceled", true);
+        form.addEventListener("mozCleverClosureHack", observer);
+        return;
+      }
+
+      let autofillForm = gAutofillForms && !PrivateBrowsingUtils.isWindowPrivate(doc.defaultView);
+
+      this._fillForm(form, autofillForm, false, false, false, null);
     },
 
-    loginsFound: function({ form, loginsFound }) {
-        let doc = form.ownerDocument;
-        let autofillForm = gAutofillForms && !PrivateBrowsingUtils.isWindowPrivate(doc.defaultView);
-
-        this._fillForm(form, autofillForm, false, false, false, loginsFound);
-    },
 
     /*
      * onUsernameInput
      *
      * Listens for DOMAutoComplete and blur events on an input field.
      */
     onUsernameInput : function(event) {
         if (!event.isTrusted)
@@ -286,21 +186,22 @@ var LoginManagerContent = {
 
         log("onUsernameInput from", event.type);
 
         // Make sure the username field fillForm will use is the
         // same field as the autocomplete was activated on.
         var [usernameField, passwordField, ignored] =
             this._getFormFields(acForm, false);
         if (usernameField == acInputField && passwordField) {
-            this._asyncFindLogins(acForm, { showMasterPassword: false })
-                .then(({ form, loginsFound }) => {
-                    this._fillForm(form, true, true, true, true, loginsFound);
-                })
-                .then(null, Cu.reportError);
+            // If the user has a master password but itsn't logged in, bail
+            // out now to prevent annoying prompts.
+            if (!Services.logins.isLoggedIn)
+                return;
+
+            this._fillForm(acForm, true, true, true, true, null);
         } else {
             // Ignore the event, it's for some input we don't care about.
         }
     },
 
 
     /*
      * _getPasswordFields
@@ -473,16 +374,25 @@ var LoginManagerContent = {
      * _onFormSubmit
      *
      * Called by the our observer when notified of a form submission.
      * [Note that this happens before any DOM onsubmit handlers are invoked.]
      * Looks for a password change in the submitted form, so we can update
      * our stored password.
      */
     _onFormSubmit : function (form) {
+
+        // For E10S this will need to move.
+        function getPrompter(aWindow) {
+            var prompterSvc = Cc["@mozilla.org/login-manager/prompter;1"].
+                              createInstance(Ci.nsILoginManagerPrompter);
+            prompterSvc.init(aWindow);
+            return prompterSvc;
+        }
+
         var doc = form.ownerDocument;
         var win = doc.defaultView;
 
         if (PrivateBrowsingUtils.isWindowPrivate(win)) {
             // We won't do anything in private browsing mode anyway,
             // so there's no need to perform further checks.
             log("(form submission ignored in private browsing mode)");
             return;
@@ -502,59 +412,140 @@ var LoginManagerContent = {
         // notification on about:accounts for Firefox.
         let topWin = win.top;
         if (/^about:accounts($|\?)/i.test(topWin.document.documentURI)) {
             log("(form submission ignored -- about:accounts)");
             return;
         }
 
         var formSubmitURL = LoginUtils._getActionOrigin(form)
+        if (!Services.logins.getLoginSavingEnabled(hostname)) {
+            log("(form submission ignored -- saving is disabled for:", hostname, ")");
+            return;
+        }
+
 
         // Get the appropriate fields from the form.
         var [usernameField, newPasswordField, oldPasswordField] =
             this._getFormFields(form, true);
 
         // Need at least 1 valid password field to do anything.
         if (newPasswordField == null)
-            return;
+                return;
 
         // Check for autocomplete=off attribute. We don't use it to prevent
         // autofilling (for existing logins), but won't save logins when it's
         // present and the storeWhenAutocompleteOff pref is false.
         // XXX spin out a bug that we don't update timeLastUsed in this case?
         if ((this._isAutocompleteDisabled(form) ||
              this._isAutocompleteDisabled(usernameField) ||
              this._isAutocompleteDisabled(newPasswordField) ||
              this._isAutocompleteDisabled(oldPasswordField)) &&
             !gStoreWhenAutocompleteOff) {
                 log("(form submission ignored -- autocomplete=off found)");
                 return;
         }
 
-        // Don't try to send DOM nodes over IPC.
-        let mockUsername = usernameField ?
-                             { name: usernameField.name,
-                               value: usernameField.value } :
-                             null;
-        let mockPassword = { name: newPasswordField.name,
-                             value: newPasswordField.value };
-        let mockOldPassword = oldPasswordField ?
-                                { name: oldPasswordField.name,
-                                  value: oldPasswordField.value } :
-                                null;
+
+        var formLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
+                        createInstance(Ci.nsILoginInfo);
+        formLogin.init(hostname, formSubmitURL, null,
+                    (usernameField ? usernameField.value : ""),
+                    newPasswordField.value,
+                    (usernameField ? usernameField.name  : ""),
+                    newPasswordField.name);
+
+        // If we didn't find a username field, but seem to be changing a
+        // password, allow the user to select from a list of applicable
+        // logins to update the password for.
+        if (!usernameField && oldPasswordField) {
+
+            var logins = Services.logins.findLogins({}, hostname, formSubmitURL, null);
+
+            if (logins.length == 0) {
+                // Could prompt to save this as a new password-only login.
+                // This seems uncommon, and might be wrong, so ignore.
+                log("(no logins for this host -- pwchange ignored)");
+                return;
+            }
+
+            var prompter = getPrompter(win);
+
+            if (logins.length == 1) {
+                var oldLogin = logins[0];
+                formLogin.username      = oldLogin.username;
+                formLogin.usernameField = oldLogin.usernameField;
+
+                prompter.promptToChangePassword(oldLogin, formLogin);
+            } else {
+                prompter.promptToChangePasswordWithUsernames(
+                                    logins, logins.length, formLogin);
+            }
+
+            return;
+        }
+
+
+        // Look for an existing login that matches the form login.
+        var existingLogin = null;
+        var logins = Services.logins.findLogins({}, hostname, formSubmitURL, null);
+
+        for (var i = 0; i < logins.length; i++) {
+            var same, login = logins[i];
 
-        let messageManager = messageManagerFromWindow(win);
-        messageManager.sendAsyncMessage("RemoteLogins:onFormSubmit",
-                                        { hostname: hostname,
-                                          formSubmitURL: formSubmitURL,
-                                          usernameField: mockUsername,
-                                          newPasswordField: mockPassword,
-                                          oldPasswordField: mockOldPassword });
+            // If one login has a username but the other doesn't, ignore
+            // the username when comparing and only match if they have the
+            // same password. Otherwise, compare the logins and match even
+            // if the passwords differ.
+            if (!login.username && formLogin.username) {
+                var restoreMe = formLogin.username;
+                formLogin.username = "";
+                same = formLogin.matches(login, false);
+                formLogin.username = restoreMe;
+            } else if (!formLogin.username && login.username) {
+                formLogin.username = login.username;
+                same = formLogin.matches(login, false);
+                formLogin.username = ""; // we know it's always blank.
+            } else {
+                same = formLogin.matches(login, true);
+            }
+
+            if (same) {
+                existingLogin = login;
+                break;
+            }
+        }
+
+        if (existingLogin) {
+            log("Found an existing login matching this form submission");
+
+            // Change password if needed.
+            if (existingLogin.password != formLogin.password) {
+                log("...passwords differ, prompting to change.");
+                prompter = getPrompter(win);
+                prompter.promptToChangePassword(existingLogin, formLogin);
+            } else {
+                // Update the lastUsed timestamp.
+                var propBag = Cc["@mozilla.org/hash-property-bag;1"].
+                              createInstance(Ci.nsIWritablePropertyBag);
+                propBag.setProperty("timeLastUsed", Date.now());
+                propBag.setProperty("timesUsedIncrement", 1);
+                Services.logins.modifyLogin(existingLogin, propBag);
+            }
+
+            return;
+        }
+
+
+        // Prompt user to save login (via dialog or notification bar)
+        prompter = getPrompter(win);
+        prompter.promptToSavePassword(formLogin);
     },
 
+
     /*
      * _fillform
      *
      * Fill the form with login information if we can find it. This will find
      * an array of logins if not given any, otherwise it will use the logins
      * passed in. The logins are returned so they can be reused for
      * optimization. Success of action is also returned in format
      * [success, foundLogins].
@@ -580,38 +571,40 @@ var LoginManagerContent = {
             return [false, foundLogins];
 
         // If the password field is disabled or read-only, there's nothing to do.
         if (passwordField.disabled || passwordField.readOnly) {
             log("not filling form, password field disabled or read-only");
             return [false, foundLogins];
         }
 
+        // Need to get a list of logins if we weren't given them
+        if (foundLogins == null) {
+            var formOrigin =
+                LoginUtils._getPasswordOrigin(form.ownerDocument.documentURI);
+            var actionOrigin = LoginUtils._getActionOrigin(form);
+            foundLogins = Services.logins.findLogins({}, formOrigin, actionOrigin, null);
+            log("found", foundLogins.length, "matching logins.");
+        } else {
+            log("reusing logins from last form.");
+        }
+
         // Discard logins which have username/password values that don't
         // fit into the fields (as specified by the maxlength attribute).
         // The user couldn't enter these values anyway, and it helps
         // with sites that have an extra PIN to be entered (bug 391514)
         var maxUsernameLen = Number.MAX_VALUE;
         var maxPasswordLen = Number.MAX_VALUE;
 
         // If attribute wasn't set, default is -1.
         if (usernameField && usernameField.maxLength >= 0)
             maxUsernameLen = usernameField.maxLength;
         if (passwordField.maxLength >= 0)
             maxPasswordLen = passwordField.maxLength;
 
-        foundLogins = foundLogins.map(login => {
-            var formLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
-                            createInstance(Ci.nsILoginInfo);
-            formLogin.init(login.hostname, login.formSubmitURL,
-                           login.httpRealm, login.username,
-                           login.password, login.usernameField,
-                           login.passwordField);
-            return formLogin;
-        });
         var logins = foundLogins.filter(function (l) {
                 var fit = (l.username.length <= maxUsernameLen &&
                            l.password.length <= maxPasswordLen);
                 if (!fit)
                     log("Ignored", l.username, "login: won't fit");
 
                 return fit;
             }, this);
@@ -794,17 +787,20 @@ var LoginManagerContent = {
         formInfo.setProperty("foundLogins", foundLogins.concat());
         formInfo.setPropertyAsInterface("selectedLogin", selectedLogin);
 
         Services.obs.notifyObservers(formInfo, "passwordmgr-found-logins", null);
     },
 
 };
 
-var LoginUtils = {
+
+
+
+LoginUtils = {
     /*
      * _getPasswordOrigin
      *
      * Get the parts of the URL we want for identification.
      */
     _getPasswordOrigin : function (uriString, allowJS) {
         var realm = "";
         try {
@@ -840,98 +836,8 @@ var LoginUtils = {
         // A blank or missing action submits to where it came from.
         if (uriString == "")
             uriString = form.baseURI; // ala bug 297761
 
         return this._getPasswordOrigin(uriString, true);
     },
 
 };
-
-// nsIAutoCompleteResult implementation
-function UserAutoCompleteResult (aSearchString, matchingLogins) {
-    function loginSort(a,b) {
-        var userA = a.username.toLowerCase();
-        var userB = b.username.toLowerCase();
-
-        if (userA < userB)
-            return -1;
-
-        if (userB > userA)
-            return  1;
-
-        return 0;
-    };
-
-    this.searchString = aSearchString;
-    this.logins = matchingLogins.sort(loginSort);
-    this.matchCount = matchingLogins.length;
-
-    if (this.matchCount > 0) {
-        this.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
-        this.defaultIndex = 0;
-    }
-}
-
-UserAutoCompleteResult.prototype = {
-    QueryInterface : XPCOMUtils.generateQI([Ci.nsIAutoCompleteResult,
-                                            Ci.nsISupportsWeakReference]),
-
-    // private
-    logins : null,
-
-    // Allow autoCompleteSearch to get at the JS object so it can
-    // modify some readonly properties for internal use.
-    get wrappedJSObject() {
-        return this;
-    },
-
-    // Interfaces from idl...
-    searchString : null,
-    searchResult : Ci.nsIAutoCompleteResult.RESULT_NOMATCH,
-    defaultIndex : -1,
-    errorDescription : "",
-    matchCount : 0,
-
-    getValueAt : function (index) {
-        if (index < 0 || index >= this.logins.length)
-            throw "Index out of range.";
-
-        return this.logins[index].username;
-    },
-
-    getLabelAt: function(index) {
-        return this.getValueAt(index);
-    },
-
-    getCommentAt : function (index) {
-        return "";
-    },
-
-    getStyleAt : function (index) {
-        return "";
-    },
-
-    getImageAt : function (index) {
-        return "";
-    },
-
-    getFinalCompleteValueAt : function (index) {
-        return this.getValueAt(index);
-    },
-
-    removeValueAt : function (index, removeFromDB) {
-        if (index < 0 || index >= this.logins.length)
-            throw "Index out of range.";
-
-        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);
-        }
-    }
-};
deleted file mode 100644
--- a/toolkit/components/passwordmgr/LoginManagerParent.jsm
+++ /dev/null
@@ -1,321 +0,0 @@
-/* vim: set ts=4 sts=4 sw=4 et tw=80: */
-/* 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/. */
-
-"use strict";
-
-const Cu = Components.utils;
-const Ci = Components.interfaces;
-const Cc = Components.classes;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "UserAutoCompleteResult",
-                                  "resource://gre/modules/LoginManagerContent.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "AutoCompleteE10S",
-                                  "resource://gre/modules/AutoCompleteE10S.jsm");
-
-this.EXPORTED_SYMBOLS = [ "LoginManagerParent" ];
-
-var gDebug;
-
-function log(...pieces) {
-    function generateLogMessage(args) {
-        let strings = ['Login Manager (parent):'];
-
-        args.forEach(function(arg) {
-            if (typeof arg === 'string') {
-                strings.push(arg);
-            } else if (typeof arg === 'undefined') {
-                strings.push('undefined');
-            } else if (arg === null) {
-                strings.push('null');
-            } else {
-                try {
-                  strings.push(JSON.stringify(arg, null, 2));
-                } catch(err) {
-                  strings.push("<<something>>");
-                }
-            }
-        });
-        return strings.join(' ');
-    }
-
-    if (!gDebug)
-        return;
-
-    let message = generateLogMessage(pieces);
-    dump(message + "\n");
-    Services.console.logStringMessage(message);
-}
-
-function prefChanged() {
-    gDebug = Services.prefs.getBoolPref("signon.debug");
-}
-
-Services.prefs.addObserver("signon.debug", prefChanged, false);
-prefChanged();
-
-var LoginManagerParent = {
-    init: function() {
-        let mm = Cc["@mozilla.org/globalmessagemanager;1"]
-                   .getService(Ci.nsIMessageListenerManager);
-        mm.addMessageListener("RemoteLogins:findLogins", this);
-        mm.addMessageListener("RemoteLogins:onFormSubmit", this);
-        mm.addMessageListener("RemoteLogins:autoCompleteLogins", this);
-    },
-
-    receiveMessage: function (msg) {
-        let data = msg.data;
-
-        switch (msg.name) {
-            case "RemoteLogins:findLogins": {
-                // TODO Verify msg.target's principals against the formOrigin?
-                this.findLogins(data.options.showMasterPassword,
-                                data.formOrigin,
-                                data.actionOrigin,
-                                data.requestId,
-                                msg.target.messageManager);
-                break;
-            }
-
-            case "RemoteLogins:onFormSubmit": {
-                // TODO Verify msg.target's principals against the formOrigin?
-                this.onFormSubmit(data.hostname,
-                                  data.formSubmitURL,
-                                  data.usernameField,
-                                  data.newPasswordField,
-                                  data.oldPasswordField,
-                                  msg.target);
-                break;
-            }
-
-            case "RemoteLogins:autoCompleteLogins": {
-                this.doAutocompleteSearch(data, msg.target);
-                break;
-            }
-        }
-    },
-
-    findLogins: function(showMasterPassword, formOrigin, actionOrigin,
-                         requestId, target) {
-        if (!showMasterPassword && !Services.logins.isLoggedIn) {
-            target.sendAsyncMessage("RemoteLogins:loginsFound",
-                                    { requestId: requestId, logins: [] });
-            return;
-        }
-
-        // If there are no logins for this site, bail out now.
-        if (!Services.logins.countLogins(formOrigin, "", null)) {
-            target.sendAsyncMessage("RemoteLogins:loginsFound",
-                                    { requestId: requestId, logins: [] });
-            return;
-        }
-
-        // If we're currently displaying a master password prompt, defer
-        // processing this form until the user handles the prompt.
-        if (Services.logins.uiBusy) {
-            log("deferring onFormPassword for", formOrigin);
-            let self = this;
-            let observer = {
-                QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                                       Ci.nsISupportsWeakReference]),
-
-                observe: function (subject, topic, data) {
-                    log("Got deferred onFormPassword notification:", topic);
-                    // Only run observer once.
-                    Services.obs.removeObserver(this, "passwordmgr-crypto-login");
-                    Services.obs.removeObserver(this, "passwordmgr-crypto-loginCanceled");
-                    if (topic == "passwordmgr-crypto-loginCanceled") {
-                        target.sendAsyncMessage("RemoteLogins:loginsFound",
-                                                { requestId: requestId, logins: [] });
-                        return;
-                    }
-
-                    self.findLogins(showMasterPassword, formOrigin, actionOrigin,
-                                    requestId, target);
-                },
-            };
-
-            // Possible leak: it's possible that neither of these notifications
-            // will fire, and if that happens, we'll leak the observer (and
-            // never return). We should guarantee that at least one of these
-            // will fire.
-            // See bug XXX.
-            Services.obs.addObserver(observer, "passwordmgr-crypto-login", false);
-            Services.obs.addObserver(observer, "passwordmgr-crypto-loginCanceled", false);
-            return;
-        }
-
-        var logins = Services.logins.findLogins({}, formOrigin, actionOrigin, null);
-        target.sendAsyncMessage("RemoteLogins:loginsFound",
-                                { requestId: requestId, logins: logins });
-    },
-
-    doAutocompleteSearch: function({ formOrigin, actionOrigin,
-                                     searchString, previousResult,
-                                     rect, requestId, remote }, target) {
-        // Note: previousResult is a regular object, not an
-        // nsIAutoCompleteResult.
-        var result;
-        var matchingLogins;
-
-        let searchStringLower = searchString.toLowerCase();
-        let logins;
-        if (previousResult &&
-            searchStringLower.startsWith(previousResult.searchString.toLowerCase())) {
-            log("Using previous autocomplete result");
-
-            // We have a list of results for a shorter search string, so just
-            // filter them further based on the new search string.
-            logins = previousResult.logins;
-        } else {
-            log("Creating new autocomplete search result.");
-
-            // Grab the logins from the database.
-            logins = Services.logins.findLogins({}, formOrigin, actionOrigin, null);
-        }
-
-        let matchingLogins = logins.filter(function(fullMatch) {
-            let match = fullMatch.username;
-
-            // Remove results that are too short, or have different prefix.
-            // Also don't offer empty usernames as possible results.
-            return match && match.toLowerCase().startsWith(searchStringLower);
-        });
-
-        // XXX In the E10S case, we're responsible for showing our own
-        // autocomplete popup here because the autocomplete protocol hasn't
-        // been e10s-ized yet. In the non-e10s case, our caller is responsible
-        // for showing the autocomplete popup (via the regular
-        // nsAutoCompleteController).
-        if (remote) {
-            result = new UserAutoCompleteResult(searchString, matchingLogins);
-            AutoCompleteE10S.showPopupWithResults(target.ownerDocument.defaultView, rect, result);
-        }
-
-        target.messageManager.sendAsyncMessage("RemoteLogins:loginsAutoCompleted",
-                                               { requestId: requestId,
-                                                 logins: matchingLogins });
-    },
-
-    onFormSubmit: function(hostname, formSubmitURL,
-                           usernameField, newPasswordField,
-                           oldPasswordField,
-                           target) {
-        function getPrompter() {
-            var prompterSvc = Cc["@mozilla.org/login-manager/prompter;1"].
-                              createInstance(Ci.nsILoginManagerPrompter);
-            // XXX For E10S, we don't want to use the browser's contentWindow
-            // because it's in another process, so we use our chrome window as
-            // the window parent (the content process is responsible for
-            // making sure that its window is not in private browsing mode).
-            // In the same-process case, we can simply use the content window.
-            prompterSvc.init(target.isRemoteBrowser ?
-                                target.ownerDocument.defaultView :
-                                target.contentWindow);
-            return prompterSvc;
-        }
-
-        if (!Services.logins.getLoginSavingEnabled(hostname)) {
-            log("(form submission ignored -- saving is disabled for:", hostname, ")");
-            return;
-        }
-
-        var formLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
-                        createInstance(Ci.nsILoginInfo);
-        formLogin.init(hostname, formSubmitURL, null,
-                    (usernameField ? usernameField.value : ""),
-                    newPasswordField.value,
-                    (usernameField ? usernameField.name  : ""),
-                    newPasswordField.name);
-
-        // If we didn't find a username field, but seem to be changing a
-        // password, allow the user to select from a list of applicable
-        // logins to update the password for.
-        if (!usernameField && oldPasswordField) {
-
-            var logins = Services.logins.findLogins({}, hostname, formSubmitURL, null);
-
-            if (logins.length == 0) {
-                // Could prompt to save this as a new password-only login.
-                // This seems uncommon, and might be wrong, so ignore.
-                log("(no logins for this host -- pwchange ignored)");
-                return;
-            }
-
-            var prompter = getPrompter();
-
-            if (logins.length == 1) {
-                var oldLogin = logins[0];
-                formLogin.username      = oldLogin.username;
-                formLogin.usernameField = oldLogin.usernameField;
-
-                prompter.promptToChangePassword(oldLogin, formLogin);
-            } else {
-                prompter.promptToChangePasswordWithUsernames(
-                                    logins, logins.length, formLogin);
-            }
-
-            return;
-        }
-
-
-        // Look for an existing login that matches the form login.
-        var existingLogin = null;
-        var logins = Services.logins.findLogins({}, hostname, formSubmitURL, null);
-
-        for (var i = 0; i < logins.length; i++) {
-            var same, login = logins[i];
-
-            // If one login has a username but the other doesn't, ignore
-            // the username when comparing and only match if they have the
-            // same password. Otherwise, compare the logins and match even
-            // if the passwords differ.
-            if (!login.username && formLogin.username) {
-                var restoreMe = formLogin.username;
-                formLogin.username = "";
-                same = formLogin.matches(login, false);
-                formLogin.username = restoreMe;
-            } else if (!formLogin.username && login.username) {
-                formLogin.username = login.username;
-                same = formLogin.matches(login, false);
-                formLogin.username = ""; // we know it's always blank.
-            } else {
-                same = formLogin.matches(login, true);
-            }
-
-            if (same) {
-                existingLogin = login;
-                break;
-            }
-        }
-
-        if (existingLogin) {
-            log("Found an existing login matching this form submission");
-
-            // Change password if needed.
-            if (existingLogin.password != formLogin.password) {
-                log("...passwords differ, prompting to change.");
-                prompter = getPrompter();
-                prompter.promptToChangePassword(existingLogin, formLogin);
-            } else {
-                // Update the lastUsed timestamp.
-                var propBag = Cc["@mozilla.org/hash-property-bag;1"].
-                              createInstance(Ci.nsIWritablePropertyBag);
-                propBag.setProperty("timeLastUsed", Date.now());
-                propBag.setProperty("timesUsedIncrement", 1);
-                Services.logins.modifyLogin(existingLogin, propBag);
-            }
-
-            return;
-        }
-
-
-        // Prompt user to save login (via dialog or notification bar)
-        prompter = getPrompter();
-        prompter.promptToSavePassword(formLogin);
-    }
-};
--- a/toolkit/components/passwordmgr/LoginStore.jsm
+++ b/toolkit/components/passwordmgr/LoginStore.jsm
@@ -184,24 +184,16 @@ LoginStore.prototype = {
                                                     { humanReadable: true });
             yield openInfo.file.close();
             yield OS.File.move(this.path, openInfo.path);
           } catch (e2) {
             Cu.reportError(e2);
           }
         }
 
-        // In some rare cases it's possible for logins to have been added to
-        // our database between the call to OS.File.read and when we've been
-        // notified that there was a problem with it. In that case, leave the
-        // synchronously-added data alone. See bug 1029128, comment 4.
-        if (this.dataReady) {
-          return;
-        }
-
         // In any case, initialize a new object to host the data.
         this.data = {
           nextId: 1,
         };
       }
 
       this._processLoadedData();
     }.bind(this));
--- a/toolkit/components/passwordmgr/moz.build
+++ b/toolkit/components/passwordmgr/moz.build
@@ -30,17 +30,16 @@ EXTRA_PP_COMPONENTS += [
     'nsLoginManager.js',
     'passwordmgr.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'InsecurePasswordUtils.jsm',
     'LoginHelper.jsm',
     'LoginManagerContent.jsm',
-    'LoginManagerParent.jsm',
 ]
 
 if CONFIG['OS_TARGET'] == 'Android':
     EXTRA_COMPONENTS += [
         'storage-mozStorage.js',
     ]
 else:
     EXTRA_COMPONENTS += [
--- a/toolkit/components/passwordmgr/nsILoginManager.idl
+++ b/toolkit/components/passwordmgr/nsILoginManager.idl
@@ -3,22 +3,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsILoginInfo;
 interface nsIAutoCompleteResult;
-interface nsIFormAutoCompleteObserver;
 interface nsIDOMHTMLInputElement;
 interface nsIDOMHTMLFormElement;
 interface nsIPropertyBag;
 
-[scriptable, uuid(f0c5ca21-db71-4b32-993e-ab63054cc6f5)]
+[scriptable, uuid(f5f2a39a-dffe-4eb9-ad28-340afd53b1a3)]
+
 interface nsILoginManager : nsISupports {
     /**
      * This promise is resolved when initialization is complete, and is rejected
      * in case initialization failed.  This includes the initial loading of the
      * login data as well as any migration from previous versions.
      *
      * Calling any method of nsILoginManager before this promise is resolved
      * might trigger the synchronous initialization fallback.
@@ -205,30 +205,29 @@ interface nsILoginManager : nsISupports 
 
     /**
      * Generate results for a userfield autocomplete menu.
      *
      * NOTE: This interface is provided for use only by the FormFillController,
      *       which calls it directly. This isn't really ideal, it should
      *       probably be callback registered through the FFC.
      */
-    void autoCompleteSearchAsync(in AString aSearchString,
-                                 in nsIAutoCompleteResult aPreviousResult,
-                                 in nsIDOMHTMLInputElement aElement,
-                                 in nsIFormAutoCompleteObserver aListener);
+    nsIAutoCompleteResult autoCompleteSearch(in AString aSearchString,
+                                    in nsIAutoCompleteResult aPreviousResult,
+                                    in nsIDOMHTMLInputElement aElement);
 
     /**
      * Fill a form with login information if we have it. This method will fill
      * aForm regardless of the signon.autofillForms preference.
      *
      * @param aForm
      *        The form to fill
-     * @return Promise that is resolved with whether or not the form was filled.
+     * @return Success of attempt fill form
      */
-    jsval fillForm(in nsIDOMHTMLFormElement aForm);
+    boolean fillForm(in nsIDOMHTMLFormElement aForm);
 
     /**
      * Search for logins in the login manager. An array is always returned;
      * if there are no logins the array is empty.
      *
      * @param count
      *        The number of elements in the array. JS callers can simply use
      *        the array's .length property, and supply an dummy object for
--- a/toolkit/components/passwordmgr/nsLoginManager.js
+++ b/toolkit/components/passwordmgr/nsLoginManager.js
@@ -1,29 +1,26 @@
 /* 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/. */
 
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
-const Cu = Components.utils;
 
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Timer.jsm");
-Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
-Cu.import("resource://gre/modules/LoginManagerContent.jsm");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
+                                  "resource://gre/modules/LoginManagerContent.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
-                                  "resource://gre/modules/BrowserUtils.jsm");
 
 var debug = false;
 function log(...pieces) {
     function generateLogMessage(args) {
         let strings = ['Login Manager:'];
 
         args.forEach(function(arg) {
             if (typeof arg === 'string') {
@@ -114,27 +111,21 @@ LoginManager.prototype = {
 
         // Get current preference values.
         debug = this._prefBranch.getBoolPref("debug");
 
         this._remember = this._prefBranch.getBoolPref("rememberSignons");
 
         // Form submit observer checks forms for new logins and pw changes.
         Services.obs.addObserver(this._observer, "xpcom-shutdown", false);
+        Services.obs.addObserver(this._observer, "passwordmgr-storage-replace",
+                                 false);
 
-        // TODO: Make this class useful in the child process (in addition to
-        // autoCompleteSearchAsync and fillForm).
-        if (Services.appinfo.processType ===
-            Services.appinfo.PROCESS_TYPE_DEFAULT) {
-            Services.obs.addObserver(this._observer, "passwordmgr-storage-replace",
-                                     false);
-
-            // Initialize storage so that asynchronous data loading can start.
-            this._initStorage();
-        }
+        // Initialize storage so that asynchronous data loading can start.
+        this._initStorage();
     },
 
 
     _initStorage : function () {
 #ifdef ANDROID
         var contractID = "@mozilla.org/login-manager/storage/mozStorage;1";
 #else
         var contractID = "@mozilla.org/login-manager/storage/json;1";
@@ -412,63 +403,101 @@ LoginManager.prototype = {
         // Nulls won't round-trip with getAllDisabledHosts().
         if (hostname.indexOf("\0") != -1)
             throw "Invalid hostname";
 
         log("Login saving for", hostname, "now enabled?", enabled);
         return this._storage.setLoginSavingEnabled(hostname, enabled);
     },
 
+
     /*
-     * autoCompleteSearchAsync
+     * autoCompleteSearch
      *
      * Yuck. This is called directly by satchel:
      * nsFormFillController::StartSearch()
-     * [toolkit/components/satchel/nsFormFillController.cpp]
+     * [toolkit/components/satchel/src/nsFormFillController.cpp]
      *
      * We really ought to have a simple way for code to register an
      * auto-complete provider, and not have satchel calling pwmgr directly.
      */
-    autoCompleteSearchAsync : function (aSearchString, aPreviousResult,
-                                        aElement, aCallback) {
-        // aPreviousResult is an nsIAutoCompleteResult, aElement is
-        // nsIDOMHTMLInputElement
+    autoCompleteSearch : function (aSearchString, aPreviousResult, aElement) {
+        // aPreviousResult & aResult are nsIAutoCompleteResult,
+        // aElement is nsIDOMHTMLInputElement
 
-        if (!this._remember) {
-            setTimeout(function() {
-                aCallback.onSearchCompletion(new UserAutoCompleteResult(aSearchString, []));
-            }, 0);
-            return;
-        }
+        if (!this._remember)
+            return null;
 
         log("AutoCompleteSearch invoked. Search is:", aSearchString);
 
-        var previousResult;
-        if (aPreviousResult) {
-            previousResult = { searchString: aPreviousResult.searchString,
-                               logins: aPreviousResult.wrappedJSObject.logins };
+        var result = null;
+
+        if (aPreviousResult &&
+                aSearchString.substr(0, aPreviousResult.searchString.length) == aPreviousResult.searchString) {
+            log("Using previous autocomplete result");
+            result = aPreviousResult;
+            result.wrappedJSObject.searchString = aSearchString;
+
+            // We have a list of results for a shorter search string, so just
+            // filter them further based on the new search string.
+            // Count backwards, because result.matchCount is decremented
+            // when we remove an entry.
+            for (var i = result.matchCount - 1; i >= 0; i--) {
+                var match = result.getValueAt(i);
+
+                // Remove results that are too short, or have different prefix.
+                if (aSearchString.length > match.length ||
+                    aSearchString.toLowerCase() !=
+                        match.substr(0, aSearchString.length).toLowerCase())
+                {
+                    log("Removing autocomplete entry:", match);
+                    result.removeValueAt(i, false);
+                }
+            }
         } else {
-            previousResult = null;
+            log("Creating new autocomplete search result.");
+
+            var doc = aElement.ownerDocument;
+            var origin = this._getPasswordOrigin(doc.documentURI);
+            var actionOrigin = this._getActionOrigin(aElement.form);
+
+            // This shouldn't trigger a master password prompt, because we
+            // don't attach to the input until after we successfully obtain
+            // logins for the form.
+            var logins = this.findLogins({}, origin, actionOrigin, null);
+            var matchingLogins = [];
+
+            // Filter out logins that don't match the search prefix. Also
+            // filter logins without a username, since that's confusing to see
+            // in the dropdown and we can't autocomplete them anyway.
+            for (i = 0; i < logins.length; i++) {
+                var username = logins[i].username.toLowerCase();
+                if (username &&
+                    aSearchString.length <= username.length &&
+                    aSearchString.toLowerCase() ==
+                        username.substr(0, aSearchString.length))
+                {
+                    matchingLogins.push(logins[i]);
+                }
+            }
+            log(matchingLogins.length, "autocomplete logins avail.");
+            result = new UserAutoCompleteResult(aSearchString, matchingLogins);
         }
 
-        let rect = BrowserUtils.getElementBoundingScreenRect(aElement);
-        LoginManagerContent._autoCompleteSearchAsync(aSearchString, previousResult,
-                                                     aElement, rect)
-                           .then(function(logins) {
-                               let results =
-                                   new UserAutoCompleteResult(aSearchString, logins);
-                               aCallback.onSearchCompletion(results);
-                           })
-                           .then(null, Cu.reportError);
+        return result;
     },
 
 
+
+
     /* ------- Internal methods / callbacks for document integration ------- */
 
 
+
+
     /*
      * _getPasswordOrigin
      *
      * Get the parts of the URL we want for identification.
      */
     _getPasswordOrigin : function (uriString, allowJS) {
         var realm = "";
         try {
@@ -511,18 +540,107 @@ LoginManager.prototype = {
 
     /*
      * fillForm
      *
      * Fill the form with login information if we can find it.
      */
     fillForm : function (form) {
         log("fillForm processing form[ id:", form.id, "]");
-        return LoginManagerContent._asyncFindLogins(form, { showMasterPassword: true })
-                                  .then(function({ form, loginsFound }) {
-                   return LoginManagerContent._fillForm(form, true, true,
-                                                        false, false, loginsFound)[0];
-               });
+        return LoginManagerContent._fillForm(form, true, true, false, false, null)[0];
     },
 
 }; // end of LoginManager implementation
 
+
+
+
+// nsIAutoCompleteResult implementation
+function UserAutoCompleteResult (aSearchString, matchingLogins) {
+    function loginSort(a,b) {
+        var userA = a.username.toLowerCase();
+        var userB = b.username.toLowerCase();
+
+        if (userA < userB)
+            return -1;
+
+        if (userB > userA)
+            return  1;
+
+        return 0;
+    };
+
+    this.searchString = aSearchString;
+    this.logins = matchingLogins.sort(loginSort);
+    this.matchCount = matchingLogins.length;
+
+    if (this.matchCount > 0) {
+        this.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
+        this.defaultIndex = 0;
+    }
+}
+
+UserAutoCompleteResult.prototype = {
+    QueryInterface : XPCOMUtils.generateQI([Ci.nsIAutoCompleteResult,
+                                            Ci.nsISupportsWeakReference]),
+
+    // private
+    logins : null,
+
+    // Allow autoCompleteSearch to get at the JS object so it can
+    // modify some readonly properties for internal use.
+    get wrappedJSObject() {
+        return this;
+    },
+
+    // Interfaces from idl...
+    searchString : null,
+    searchResult : Ci.nsIAutoCompleteResult.RESULT_NOMATCH,
+    defaultIndex : -1,
+    errorDescription : "",
+    matchCount : 0,
+
+    getValueAt : function (index) {
+        if (index < 0 || index >= this.logins.length)
+            throw "Index out of range.";
+
+        return this.logins[index].username;
+    },
+
+    getLabelAt: function(index) {
+        return this.getValueAt(index);
+    },
+
+    getCommentAt : function (index) {
+        return "";
+    },
+
+    getStyleAt : function (index) {
+        return "";
+    },
+
+    getImageAt : function (index) {
+        return "";
+    },
+
+    getFinalCompleteValueAt : function (index) {
+        return this.getValueAt(index);
+    },
+
+    removeValueAt : function (index, removeFromDB) {
+        if (index < 0 || index >= this.logins.length)
+            throw "Index out of range.";
+
+        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);
+        }
+    }
+};
+
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([LoginManager]);
--- a/toolkit/components/passwordmgr/storage-json.js
+++ b/toolkit/components/passwordmgr/storage-json.js
@@ -6,18 +6,16 @@
 
 /**
  * nsILoginManagerStorage implementation for the JSON back-end.
  */
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Globals
 
-"use strict";
-
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
                                   "resource://gre/modules/LoginHelper.jsm");
@@ -134,20 +132,22 @@ this.LoginManagerStorage_json.prototype 
 
     /*
      * addLogin
      *
      */
     addLogin : function (login) {
         this._store.ensureDataReady();
 
+        let encUsername, encPassword;
+
         // Throws if there are bogus values.
         LoginHelper.checkLoginValues(login);
 
-        let [encUsername, encPassword, encType] = this._encryptLogin(login);
+        [encUsername, encPassword, encType] = this._encryptLogin(login);
 
         // Clone the login, so we don't modify the caller's object.
         let loginClone = login.clone();
 
         // Initialize the nsILoginMetaInfo fields, unless the caller gave us values
         loginClone.QueryInterface(Ci.nsILoginMetaInfo);
         if (loginClone.guid) {
             if (!this._isGuidUnique(loginClone.guid))
--- a/toolkit/components/passwordmgr/test/pwmgr_common.js
+++ b/toolkit/components/passwordmgr/test/pwmgr_common.js
@@ -123,20 +123,17 @@ function doKey(aKey, modifier) {
 
     if (wutils.sendKeyEvent("keydown",  key, 0, modifier)) {
       wutils.sendKeyEvent("keypress", key, 0, modifier);
     }
     wutils.sendKeyEvent("keyup",    key, 0, modifier);
 }
 
 // Init with a common login
-// If selfFilling is true or non-undefined, fires an event at the page so that
-// the test can start checking filled-in values. Tests that check observer
-// notifications might be confused by this.
-function commonInit(selfFilling) {
+function commonInit() {
     var pwmgr = SpecialPowers.Cc["@mozilla.org/login-manager;1"].
                 getService(SpecialPowers.Ci.nsILoginManager);
     ok(pwmgr != null, "Access LoginManager");
 
 
     // Check that initial state has no logins
     var logins = pwmgr.getAllLogins();
     if (logins.length) {
@@ -157,51 +154,16 @@ function commonInit(selfFilling) {
                "testuser", "testpass", "uname", "pword");
     pwmgr.addLogin(login);
 
     // Last sanity check
     logins = pwmgr.getAllLogins();
     is(logins.length, 1, "Checking for successful init login");
     disabledHosts = pwmgr.getAllDisabledHosts();
     is(disabledHosts.length, 0, "Checking for no disabled hosts");
-
-    if (selfFilling)
-        return;
-
-    // We provide a general mechanism for our tests to know when they can
-    // safely run: we add a final form that we know will be filled in, wait
-    // for the login manager to tell us that it's filled in and then continue
-    // with the rest of the tests.
-    window.addEventListener("DOMContentLoaded", (event) => {
-        var form = document.createElement('form');
-        form.id = 'observerforcer';
-        var username = document.createElement('input');
-        username.name = 'testuser';
-        form.appendChild(username);
-        var password = document.createElement('input');
-        password.name = 'testpass';
-        password.type = 'password';
-        form.appendChild(password);
-
-        var observer = SpecialPowers.wrapCallback(function(subject, topic, data) {
-            var bag = subject.QueryInterface(SpecialPowers.Ci.nsIPropertyBag2);
-            var username = bag.get("usernameField");
-            if (!username || username.form.id !== 'observerforcer')
-                return;
-            SpecialPowers.removeObserver(observer, "passwordmgr-found-logins");
-            form.parentNode.removeChild(form);
-            SimpleTest.executeSoon(() => {
-                var event = new Event("runTests");
-                window.dispatchEvent(event);
-            });
-        });
-        SpecialPowers.addObserver(observer, "passwordmgr-found-logins", false);
-
-        document.body.appendChild(form);
-    });
 }
 
 const masterPassword = "omgsecret!";
 
 function enableMasterPassword() {
     setMasterPassword(true);
 }
 
--- a/toolkit/components/passwordmgr/test/subtst_privbrowsing_3.html
+++ b/toolkit/components/passwordmgr/test/subtst_privbrowsing_3.html
@@ -14,14 +14,14 @@
   <button type='submit'>Submit</button>
 </form>
 
 <script>
 function submitForm() {
   form.submit();
 }
 
+window.onload = submitForm;
 var form      = document.getElementById("form");
-window.addEventListener('message', () => { submitForm(); });
 
 </script>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/subtst_privbrowsing_4.html
+++ b/toolkit/components/passwordmgr/test/subtst_privbrowsing_4.html
@@ -23,16 +23,15 @@ function startAutocomplete() {
 }
 
 function submitForm() {
   doKey("down");
   doKey("return");
   setTimeout(function(){ form.submit(); }, 100);
 }
 
+window.onload = startAutocomplete;
 var form      = document.getElementById("form");
 var userField = document.getElementById("user");
 
-window.addEventListener('message', () => { startAutocomplete(); });
-
 </script>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/test_basic_form.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form.html
@@ -17,17 +17,17 @@ SimpleTest.waitForExplicitFinish();
 
 function startTest() {
   is($_(1, "uname").value, "testuser", "Checking for filled username");
   is($_(1, "pword").value, "testpass", "Checking for filled password");
 
   SimpleTest.finish();
 }
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 </script>
 
 <p id="display"></p>
 
 <div id="content" style="display: none">
 
   <form id="form1" action="formtest.js">
     <p>This is form 1.</p>
--- a/toolkit/components/passwordmgr/test/test_basic_form_0pw.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_0pw.html
@@ -60,16 +60,16 @@ commonInit();
 function startTest() {
   is($_(3, "uname").value, "", "Checking for unfilled checkbox (form 3)");
   is($_(4, "yyyyy").value, "", "Checking for unfilled text field (form 4)");
   is($_(5, "uname").value, "", "Checking for unfilled text field (form 5)");
 
   SimpleTest.finish();
 }
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 </body>
 </html>
 
--- a/toolkit/components/passwordmgr/test/test_basic_form_1pw.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_1pw.html
@@ -156,15 +156,15 @@ function startTest() {
     checkForm(f++, "testpass", "xxxxxxxx");
     checkForm(f++, "xxxxxxxx", "testuser", "testpass");
     checkForm(f++, "testuser", "testpass", "xxxxxxxx");
 
     SimpleTest.finish();
 }
 
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/test_basic_form_1pw_2.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_1pw_2.html
@@ -97,16 +97,16 @@ function startTest() {
     checkForm(9, "testuser", "testpass");
     checkForm(10, "TESTUSER", "testpass");
     checkForm(11, "TESTUSER", "testpass");
 
     SimpleTest.finish();
 }
 
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 </body>
 </html>
 
--- a/toolkit/components/passwordmgr/test/test_basic_form_2.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_2.html
@@ -39,28 +39,26 @@ SpecialPowers.setBoolPref("signon.autofi
 /** Test for Login Manager: simple form fill with autofillForms disabled **/
 
 function startTest(){
   // Ensure the form is empty at start
   is($_(1, "uname").value, "", "Checking for blank username");
   is($_(1, "pword").value, "", "Checking for blank password");
 
   // Call the public method, check return value
-  pwmgr.fillForm(document.getElementById("form1"))
-       .then(function(result) {
-         is(result, true, "Checking return value of fillForm");
+  is(pwmgr.fillForm(document.getElementById("form1")), true,
+     "Checking return value of fillForm");
 
-         // Check that the form was filled
-         is($_(1, "uname").value, "testuser", "Checking for filled username");
-         is($_(1, "pword").value, "testpass", "Checking for filled password");
+  // Check that the form was filled
+  is($_(1, "uname").value, "testuser", "Checking for filled username");
+  is($_(1, "pword").value, "testpass", "Checking for filled password");
 
-         // Reset pref (since we assumed it was true to start)
-         SpecialPowers.setBoolPref("signon.autofillForms", true);
+  // Reset pref (since we assumed it was true to start)
+  SpecialPowers.setBoolPref("signon.autofillForms", true);
 
-         SimpleTest.finish();
-       });
+  SimpleTest.finish();
 }
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/test_basic_form_2pw_1.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_2pw_1.html
@@ -177,16 +177,16 @@ function startTest() {
     checkForm(f++, "",         "xxxxxxxx", "testpass");
     checkForm(f++, "testpass", "",         "");
     checkForm(f++, "xxxxxxxx", "",         "");
 
     SimpleTest.finish();
 }
 
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 </body>
 </html>
 
--- a/toolkit/components/passwordmgr/test/test_basic_form_2pw_2.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_2pw_2.html
@@ -5,16 +5,28 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.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: (placeholder)
 <p id="display"></p>
 
+<div id="content" style="display: none">
+  <form id="form1" onsubmit="return checkSubmit(1)" action="http://newuser.com">
+    <input  type="text"     name="uname">
+    <input  type="password" name="pword">
+    <input  type="password" name="qword">
+
+    <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: form fill, 2 password fields **/
 
 /*
  * If a form has two password fields, other things may be going on....
  *
@@ -101,28 +113,16 @@ ok(Cc_pwmgr != null, "Access Cc[@mozilla
 
 var Ci_pwmgr = SpecialPowers.Ci.nsILoginManager;
 ok(Ci_pwmgr != null, "Access Ci.nsILoginManager");
 
 var pwmgr = Cc_pwmgr.getService(Ci_pwmgr);
 ok(pwmgr != null, "pwmgr getService()");
 
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
-<div id="content" style="display: none">
-  <form id="form1" onsubmit="return checkSubmit(1)" action="http://newuser.com">
-    <input  type="text"     name="uname">
-    <input  type="password" name="pword">
-    <input  type="password" name="qword">
-
-    <button type="submit">Submit</button>
-    <button type="reset"> Reset </button>
-  </form>
-
-</div>
-
 </body>
 </html>
 
--- a/toolkit/components/passwordmgr/test/test_basic_form_3pw_1.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_3pw_1.html
@@ -164,16 +164,16 @@ function startTest() {
   is($_(9, "rword").value, "",         "Checking password 9 (r)");
   is($_(9, "pword").value, "testpass", "Checking password 9");
 
   // TODO: as with the 2-password cases, add tests to check for creating new
   // logins and changing passwords.
   SimpleTest.finish();
 }
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 </body>
 </html>
 
--- a/toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html
@@ -159,17 +159,16 @@ try {
   </form>
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Login Manager: multiple login autocomplete. **/
 
-var tester;
 
 var uname = $_(1, "uname");
 var pword = $_(1, "pword");
 const shiftModifier = SpecialPowers.Ci.nsIDOMEvent.SHIFT_MASK;
 
 // Restore the form to the default state.
 function restoreForm() {
     uname.value = "";
@@ -195,588 +194,583 @@ function sendFakeAutocompleteEvent(eleme
 function hitEventLoop(func, times) {
   if (times > 0) {
     setTimeout(hitEventLoop, 0, func, times - 1);
   } else {
     setTimeout(func, 0);
   }
 }
 
+var gNextTestWillOpenPopup = true;
+var gLastTest = 704;
+
 function addPopupListener(eventName, func, capture) {
   autocompletePopup.addEventListener(eventName, func, capture);
 }
 
 function removePopupListener(eventName, func, capture) {
   autocompletePopup.removeEventListener(eventName, func, capture);
 }
 
 /*
  * Main section of test...
  *
- * This test is, to a first approximation, event driven. Each time we need to
- * wait for an event, runTest sets an event listener (or timeout for a couple
- * of rare cases) and yields. The event listener then resumes the generator by
- * calling its |next| method.
+ * 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() {
-  var testNum = 1;
+function runTest(testNum) {
   ok(true, "Starting test #" + testNum);
 
-  function waitForPopup() {
-    addPopupListener("popupshown", function popupshown() {
-      removePopupListener("popupshown", popupshown, false);
-
-      window.setTimeout(tester.next.bind(tester), 0);
-    }, false);
-  }
+  if (gNextTestWillOpenPopup) {
+    addPopupListener("popupshown", function() {
+      removePopupListener("popupshown", arguments.callee, false);
 
-  function runNextTest(expectPopup) {
-    var save = testNum++;
-    if (expectPopup === "expect popup")
-      return waitForPopup();
+      if (testNum != gLastTest) {
+        window.setTimeout(runTest, 0, testNum + 1);
+      }
+    }, false);
+  } else {
+    var unexpectedPopup = function() {
+      removePopupListener("popupshown", arguments.callee, false);
 
-    var unexpectedPopup = function() {
-      removePopupListener("popupshown", unexpectedPopup, false);
-      ok(false, "Test " + save + " should not show a popup");
+      ok(false, "Test " + testNum + " should not show a popup");
     };
     addPopupListener("popupshown", unexpectedPopup, false);
-
-    hitEventLoop(function() {
+    if (testNum == gLastTest) {
       removePopupListener("popupshown", unexpectedPopup, false);
-      tester.next();
-    }, 100);
-  }
-
-  // We use this function when we're trying to prove that something doesn't
-  // happen, but where if it did it would do so asynchronously. It isn't
-  // perfect, but it's better than nothing.
-  function spinEventLoop() {
-    setTimeout(function() { tester.next(); }, 0);
-  }
-
-  function waitForCompletion() {
-    var observer = SpecialPowers.wrapCallback(function(subject, topic, data) {
-      SpecialPowers.removeObserver(observer, "passwordmgr-found-logins");
-      tester.next();
-    });
-    SpecialPowers.addObserver(observer, "passwordmgr-found-logins", false);
+    } else {
+      hitEventLoop(function() {
+        removePopupListener("popupshown", unexpectedPopup, false);
+        runTest(testNum + 1);
+      }, 100);
+    }
   }
 
-  /* test 1 */
-  // Make sure initial form is empty.
-  checkACForm("", "");
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+  switch(testNum) {
+    case 1:
+        // Make sure initial form is empty.
+        checkACForm("", "");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 2 */
-  // Check first entry
-  doKey("down");
-  checkACForm("", ""); // value shouldn't update
-  doKey("return"); // not "enter"!
-  yield waitForCompletion();
-  checkACForm("tempuser1", "temppass1");
-
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+    case 2:
+        // Check first entry
+        doKey("down");
+        checkACForm("", ""); // value shouldn't update
+        doKey("return"); // not "enter"!
+        checkACForm("tempuser1", "temppass1");
 
-  /* test 3 */
-  // Check second entry
-  doKey("down");
-  doKey("down");
-  doKey("return"); // not "enter"!
-  yield waitForCompletion();
-  checkACForm("testuser2", "testpass2");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 3:
+        // Check second entry
+        doKey("down");
+        doKey("down");
+        doKey("return"); // not "enter"!
+        checkACForm("testuser2", "testpass2");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 4 */
-  // Check third entry
-  doKey("down");
-  doKey("down");
-  doKey("down");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("testuser3", "testpass3");
+    case 4:
+        // Check third entry
+        doKey("down");
+        doKey("down");
+        doKey("down");
+        doKey("return");
+        checkACForm("testuser3", "testpass3");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 5 */
-  // Check fourth entry
-  doKey("down");
-  doKey("down");
-  doKey("down");
-  doKey("down");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("zzzuser4", "zzzpass4");
+    case 5:
+        // Check fourth entry
+        doKey("down");
+        doKey("down");
+        doKey("down");
+        doKey("down");
+        doKey("return");
+        checkACForm("zzzuser4", "zzzpass4");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 6 */
-  // Check first entry (wraparound)
-  doKey("down");
-  doKey("down");
-  doKey("down");
-  doKey("down");
-  doKey("down"); // deselects
-  doKey("down");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("tempuser1", "temppass1");
+    case 6:
+        // Check first entry (wraparound)
+        doKey("down");
+        doKey("down");
+        doKey("down");
+        doKey("down");
+        doKey("down"); // deselects
+        doKey("down");
+        doKey("return");
+        checkACForm("tempuser1", "temppass1");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 7 */
-  // Check the last entry via arrow-up
-  doKey("up");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("zzzuser4", "zzzpass4");
+    case 7:
+        // Check the last entry via arrow-up
+        doKey("up");
+        doKey("return");
+        checkACForm("zzzuser4", "zzzpass4");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 8 */
-  // Check the last entry via arrow-up
-  doKey("down"); // select first entry
-  doKey("up");   // selects nothing!
-  doKey("up");   // select last entry
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("zzzuser4", "zzzpass4");
+    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");
+        checkACForm("zzzuser4", "zzzpass4");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 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");
-  yield waitForCompletion();
-  checkACForm("zzzuser4", "zzzpass4");
+    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");
+        checkACForm("zzzuser4", "zzzpass4");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 10:
+        // Set first entry w/o triggering autocomplete
+        doKey("down");
+        doKey("right");
+        checkACForm("tempuser1", ""); // empty password
 
-  /* test 10 */
-  // Set first entry w/o triggering autocomplete
-  doKey("down");
-  doKey("right");
-  yield spinEventLoop();
-  checkACForm("tempuser1", ""); // empty password
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+    case 11:
+        // Set first entry w/o triggering autocomplete
+        doKey("down");
+        doKey("left");
+        checkACForm("tempuser1", ""); // empty password
 
-  /* test 11 */
-  // Set first entry w/o triggering autocomplete
-  doKey("down");
-  doKey("left");
-  checkACForm("tempuser1", ""); // empty password
-
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 12 */
-  // Check first entry (page up)
-  doKey("down");
-  doKey("down");
-  doKey("page_up");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("tempuser1", "temppass1");
+    case 12:
+        // Check first entry (page up)
+        doKey("down");
+        doKey("down");
+        doKey("page_up");
+        doKey("return");
+        checkACForm("tempuser1", "temppass1");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 13 */
-  // Check last entry (page down)
-  doKey("down");
-  doKey("page_down");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("zzzuser4", "zzzpass4");
-  restoreForm();
-  yield runNextTest();
+    case 13:
+        // Check last entry (page down)
+        doKey("down");
+        doKey("page_down");
+        doKey("return");
+        checkACForm("zzzuser4", "zzzpass4");
+        restoreForm();
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 14 */
-  // Send a fake (untrusted) event.
-  checkACForm("", "");
-  uname.value = "zzzuser4";
-  sendFakeAutocompleteEvent(uname);
-  yield spinEventLoop();
-  checkACForm("zzzuser4", "");
+    case 14:
+        // Send a fake (untrusted) event.
+        checkACForm("", "");
+        uname.value = "zzzuser4";
+        sendFakeAutocompleteEvent(uname);
+        checkACForm("zzzuser4", "");
+        gNextTestWillOpenPopup = true;
+        break;
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  testNum = 49;
-  yield runNextTest("expect popup");
+    case 15:
+        //checkACForm("zzzuser4", "");
 
-  // 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.
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        testNum = 49;
+        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.
 
 
-  /* test 50 */
-  // Delete the first entry (of 4), "tempuser1"
-  doKey("down");
-  var numLogins;
-  numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
-  is(numLogins, 5, "Correct number of logins before deleting one");
+    case 50:
+        // Delete the first entry (of 4), "tempuser1"
+        doKey("down");
+        var numLogins;
+        numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
+        is(numLogins, 5, "Correct number of logins before deleting one");
 
-  // On OS X, shift-backspace and shift-delete work, just delete does not.
-  // On Win/Linux, shift-backspace does not work, delete and shift-delete do.
-  doKey("delete", shiftModifier);
+        // On OS X, shift-backspace and shift-delete work, just delete does not.
+        // On Win/Linux, shift-backspace does not work, delete and shift-delete do.
+        doKey("delete", shiftModifier);
 
-  checkACForm("", "");
-  numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
-  is(numLogins, 4, "Correct number of logins after deleting one");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("testuser2", "testpass2");
+        checkACForm("", "");
+        numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
+        is(numLogins, 4, "Correct number of logins after deleting one");
+        doKey("return");
+        checkACForm("testuser2", "testpass2");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 51 */
-  // Check the new first entry (of 3)
-  doKey("down");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("testuser2", "testpass2");
+    case 51:
+        // Check the new first entry (of 3)
+        doKey("down");
+        doKey("return");
+        checkACForm("testuser2", "testpass2");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 52 */
-  // Delete the second entry (of 3), "testuser3"
-  doKey("down");
-  doKey("down");
-  doKey("delete", shiftModifier);
-  checkACForm("", "");
-  numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
-  is(numLogins, 3, "Correct number of logins after deleting one");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("zzzuser4", "zzzpass4");
+    case 52:
+        // Delete the second entry (of 3), "testuser3"
+        doKey("down");
+        doKey("down");
+        doKey("delete", shiftModifier);
+        checkACForm("", "");
+        numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
+        is(numLogins, 3, "Correct number of logins after deleting one");
+        doKey("return");
+        checkACForm("zzzuser4", "zzzpass4");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 53 */
-  // Check the new second entry (of 2)
-  doKey("down");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("testuser2", "testpass2");
+    case 53:
+        // Check the new second entry (of 2)
+        doKey("down");
+        doKey("return");
+        checkACForm("testuser2", "testpass2");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 54 */
-  // Delete the last entry (of 2), "zzzuser4"
-  doKey("down");
-  doKey("down");
-  doKey("delete", shiftModifier);
-  checkACForm("", "");
-  numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
-  is(numLogins, 2, "Correct number of logins after deleting one");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("testuser2", "testpass2");
+    case 54:
+        // Delete the last entry (of 2), "zzzuser4"
+        doKey("down");
+        doKey("down");
+        doKey("delete", shiftModifier);
+        checkACForm("", "");
+        numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
+        is(numLogins, 2, "Correct number of logins after deleting one");
+        doKey("return");
+        checkACForm("testuser2", "testpass2");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 55 */
-  // Check the new second entry (of 2)
-  doKey("down");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("testuser2", "testpass2");
+    case 55:
+        // Check the new second entry (of 2)
+        doKey("down");
+        doKey("return");
+        checkACForm("testuser2", "testpass2");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 56 */
-  // Delete the only remaining entry, "testuser2"
-  doKey("down");
-  doKey("delete", shiftModifier);
-  //doKey("return");
-  checkACForm("", "");
-  numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
-  is(numLogins, 1, "Correct number of logins after deleting one");
-  pwmgr.removeLogin(login0); // remove the login that's not shown in the list.
-  testNum = 99;
-  yield runNextTest();
+    case 56:
+        // Delete the only remaining entry, "testuser2"
+        doKey("down");
+        doKey("delete", shiftModifier);
+        //doKey("return");
+        checkACForm("", "");
+        numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
+        is(numLogins, 1, "Correct number of logins after deleting one");
+        pwmgr.removeLogin(login0); // remove the login that's not shown in the list.
+        testNum = 99;
+        gNextTestWillOpenPopup = true;
+        break;
 
 
-  /* Tests for single-user forms with autocomplete=off */
+    /* Tests for single-user forms with autocomplete=off */
 
-  /* test 100 */
-  // Turn our attention to form2
-  uname = $_(2, "uname");
-  pword = $_(2, "pword");
-  checkACForm("", "");
+    case 100:
+        // Turn our attention to form2
+        uname = $_(2, "uname");
+        pword = $_(2, "pword");
+        checkACForm("", "");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 101 */
-  // Check first entry
-  doKey("down");
-  checkACForm("", ""); // value shouldn't update
-  doKey("return"); // not "enter"!
-  yield waitForCompletion();
-  checkACForm("singleuser5", "singlepass5");
-  restoreForm(); // clear field, so reloading test doesn't fail
-  yield runNextTest();
+    case 101:
+        // Check first entry
+        doKey("down");
+        checkACForm("", ""); // value shouldn't update
+        doKey("return"); // not "enter"!
+        checkACForm("singleuser5", "singlepass5");
+        restoreForm(); // clear field, so reloading test doesn't fail
+        gNextTestWillOpenPopup = true;
+        break;
 
-  /* test 102 */
-  // Turn our attention to form3
-  uname = $_(3, "uname");
-  pword = $_(3, "pword");
-  checkACForm("", "");
+    case 102:
+        // Turn our attention to form3
+        uname = $_(3, "uname");
+        pword = $_(3, "pword");
+        checkACForm("", "");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 103 */
-  // Check first entry
-  doKey("down");
-  checkACForm("", ""); // value shouldn't update
-  doKey("return"); // not "enter"!
-  yield waitForCompletion();
-  checkACForm("singleuser5", "singlepass5");
-  yield runNextTest();
+    case 103:
+        // Check first entry
+        doKey("down");
+        checkACForm("", ""); // value shouldn't update
+        doKey("return"); // not "enter"!
+        checkACForm("singleuser5", "singlepass5");
+        gNextTestWillOpenPopup = true;
+        break;
 
-  /* test 104 */
-  // Turn our attention to form4
-  uname = $_(4, "uname");
-  pword = $_(4, "pword");
-  checkACForm("", "");
+    case 104:
+        // Turn our attention to form4
+        uname = $_(4, "uname");
+        pword = $_(4, "pword");
+        checkACForm("", "");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 105 */
-  // Check first entry
-  doKey("down");
-  checkACForm("", ""); // value shouldn't update
-  doKey("return"); // not "enter"!
-  yield waitForCompletion();
-  checkACForm("singleuser5", "singlepass5");
-  yield runNextTest();
+    case 105:
+        // Check first entry
+        doKey("down");
+        checkACForm("", ""); // value shouldn't update
+        doKey("return"); // not "enter"!
+        checkACForm("singleuser5", "singlepass5");
+        gNextTestWillOpenPopup = true;
+        break;
 
-  /* test 106 */
-  // Turn our attention to form5
-  uname = $_(5, "uname");
-  pword = $_(5, "pword");
-  checkACForm("", "");
+    case 106:
+        // Turn our attention to form5
+        uname = $_(5, "uname");
+        pword = $_(5, "pword");
+        checkACForm("", "");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 107 */
-  // Check first entry
-  doKey("down");
-  checkACForm("", ""); // value shouldn't update
-  doKey("return"); // not "enter"!
-  yield waitForCompletion();
-  checkACForm("singleuser5", "singlepass5");
-  yield runNextTest();
+    case 107:
+        // Check first entry
+        doKey("down");
+        checkACForm("", ""); // value shouldn't update
+        doKey("return"); // not "enter"!
+        checkACForm("singleuser5", "singlepass5");
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 108 */
-  // Turn our attention to form6
-  // (this is a control, w/o autocomplete=off, to ensure the login
-  // that was being suppressed would have been filled in otherwise)
-  uname = $_(6, "uname");
-  pword = $_(6, "pword");
-  checkACForm("singleuser5", "singlepass5");
-  yield runNextTest();
+    case 108:
+        // Turn our attention to form6
+        // (this is a control, w/o autocomplete=off, to ensure the login
+        // that was being suppressed would have been filled in otherwise)
+        uname = $_(6, "uname");
+        pword = $_(6, "pword");
+        checkACForm("singleuser5", "singlepass5");
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 109 */
-  // Test that the password field remains filled in after changing
-  // the username.
-  uname.focus();
-  doKey("right");
-  sendChar("X");
-  // Trigger the 'blur' event on uname
-  pword.focus();
-  yield spinEventLoop();
-  checkACForm("sXingleuser5", "singlepass5");
+    case 109:
+        // Test that the password field remains filled in after changing
+        // the username.
+        uname.focus();
+        doKey("right");
+        sendChar("X");
+        // Trigger the 'blur' event on uname
+        pword.focus();
+        checkACForm("sXingleuser5", "singlepass5");
 
-  pwmgr.removeLogin(login5);
-  testNum = 499;
-  yield runNextTest();
+        pwmgr.removeLogin(login5);
+        testNum = 499;
+        gNextTestWillOpenPopup = true;
+        break;
 
-  /* test 500 */
-  // Turn our attention to form7
-  uname = $_(7, "uname");
-  pword = $_(7, "pword");
-  checkACForm("", "");
+    case 500:
+        // Turn our attention to form7
+        uname = $_(7, "uname");
+        pword = $_(7, "pword");
+        checkACForm("", "");
 
-  // Insert a new username field into the form. We'll then make sure
-  // that invoking the autocomplete doesn't try to fill the form.
-  var newField = document.createElement("input");
-  newField.setAttribute("type", "text");
-  newField.setAttribute("name", "uname2");
-  pword.parentNode.insertBefore(newField, pword);
-  is($_(7, "uname2").value, "", "Verifying empty uname2");;
+        // Insert a new username field into the form. We'll then make sure
+        // that invoking the autocomplete doesn't try to fill the form.
+        var newField = document.createElement("input");
+        newField.setAttribute("type", "text");
+        newField.setAttribute("name", "uname2");
+        pword.parentNode.insertBefore(newField, pword);
+        is($_(7, "uname2").value, "", "Verifying empty uname2");;
 
-  // Delete login6B. It was created just to prevent filling in a login
-  // automatically, removing it makes it more likely that we'll catch a
-  // future regression with form filling here.
-  pwmgr.removeLogin(login6B);
+        // Delete login6B. It was created just to prevent filling in a login
+        // automatically, removing it makes it more likely that we'll catch a
+        // future regression with form filling here.
+        pwmgr.removeLogin(login6B);
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest("expect popup");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 501 */
-  // Check first entry
-  doKey("down");
-  checkACForm("", ""); // value shouldn't update
-  doKey("return"); // not "enter"!
-  // The form changes, so we expect the old username field to get the
-  // selected autocomplete value, but neither the new username field nor
-  // the password field should have any values filled in.
-  yield spinEventLoop();
-  checkACForm("form7user1", "");
-  is($_(7, "uname2").value, "", "Verifying empty uname2");;
-  restoreForm(); // clear field, so reloading test doesn't fail
+    case 501:
+        // Check first entry
+        doKey("down");
+        checkACForm("", ""); // value shouldn't update
+        doKey("return"); // not "enter"!
+        // The form changes, so we expect the old username field to get the
+        // selected autocomplete value, but neither the new username field nor
+        // the password field should have any values filled in.
+        checkACForm("form7user1", "");
+        is($_(7, "uname2").value, "", "Verifying empty uname2");;
+        restoreForm(); // clear field, so reloading test doesn't fail
 
-  pwmgr.removeLogin(login6A);
-  testNum = 599;
-  yield runNextTest();
+        pwmgr.removeLogin(login6A);
+        testNum = 599;
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 600 */
-  // Turn our attention to form8
-  uname = $_(8, "uname");
-  pword = $_(8, "pword");
-  checkACForm("form8user", "form8pass");
-  restoreForm();
-  yield runNextTest();
+    case 600:
+        // Turn our attention to form8
+        uname = $_(8, "uname");
+        pword = $_(8, "pword");
+        checkACForm("form8user", "form8pass");
+        restoreForm();
+        gNextTestWillOpenPopup = false;
+        break;
+
+    case 601:
+        checkACForm("", "");
+        // Focus the previous form to trigger a blur.
+        $_(7, "uname").focus();
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 601 */
-  checkACForm("", "");
-  // Focus the previous form to trigger a blur.
-  $_(7, "uname").focus();
-  yield runNextTest();
+    case 602:
+        checkACForm("", "");
+        restoreForm();
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 602 */
-  checkACForm("", "");
-  restoreForm();
-  yield runNextTest();
+    case 603:
+        checkACForm("", "");
+        pwmgr.removeLogin(login7);
 
-  /* test 603 */
-  checkACForm("", "");
-  pwmgr.removeLogin(login7);
-
-  testNum = 699;
-  yield runNextTest();
+        testNum = 699;
+        gNextTestWillOpenPopup = true;
+        break;
 
-  /* test 700 */
-  // Turn our attention to form9 to test the dropdown - bug 497541
-  uname = $_(9, "uname");
-  pword = $_(9, "pword");
-  uname.focus();
-  sendString("form9userAB");
-  yield runNextTest("expect popup");
+    case 700:
+        // Turn our attention to form9 to test the dropdown - bug 497541
+        uname = $_(9, "uname");
+        pword = $_(9, "pword");
+        uname.focus();
+        sendString("form9userAB");
+        gNextTestWillOpenPopup = true;
+        break;
 
-  /* test 701 */
-  checkACForm("form9userAB", "");
-  uname.focus();
-  doKey("left");
-  sendChar("A");
-  yield runNextTest("expect popup");
+    case 701:
+        checkACForm("form9userAB", "");
+        uname.focus();
+        doKey("left");
+        sendChar("A");
+        gNextTestWillOpenPopup = false;
+        break;
+
+    case 702:
+        // check dropdown is updated after inserting "A"
+        checkACForm("form9userAAB", "");
+        checkMenuEntries(["form9userAAB"]);
+        doKey("down");
+        doKey("return");
+        checkACForm("form9userAAB", "form9pass");
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 702 */
-  // check dropdown is updated after inserting "A"
-  checkACForm("form9userAAB", "");
-  checkMenuEntries(["form9userAAB"]);
-  doKey("down");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("form9userAAB", "form9pass");
-  yield runNextTest();
+    case 703:
+        pwmgr.addLogin(login8C);
+        uname.focus();
+        sendChar("z");
+        gNextTestWillOpenPopup = false;
+        break;
 
-  /* test 703 */
-  // Note that this addLogin call will only be seen by the autocomplete
-  // attempt for the sendChar if we do not successfully cache the
-  // autocomplete results.
-  pwmgr.addLogin(login8C);
-  uname.focus();
-  sendChar("z");
-  yield runNextTest();
+    case 704:
+        // check that empty results are cached - bug 496466
+        checkMenuEntries([]);
 
-  /* test 704 */
-  // check that empty results are cached - bug 496466
-  checkMenuEntries([]);
+        SimpleTest.finish();
+        return;
 
-  SimpleTest.finish();
-  return;
+    default:
+        ok(false, "Unexpected invocation of test #" + testNum);
+        SimpleTest.finish();
+        return;
+  }
 }
 
 
 function checkMenuEntries(expectedValues) {
     var actualValues = getMenuEntries();
     is(actualValues.length, expectedValues.length, "Checking length of expected menu");
     for (var i = 0; i < expectedValues.length; i++)
         is(actualValues[i], expectedValues[i], "Checking menu entry #"+i);
@@ -805,18 +799,17 @@ function startTest() {
                     .rootTreeItem
                     .QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIDOMWindow)
                     .QueryInterface(Ci.nsIDOMChromeWindow);
     // shouldn't reach into browser internals like this and
     // shouldn't assume ID is consistent across products
     autocompletePopup = chromeWin.document.getElementById("PopupAutoComplete");
     ok(autocompletePopup, "Got autocomplete popup");
-    tester = runTest();
-    tester.next();
+    runTest(1);
 }
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 </script>
 </pre>
 </body>
 </html>
 
--- a/toolkit/components/passwordmgr/test/test_basic_form_html5.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_html5.html
@@ -159,13 +159,13 @@ function startTest() {
   pwmgr.removeLogin(login1);
   pwmgr.removeLogin(login2);
   pwmgr.removeLogin(login3);
   pwmgr.removeLogin(login4);
  
   SimpleTest.finish();
 }
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/test_basic_form_observer_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_observer_autocomplete.html
@@ -55,20 +55,18 @@ var TestObserver = {
     if (topic == "passwordmgr-found-form") {
       if (subject.id == "form1") {
         this.receivedNotification1 = true;
         this.data1 = data;
       } else if (subject.id == "form2") {
         this.receivedNotification2 = true;
         this.data2 = data;
       }
-
       // Now fill the form
-      pwmgr.fillForm(subject)
-           .then(startTest);
+      pwmgr.fillForm(subject);
     }
   }
 };
 
 // Add the observer
 SpecialPowers.addObserver(TestObserver, "passwordmgr-found-form", false);
 
 function startTest(){
@@ -92,12 +90,14 @@ function startTest(){
   try {
     SpecialPowers.removeObserver(TestObserver, "passwordmgr-found-form");
   } catch (e) {
     ok(false, SpecialPowers.wrap(e));
   }
 
   SimpleTest.finish();
 }
+
+window.onload = startTest;
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/test_basic_form_observer_autofillForms.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_observer_autofillForms.html
@@ -4,17 +4,17 @@
   <title>Test for Login Manager</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.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: simple form with autofillForms disabled and notifying observers
 <script>
-commonInit(true);
+commonInit();
 SimpleTest.waitForExplicitFinish();
 
 const Cc = SpecialPowers.Cc;
 const Ci = SpecialPowers.Ci;
 
 // Assume that the pref starts out true, so set to false
 SpecialPowers.setBoolPref("signon.autofillForms", false);
 
@@ -26,26 +26,19 @@ var TestObserver = {
   observe : function (subject, topic, data) {
     var pwmgr = Cc["@mozilla.org/login-manager;1"].
                 getService(Ci.nsILoginManager);
     if (topic == "passwordmgr-found-form") {
       info("got passwordmgr-found-form");
       this.receivedNotificationFoundForm = true;
       this.dataFoundForm = data;
       // Now fill the form
-      pwmgr.fillForm(subject)
-           .then(window.startTest)
-           .then(null, function(e) { alert(e); });
+      pwmgr.fillForm(subject);
     } else if (topic == "passwordmgr-found-logins") {
       info("got passwordmgr-found-logins");
-
-      // We only care about the first notification (the second comes from our
-      // own call to pwmgr.fillForm.
-      if (this.receivedNotificationFoundLogins)
-        return;
       this.receivedNotificationFoundLogins = true;
       this.dataFoundLogins = subject.QueryInterface(Ci.nsIPropertyBag2);
     }
   }
 };
 
 // Add the observer
 SpecialPowers.addObserver(TestObserver, "passwordmgr-found-form", false);
@@ -66,17 +59,17 @@ SpecialPowers.addObserver(TestObserver, 
   </form>
 
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Login Manager: simple form with autofillForms disabled and notifying observers **/
-function startTest() {
+function startTest(){
   // Test that found-form observer is notified & got correct data
   is(TestObserver.receivedNotificationFoundForm, true, "Checking found-form observer was notified");
   is(TestObserver.dataFoundForm, "noAutofillForms", "Checking found-form observer got correct data");
 
   // Check that form1 was filled
   is($_(1, "uname").value, "testuser", "Checking for filled username");
   is($_(1, "pword").value, "testpass", "Checking for filled password");
 
@@ -96,13 +89,13 @@ function startTest() {
 
   // Remove the observer
   SpecialPowers.removeObserver(TestObserver, "passwordmgr-found-form");
   SpecialPowers.removeObserver(TestObserver, "passwordmgr-found-logins");
 
   SimpleTest.finish();
 }
 
-
+window.onload = startTest;
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/test_basic_form_observer_foundLogins.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_observer_foundLogins.html
@@ -169,13 +169,13 @@ function startTest(){
 
   // Remove the logins added for the multiple logins test.
   pwmgr.removeLogin(login1);
   pwmgr.removeLogin(login2);
 
   SimpleTest.finish();
 }
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/test_basic_form_pwevent.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_pwevent.html
@@ -9,46 +9,44 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="text/javascript" src="pwmgr_common.js"></script>
   <script type="application/javascript">
   /** Test for Bug 355063 **/
 
   function startTest() {
     info("startTest");
-    // Password Manager's own listener should always have been added first, so
-    // the test's listener should be called after the pwmgr's listener fills in
-    // a login.
-    //
-    SpecialPowers.addChromeEventListener("DOMFormHasPassword", function eventFired() {
-      SpecialPowers.removeChromeEventListener("DOMFormHasPassword", eventFired);
-      setTimeout(checkForm, 300);
-    });
     addForm();
   }
 
   function addForm() {
     info("addForm");
     var c = document.getElementById("content");
     c.innerHTML = "<form id=form1>form1: <input id=u1><input type=password id=p1></form><br>";
   }
 
   function checkForm() {
     info("checkForm");
     var userField = document.getElementById("u1");
     var passField = document.getElementById("p1");
     is(userField.value, "testuser", "checking filled username");
     is(passField.value, "testpass", "checking filled password");
 
+    SpecialPowers.removeChromeEventListener("DOMFormHasPassword", checkForm);
     SimpleTest.finish();
   }
 
   commonInit();
 
-  window.addEventListener("runTests", startTest);
+  // Password Manager's own listener should always have been added first, so
+  // the test's listener should be called after the pwmgr's listener fills in
+  // a login.
+  //
+  SpecialPowers.addChromeEventListener("DOMFormHasPassword", checkForm);
+  window.addEventListener("load", startTest);
   SimpleTest.waitForExplicitFinish();
 </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=355063">Mozilla Bug 355063</a>
 <p id="display"></p>
 <div id="content">
 forms go here!
--- a/toolkit/components/passwordmgr/test/test_basic_form_pwonly.html
+++ b/toolkit/components/passwordmgr/test/test_basic_form_pwonly.html
@@ -206,14 +206,14 @@ function startTest() {
 
     checkUnmodifiedForm(12);
     checkUnmodifiedForm(13);
 
     pwmgr.removeLogin(pwlogin2);
     SimpleTest.finish();
 }
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 </script>
 </pre>
 </body>
 </html>
 
--- a/toolkit/components/passwordmgr/test/test_bug_227640.html
+++ b/toolkit/components/passwordmgr/test/test_bug_227640.html
@@ -230,14 +230,14 @@ function getFormSubmitButton(formNum) {
 }
 
 // Counts the number of logins currently stored by password manager.
 function countLogins() {
   var logins = pwmgr.getAllLogins();
 
   return logins.length;
 }
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 </script>
 </pre>
 </body>
 </html>
 
--- a/toolkit/components/passwordmgr/test/test_bug_242956.html
+++ b/toolkit/components/passwordmgr/test/test_bug_242956.html
@@ -113,16 +113,16 @@ function startTest() {
   is($_(7, "uname").value, "testuser", "Checking for unmodified username 7");
   is($_(7, "pword").value, "",         "Checking for unfilled password 7");
 
 
 
   SimpleTest.finish();
 }
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/test_bug_360493_1.html
+++ b/toolkit/components/passwordmgr/test/test_bug_360493_1.html
@@ -124,17 +124,17 @@ function startTest() {
 
   // The login's formSubmitURL isn't "javascript:", so don't fill it in.
   isnot($_(10, "uname"), "testuser", "Checking username w/ JS action URL");
   isnot($_(10, "pword"), "testpass", "Checking password w/ JS action URL");
 
   SimpleTest.finish();
 }
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
 
--- a/toolkit/components/passwordmgr/test/test_bug_360493_2.html
+++ b/toolkit/components/passwordmgr/test/test_bug_360493_2.html
@@ -161,17 +161,17 @@ function startTest() {
   // there may be a security issue.
   is(document.forms.length,  11,  "Checking for unexpected forms");
   $("neutered_submit10").click();
   $("neutered_submit11").click();
 
   SimpleTest.finish();
 }
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
 
--- a/toolkit/components/passwordmgr/test/test_bug_391514.html
+++ b/toolkit/components/passwordmgr/test/test_bug_391514.html
@@ -125,16 +125,16 @@ function startTest() {
 
   // Note that tests 11-13 are limited to exactly the expected value.
   // Assert this lest someone change the login we're testing with.
   is($_(11, "uname").value.length, 8, "asserting test assumption is valid.");
 
   SimpleTest.finish();
 }
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/test_bug_427033.html
+++ b/toolkit/components/passwordmgr/test/test_bug_427033.html
@@ -5,20 +5,16 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.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: form with JS submit action
 <script>
 SimpleTest.waitForExplicitFinish();
-
-// Note: Call this first so it doesn't override our login.
-commonInit();
-
 var pwmgr = SpecialPowers.Cc["@mozilla.org/login-manager;1"]
                          .getService(SpecialPowers.Ci.nsILoginManager);
 var jslogin = SpecialPowers.Cc["@mozilla.org/login-manager/loginInfo;1"]
                            .createInstance(SpecialPowers.Ci.nsILoginInfo);
 jslogin.init("http://mochi.test:8888", "javascript:", null,
               "jsuser", "jspass123", "uname", "pword");
 pwmgr.addLogin(jslogin);
 
@@ -26,18 +22,17 @@ pwmgr.addLogin(jslogin);
 
 function startTest() {
     checkForm(1, "jsuser", "jspass123");
 
     pwmgr.removeLogin(jslogin);
     SimpleTest.finish();
 }
 
-// XXX
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 </script>
 
 <p id="display"></p>
 
 <div id="content" style="display: none">
 
 
 <form id='form1' action='javascript:alert("never shows")'> 1
--- a/toolkit/components/passwordmgr/test/test_bug_444968.html
+++ b/toolkit/components/passwordmgr/test/test_bug_444968.html
@@ -122,13 +122,13 @@ function startTest() {
   pwmgr.removeLogin(login1B);
   pwmgr.removeLogin(login2A);
   pwmgr.removeLogin(login2B);
   pwmgr.removeLogin(login2C);
 
   SimpleTest.finish();
 }
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/test_case_differences.html
+++ b/toolkit/components/passwordmgr/test/test_case_differences.html
@@ -61,17 +61,16 @@ try {
 
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Login Manager: multiple login autocomplete. **/
 
-var tester;
 
 var uname = $_(1, "uname");
 var pword = $_(1, "pword");
 
 // Restore the form to the default state.
 function restoreForm() {
     uname.value = "";
     pword.value = "";
@@ -88,107 +87,111 @@ function checkACForm(expectedUsername, e
 
 
 function sendFakeAutocompleteEvent(element) {
     var acEvent = document.createEvent("HTMLEvents");
     acEvent.initEvent("DOMAutoComplete", true, false);
     element.dispatchEvent(acEvent);
 }
 
+var gLastTest = 6;
+
 function addPopupListener(eventName, func, capture) {
   autocompletePopup.addEventListener(eventName, func, capture);
 }
 
 function removePopupListener(eventName, func, capture) {
   autocompletePopup.removeEventListener(eventName, func, capture);
 }
 
 /*
  * 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() {
-  function runNextTest() {
-    addPopupListener("popupshown", function() {
-      removePopupListener("popupshown", arguments.callee, false);
+function runTest(testNum) {
+  ok(true, "Starting test #" + testNum);
 
-      window.setTimeout(tester.next.bind(tester), 0);
-    }, false);
-  }
+  addPopupListener("popupshown", function() {
+    removePopupListener("popupshown", arguments.callee, false);
 
-  function waitForCompletion() {
-    var observer = SpecialPowers.wrapCallback(function(subject, topic, data) {
-      SpecialPowers.removeObserver(observer, "passwordmgr-found-logins");
-      tester.next();
-    });
-    SpecialPowers.addObserver(observer, "passwordmgr-found-logins", false);
-  }
+    if (testNum != gLastTest) {
+      window.setTimeout(runTest, 0, testNum + 1);
+    }
+  }, false);
 
-  /* test 1 */
-  // Make sure initial form is empty.
-  checkACForm("", "");
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest();
+  switch(testNum) {
+    case 1:
+        // Make sure initial form is empty.
+        checkACForm("", "");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  /* test 2 */
-  // Check first entry
-  doKey("down");
-  checkACForm("", ""); // value shouldn't update
-  doKey("return"); // not "enter"!
-  yield waitForCompletion();
-  checkACForm("name", "pass");
+    case 2:
+        // Check first entry
+        doKey("down");
+        checkACForm("", ""); // value shouldn't update
+        doKey("return"); // not "enter"!
+        checkACForm("name", "pass");
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest();
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 3:
+        // Check second entry
+        doKey("down");
+        doKey("down");
+        doKey("return"); // not "enter"!
+        checkACForm("Name", "Pass");
 
-  /* test 3 */
-  // Check second entry
-  doKey("down");
-  doKey("down");
-  doKey("return"); // not "enter"!
-  yield waitForCompletion();
-  checkACForm("Name", "Pass");
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  // Trigger autocomplete popup
-  restoreForm();
-  doKey("down");
-  yield runNextTest();
+    case 4:
+        // Check third entry
+        doKey("down");
+        doKey("down");
+        doKey("down");
+        doKey("return");
+        checkACForm("USER", "PASS");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        uname.value = "user";
+        doKey("down");
+        break;
 
-  /* test 4 */
-  // Check third entry
-  doKey("down");
-  doKey("down");
-  doKey("down");
-  doKey("return");
-  yield waitForCompletion();
-  checkACForm("USER", "PASS");
+    case 5:
+        // Check that we don't clobber user-entered text when tabbing away
+        doKey("tab");
+        checkACForm("user", "PASS");
+
+        // Trigger autocomplete popup
+        restoreForm();
+        doKey("down");
+        break;
 
-  // Trigger autocomplete popup
-  restoreForm();
-  uname.value = "user";
-  doKey("down");
-  yield runNextTest();
+    case 6:
+        SimpleTest.finish();
+        return;
 
-  /* test 5 */
-  // Check that we don't clobber user-entered text when tabbing away
-  doKey("tab");
-  yield waitForCompletion();
-  checkACForm("user", "PASS");
-
-  // Trigger autocomplete popup
-  restoreForm();
-  SimpleTest.finish();
+    default:
+        ok(false, "Unexpected invocation of test #" + testNum);
+        SimpleTest.finish();
+        return;
+  }
 }
 
 
 var autocompletePopup;
 
 function startTest() {
     var Ci = SpecialPowers.Ci;
     chromeWin = SpecialPowers.wrap(window)
@@ -198,18 +201,17 @@ function startTest() {
                     .rootTreeItem
                     .QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIDOMWindow)
                     .QueryInterface(Ci.nsIDOMChromeWindow);
     // shouldn't reach into browser internals like this and
     // shouldn't assume ID is consistent across products
     autocompletePopup = chromeWin.document.getElementById("PopupAutoComplete");
     ok(autocompletePopup, "Got autocomplete popup");
-    tester = runTest();
-    tester.next();
+    runTest(1);
 }
 
-window.addEventListener("runTests", startTest);
+window.onload = startTest;
 </script>
 </pre>
 </body>
 </html>
 
--- a/toolkit/components/passwordmgr/test/test_master_password.html
+++ b/toolkit/components/passwordmgr/test/test_master_password.html
@@ -110,27 +110,24 @@ function handleDialog(doc, testNum) {
 
     if (testNum == 4)
         checkTest4A();
 }
 
 var outerWindowObserver = {
   observe: function(id) {
     SpecialPowers.removeObserver(outerWindowObserver, "outer-window-destroyed");
-    var func;
     if (testNum == 1)
-        func = startTest2;
+        startTest2();
     else if (testNum == 2)
-        func = startTest3;
+        startTest3();
     else if (testNum == 3)
-        func = checkTest3;
+        checkTest3();
     else if (testNum == 5)
-        func = checkTest4C;
-
-    setTimeout(func, 300);
+        checkTest4C();
   }
 };
 
 
 function startTest1() {
     ok(pwcrypt.isLoggedIn, "should be initially logged in (no MP)");
     enableMasterPassword();
     ok(!pwcrypt.isLoggedIn, "should be logged out after setting MP");
@@ -267,14 +264,14 @@ function finishTest() {
     disableMasterPassword();
     ok(pwcrypt.isLoggedIn, "should be logged in");
 
     pwmgr.removeLogin(login1);
     pwmgr.removeLogin(login2);
     SimpleTest.finish();
 }
 
-window.addEventListener("runTests", startTest1);
+window.onload = startTest1;
 </script>
 </pre>
 </body>
 </html>
 
--- a/toolkit/components/passwordmgr/test/test_privbrowsing_perwindowpb.html
+++ b/toolkit/components/passwordmgr/test/test_privbrowsing_perwindowpb.html
@@ -35,17 +35,16 @@ var subtests = [
                    "subtst_privbrowsing_2.html", // 4
                    "subtst_privbrowsing_2.html", // 5
                    "subtst_privbrowsing_2.html", // 6
                    "subtst_privbrowsing_3.html", // 7
                    "subtst_privbrowsing_3.html", // 8
                    "subtst_privbrowsing_4.html", // 9
                    "subtst_privbrowsing_3.html" // 10
                ];
-var observer;
 
 var testNum = 0;
 function loadNextTest() {
   // run the initialization code for each test
   switch (++ testNum) {
     case 1:
       popupNotifications = normalWindowPopupNotifications;
       iframe = normalWindowIframe;
@@ -91,27 +90,21 @@ function loadNextTest() {
       popupNotifications = normalWindowPopupNotifications;
       iframe = normalWindowIframe;
       break;
 
     default:
       ok(false, "Unexpected call to loadNextTest for test #" + testNum);
   }
 
-  if (testNum === 7) {
-    observer = SpecialPowers.wrapCallback(function(subject, topic, data) {
-      SimpleTest.executeSoon(() => { iframe.contentWindow.postMessage("go", "*"); });
-    });
-    SpecialPowers.addObserver(observer, "passwordmgr-found-logins", false);
-  }
-
   ok(true, "Starting test #" + testNum);
   iframe.src = prefix + subtests[testNum-1];
 }
 
+
 function checkTest() {
   var popup;
 
   switch (testNum) {
     case 1:
       // run outside of private mode, popup notification should appear
       popup = getPopup(popupNotifications, "password-save");
       ok(popup, "got popup notification");
@@ -250,17 +243,16 @@ function handleLoad(aEvent) {
     loadNextTest();
   } else {
     ok(true, "private browsing notification tests finished.");
 
     testWindows.forEach(function(aWin) {
       aWin.close();
     });
 
-    SpecialPowers.removeObserver(observer, "passwordmgr-found-logins");
     SimpleTest.finish();
   }
 }
 
 var pwmgr = Cc["@mozilla.org/login-manager;1"].
             getService(Ci.nsILoginManager);
 ok(pwmgr != null, "Access pwmgr");
 
--- a/toolkit/components/satchel/AutoCompleteE10S.jsm
+++ b/toolkit/components/satchel/AutoCompleteE10S.jsm
@@ -70,93 +70,69 @@ this.AutoCompleteE10S = {
   init: function() {
     let messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
                          getService(Ci.nsIMessageListenerManager);
     messageManager.addMessageListener("FormAutoComplete:SelectBy", this);
     messageManager.addMessageListener("FormAutoComplete:GetSelectedIndex", this);
     messageManager.addMessageListener("FormAutoComplete:ClosePopup", this);
   },
 
-  _initPopup: function(browserWindow, rect) {
+  search: function(message) {
+    let browserWindow = message.target.ownerDocument.defaultView;
     this.browser = browserWindow.gBrowser.selectedBrowser;
     this.popup = this.browser.autoCompletePopup;
     this.popup.hidden = false;
-    this.popup.setAttribute("width", rect.width);
+    this.popup.setAttribute("width", message.data.width);
 
+    let rect = message.data;
     let {x, y} = this.browser.mapScreenCoordinatesFromContent(rect.left, rect.top + rect.height);
     this.x = x;
     this.y = y;
+
+    let formAutoComplete = Cc["@mozilla.org/satchel/form-autocomplete;1"]
+                             .getService(Ci.nsIFormAutoComplete);
+
+    formAutoComplete.autoCompleteSearchAsync(message.data.inputName,
+                                             message.data.untrimmedSearchString,
+                                             null,
+                                             null,
+                                             this.onSearchComplete.bind(this));
   },
 
-  _showPopup: function(results) {
+  onSearchComplete: function(results) {
     AutoCompleteE10SView.clearResults();
 
     let resultsArray = [];
     let count = results.matchCount;
     for (let i = 0; i < count; i++) {
       let result = results.getValueAt(i);
       resultsArray.push(result);
       AutoCompleteE10SView.addResult(result);
     }
 
     this.popup.view = AutoCompleteE10SView;
 
+    this.browser.messageManager.sendAsyncMessage(
+      "FormAutoComplete:AutoCompleteSearchAsyncResult",
+      {results: resultsArray}
+    );
+
     this.popup.selectedIndex = -1;
     this.popup.invalidate();
 
     if (count > 0) {
       this.popup.openPopupAtScreen(this.x, this.y, true);
       // Bug 947503 - This openPopup call is not triggering the "popupshowing"
       // event, which autocomplete.xml uses to track the openness of the popup
       // by setting its mPopupOpen flag. This flag needs to be properly set
       // for closePopup to work. For now, we set it manually.
       this.popup.mPopupOpen = true;
     } else {
       this.popup.closePopup();
     }
-
-    return resultsArray;
-  },
-
-  // This function is used by the login manager, which uses a single message
-  // to fill in the autocomplete results. See
-  // "RemoteLogins:autoCompleteLogins".
-  showPopupWithResults: function(browserWindow, rect, results) {
-    this._initPopup(browserWindow, rect);
-    this._showPopup(results);
-  },
-
-  // This function is called in response to AutoComplete requests from the
-  // child (received via the message manager, see
-  // "FormHistory:AutoCompleteSearchAsync").
-  search: function(message) {
-    let browserWindow = message.target.ownerDocument.defaultView;
-    let rect = message.data;
-
-    this._initPopup(browserWindow, rect);
-
-    let formAutoComplete = Cc["@mozilla.org/satchel/form-autocomplete;1"]
-                             .getService(Ci.nsIFormAutoComplete);
-
-    formAutoComplete.autoCompleteSearchAsync(message.data.inputName,
-                                             message.data.untrimmedSearchString,
-                                             null,
-                                             null,
-                                             this.onSearchComplete.bind(this));
-  },
-
-  // The second half of search, this fills in the popup and returns the
-  // results to the child.
-  onSearchComplete: function(results) {
-    let resultsArray = this._showPopup(results);
-
-    this.browser.messageManager.sendAsyncMessage(
-      "FormAutoComplete:AutoCompleteSearchAsyncResult",
-      {results: resultsArray}
-    );
   },
 
   receiveMessage: function(message) {
     switch (message.name) {
       case "FormAutoComplete:SelectBy":
         this.popup.selectBy(message.data.reverse, message.data.page);
         break;
 
--- a/toolkit/components/satchel/nsFormAutoComplete.js
+++ b/toolkit/components/satchel/nsFormAutoComplete.js
@@ -433,17 +433,17 @@ FormAutoCompleteResult.prototype = {
     get wrappedJSObject() {
         return this;
     },
 
     // Interfaces from idl...
     searchString : null,
     errorDescription : "",
     get defaultIndex() {
-        if (this.entries.length == 0)
+        if (entries.length == 0)
             return -1;
         else
             return 0;
     },
     get searchResult() {
         if (this.entries.length == 0)
             return Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
         return Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -594,29 +594,32 @@ nsFormFillController::GetInPrivateContex
 ////////////////////////////////////////////////////////////////////////
 //// nsIAutoCompleteSearch
 
 NS_IMETHODIMP
 nsFormFillController::StartSearch(const nsAString &aSearchString, const nsAString &aSearchParam,
                                   nsIAutoCompleteResult *aPreviousResult, nsIAutoCompleteObserver *aListener)
 {
   nsresult rv;
+  nsCOMPtr<nsIAutoCompleteResult> result;
 
   // If the login manager has indicated it's responsible for this field, let it
   // handle the autocomplete. Otherwise, handle with form history.
   bool dummy;
   if (mPwmgrInputs.Get(mFocusedInputNode, &dummy)) {
     // XXX aPreviousResult shouldn't ever be a historyResult type, since we're not letting
     // satchel manage the field?
-    mLastListener = aListener;
-    rv = mLoginManager->AutoCompleteSearchAsync(aSearchString,
-                                                aPreviousResult,
-                                                mFocusedInput,
-                                                this);
+    rv = mLoginManager->AutoCompleteSearch(aSearchString,
+                                           aPreviousResult,
+                                           mFocusedInput,
+                                           getter_AddRefs(result));
     NS_ENSURE_SUCCESS(rv, rv);
+    if (aListener) {
+      aListener->OnSearchResult(this, result);
+    }
   } else {
     mLastListener = aListener;
 
     // It appears that mFocusedInput is always null when we are focusing a XUL
     // element. Scary :)
     if (!mFocusedInput || nsContentUtils::IsAutocompleteEnabled(mFocusedInput)) {
       nsCOMPtr <nsIFormAutoComplete> formAutoComplete =
         do_GetService("@mozilla.org/satchel/form-autocomplete;1", &rv);
@@ -645,52 +648,42 @@ nsresult
 nsFormFillController::PerformInputListAutoComplete(nsIAutoCompleteResult* aPreviousResult)
 {
   // If an <input> is focused, check if it has a list="<datalist>" which can
   // provide the list of suggestions.
 
   nsresult rv;
   nsCOMPtr<nsIAutoCompleteResult> result;
 
-  bool dummy;
-  if (!mPwmgrInputs.Get(mFocusedInputNode, &dummy)) {
-    nsCOMPtr <nsIInputListAutoComplete> inputListAutoComplete =
-      do_GetService("@mozilla.org/satchel/inputlist-autocomplete;1", &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = inputListAutoComplete->AutoCompleteSearch(aPreviousResult,
-                                                   mLastSearchString,
-                                                   mFocusedInput,
-                                                   getter_AddRefs(result));
-    NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr <nsIInputListAutoComplete> inputListAutoComplete =
+    do_GetService("@mozilla.org/satchel/inputlist-autocomplete;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = inputListAutoComplete->AutoCompleteSearch(aPreviousResult,
+                                                 mLastSearchString,
+                                                 mFocusedInput,
+                                                 getter_AddRefs(result));
+  NS_ENSURE_SUCCESS(rv, rv);
 
-    if (mFocusedInput) {
-      nsCOMPtr<nsIDOMHTMLElement> list;
-      mFocusedInput->GetList(getter_AddRefs(list));
+  if (mFocusedInput) {
+    nsCOMPtr<nsIDOMHTMLElement> list;
+    mFocusedInput->GetList(getter_AddRefs(list));
 
-      // Add a mutation observer to check for changes to the items in the <datalist>
-      // and update the suggestions accordingly.
-      nsCOMPtr<nsINode> node = do_QueryInterface(list);
-      if (mListNode != node) {
-        if (mListNode) {
-          mListNode->RemoveMutationObserver(this);
-          mListNode = nullptr;
-        }
-        if (node) {
-          node->AddMutationObserverUnlessExists(this);
-          mListNode = node;
-        }
+    // Add a mutation observer to check for changes to the items in the <datalist>
+    // and update the suggestions accordingly.
+    nsCOMPtr<nsINode> node = do_QueryInterface(list);
+    if (mListNode != node) {
+      if (mListNode) {
+        mListNode->RemoveMutationObserver(this);
+        mListNode = nullptr;
+      }
+      if (node) {
+        node->AddMutationObserverUnlessExists(this);
+        mListNode = node;
       }
     }
-  } else {
-    result = aPreviousResult;
-
-    // If this is a password manager input mLastSearchResult will be a JS
-    // object (wrapped in an XPConnect reflector), so we need to take care not
-    // to hold onto it for too long.
-    mLastSearchResult = nullptr;
   }
 
   if (mLastListener) {
     mLastListener->OnSearchResult(this, result);
   }
 
   return NS_OK;
 }