Merge mobile-browser -> mobile2.
authorMatt Brubeck <mbrubeck@mozilla.com>
Wed, 15 Sep 2010 14:11:37 -0700
changeset 66650 98bc118caee2b337187f7116eda2a83ab2f30db5
parent 66649 f865a1973d750f3cedce1226e856b3e42fd30bf7 (current diff)
parent 66591 1db691fab2b14ac4aac3251f775ca4e3b739a85f (diff)
child 66651 098bbfa2b01e8eed3842ce9bddad469baa4c8bab
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
Merge mobile-browser -> mobile2.
mobile/app/mobile.js
mobile/chrome/content/browser-ui.js
mobile/chrome/content/browser.js
mobile/chrome/content/browser.xul
mobile/chrome/content/content.js
--- a/mobile/app/mobile.js
+++ b/mobile/app/mobile.js
@@ -176,21 +176,21 @@ pref("extensions.logging.enabled", false
 pref("extensions.hideInstallButton", true);
 pref("extensions.showMismatchUI", false);
 pref("extensions.hideUpdateButton", false);
 
 pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%");
 
 /* preferences for the Get Add-ons pane */
 pref("extensions.getAddons.cache.enabled", true);
-pref("extensions.getAddons.maxResults", 5);
+pref("extensions.getAddons.maxResults", 15);
 pref("extensions.getAddons.recommended.browseURL", "https://addons.mozilla.org/%LOCALE%/mobile/recommended/");
-pref("extensions.getAddons.recommended.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/list/featured/all/10/%OS%/%VERSION%");
+pref("extensions.getAddons.recommended.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/list/featured/all/%MAX_RESULTS%/%OS%/%VERSION%");
 pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/mobile/search?q=%TERMS%");
-pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/%TERMS%/all/10/%OS%/%VERSION%");
+pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%");
 pref("extensions.getAddons.browseAddons", "https://addons.mozilla.org/%LOCALE%/mobile/");
 
 /* blocklist preferences */
 pref("extensions.blocklist.enabled", true);
 pref("extensions.blocklist.interval", 86400);
 pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/");
 pref("extensions.blocklist.detailsURL", "https://www.mozilla.com/%LOCALE%/blocklist/");
 
@@ -383,19 +383,23 @@ pref("browser.ui.pinch.scalingFactor", 5
 // Touch radius (area around the touch location to look for target elements):
 pref("browser.ui.touch.left", 8);
 pref("browser.ui.touch.right", 8);
 pref("browser.ui.touch.top", 12);
 pref("browser.ui.touch.bottom", 4);
 pref("browser.ui.touch.weight.visited", 120); // percentage
 
 // plugins
+#if ANDROID
 pref("plugin.disable", true);
-pref("plugin.default_plugin_disabled", true);
 pref("dom.ipc.plugins.enabled", false);
+#else
+pref("plugin.disable", false);
+pref("dom.ipc.plugins.enabled", true);
+#endif
 
 // product URLs
 // The breakpad report server to link to in about:crashes
 pref("breakpad.reportURL", "http://crash-stats.mozilla.com/report/index/");
 pref("app.releaseNotesURL", "http://www.mozilla.com/%LOCALE%/mobile/%VERSION%/releasenotes/");
 //pref("app.support.baseURL", "http://support.mozilla.com/1/mobile/%VERSION%/%OS%/%LOCALE%/");
 pref("app.support.baseURL", "http://mobile.support.mozilla.com/");
 pref("app.faqURL", "http://www.mozilla.com/%LOCALE%/mobile/faq/");
@@ -440,30 +444,32 @@ pref("browser.search.param.yahoo-fr", "m
 pref("browser.search.param.yahoo-fr-cjkt", "moz35");
 pref("browser.search.param.yahoo-fr-ja", "mozff");
 #endif
 
 /* app update prefs */
 pref("app.update.timer", 60000); // milliseconds (1 min)
 
 #ifdef MOZ_UPDATER
-pref("app.update.auto", true);
+pref("app.update.enabled", true);
+pref("app.update.timerFirstInterval", 20000); // milliseconds
+pref("app.update.auto", false);
 pref("app.update.channel", "@MOZ_UPDATE_CHANNEL@");
 pref("app.update.mode", 1);
 pref("app.update.silent", false);
 pref("app.update.url", "https://aus2.mozilla.org/update/4/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PLATFORM_VERSION%/update.xml");
 pref("app.update.nagTimer.restart", 86400);
 pref("app.update.promptWaitTime", 43200);
 pref("app.update.idletime", 60);
 pref("app.update.showInstalledUI", false);
 pref("app.update.incompatible.mode", 0);
+pref("app.update.download.backgroundInterval", 0);
 
 #ifdef MOZ_OFFICIAL_BRANDING
 pref("app.update.interval", 86400);
-pref("app.update.download.backgroundInterval", 600);
 pref("app.update.url.manual", "http://www.mozilla.com/%LOCALE%/m/");
 pref("app.update.url.details", "http://www.mozilla.com/%LOCALE%/mobile/releases/");
 #else
 pref("app.update.interval", 28800);
 #endif
 #endif
 
 // replace newlines with spaces on paste into single-line text boxes
--- a/mobile/app/profile/extensions/feedback@mobile.mozilla.org/content/overlay.js
+++ b/mobile/app/profile/extensions/feedback@mobile.mozilla.org/content/overlay.js
@@ -34,16 +34,39 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 var Feedback = {
   init: function(aEvent) {
     let appInfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
     document.getElementById("feedback-about").setAttribute("desc", appInfo.version);
+
+    // A simple frame script to fill in the referrer page
+    messageManager.loadFrameScript("data:,addMessageListener('Feedback:InitPage', function(m) { content.document.getElementById('id_url').value = m.json.referrer; });", true);
+
+    // Try to delay the widget initialization during startup
+    messageManager.addMessageListener("DOMContentLoaded", function() {
+      // We only want to delay one time
+      messageManager.removeMessageListener("DOMContentLoaded", arguments.callee, true);
+
+      // We unhide the panelUI so the XBL and settings can initialize
+      document.getElementById("feedback-container").hidden = false;
+    });
+  },
+
+  openFeedback: function(aURL) {
+    let currentURL = Browser.selectedBrowser.currentURI.spec;
+    let newTab = BrowserUI.newTab(aURL);
+
+    // Tell the feedback page to fill in the referrer URL
+    newTab.browser.messageManager.addMessageListener("DOMContentLoaded", function() {
+      newTab.browser.messageManager.removeMessageListener("DOMContentLoaded", arguments.callee, true);
+      newTab.browser.messageManager.sendAsyncMessage("Feedback:InitPage", { referrer: currentURL });
+    });
   },
 
   openReadme: function() {
     let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
     let url = formatter.formatURLPref("app.releaseNotesURL");
     BrowserUI.newTab(url);
   },
 
--- a/mobile/app/profile/extensions/feedback@mobile.mozilla.org/content/overlay.xul
+++ b/mobile/app/profile/extensions/feedback@mobile.mozilla.org/content/overlay.xul
@@ -47,26 +47,26 @@
 <overlay id="feedback-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript" src="chrome://feedback/content/overlay.js"/>
 
   <box id="panel-controls">
     <toolbarbutton id="tool-feedback" autocheck="true" type="radio" group="1" class="panel-button button-dark" linkedpanel="feedback-container" insertbefore="tool-addons"/>
   </box>
 
   <deck id="panel-items">
-    <vbox id="feedback-container" flex="1">
+    <vbox id="feedback-container" flex="1" hidden="true">
       <hbox id="feedback-header" class="panel-header">
         <label value="&feedbackHeader.label;" flex="1"/>
       </hbox>
       <notificationbox id="feedback-messages" flex="1">
         <richlistbox id="feedback-list" flex="1" onselect="this.ensureSelectedElementIsVisible()">
           <settings id="feedback-communicate" label="&feedback.communicate.title;">
             <setting title="&feedback.feedback.title;" type="control">
-              <button id="feedback-feedback-happy" class="button-dark" oncommand="BrowserUI.newTab('http://m.input.mozilla.com/en-US/happy');"/>
-              <button id="feedback-feedback-sad" class="button-dark" oncommand="BrowserUI.newTab('http://m.input.mozilla.com/en-US/sad');"/>
+              <button id="feedback-feedback-happy" class="button-dark" oncommand="Feedback.openFeedback('http://m.input.mozilla.com/en-US/happy');"/>
+              <button id="feedback-feedback-sad" class="button-dark" oncommand="Feedback.openFeedback('http://m.input.mozilla.com/en-US/sad');"/>
             </setting>
           </settings>
           <settings id="feedback-information" label="&feedback.information.title;">
             <setting id="feedback-about" title="&feedback.about.title;" type="control">
               <button id="feedback-about-button" label="&feedback.about.button;" oncommand="Feedback.openReadme();"/>
             </setting>
           </settings>
           <settings id="feedback-tools" label="&feedback.tools.title;">
--- a/mobile/chrome/content/LoginManagerChild.js
+++ b/mobile/chrome/content/LoginManagerChild.js
@@ -321,17 +321,17 @@ var loginManager = {
             realm = null;
         }
 
         return realm;
     },
 
 
     _getActionOrigin : function (form) {
-        var uriString = form.action;
+        var uriString = form.mozActionUri;
 
         // A blank or mission action submits to where it came from.
         if (uriString == "")
             uriString = form.baseURI; // ala bug 297761
 
         return this._getPasswordOrigin(uriString, true);
     },
 
--- a/mobile/chrome/content/bindings/extensions.xml
+++ b/mobile/chrome/content/bindings/extensions.xml
@@ -22,17 +22,17 @@
             <xul:label class="normal" xbl:inherits="value=version"/>
             <xul:spacer flex="1000"/>
             <xul:label class="normal" xbl:inherits="value=typeLabel"/>
           </xul:hbox>
           <xul:vbox>
             <xul:label class="normal hide-on-select" xbl:inherits="value=description" crop="end" flex="1"/>
             <xul:description class="normal show-on-select" xbl:inherits="xbl:text=description" flex="1"/>
             <xul:label class="updateStatus normal-bold" xbl:inherits="value=updateStatus"/>
-            <xul:label class="blockStatus normal-bold" xbl:inherits="value=blockedStatus"/>
+            <xul:label class="blockedStatus normal-bold" xbl:inherits="value=blockedStatus"/>
           </xul:vbox>
         </xul:vbox>
       </xul:hbox>
       <xul:hbox class="show-on-select buttons-box">
         <xul:button anonid="options-button" type="checkbox" class="addon-options" label="&addonOptions.label;"
                     oncommand="document.getBindingParent(this).toggleOptions();"/>
         <xul:spacer flex="1"/>
         <xul:button anonid="enable-button" class="show-on-disable hide-on-enable hide-on-uninstall addon-enable" label="&addonEnable.label;"
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -1094,17 +1094,19 @@ var TapHighlightHelper = {
     this._overlay.style.display = "none";
   }
 };
 
 var PageActions = {
   init: function init() {
     this.register("pageaction-reset", this.updatePagePermissions, this);
     this.register("pageaction-password", this.updateForgetPassword, this);
+#ifdef NS_PRINTING
     this.register("pageaction-saveas", this.updatePageSaveAs, this);
+#endif
     this.register("pageaction-share", this.updateShare, this);
     this.register("pageaction-search", BrowserSearch.updatePageSearchEngines, BrowserSearch);
   },
 
   /**
    * @param aId id of a pageaction element
    * @param aCallback function that takes an element and returns true if it should be visible
    * @param aThisObj (optional) scope object for aCallback
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -465,23 +465,23 @@ var Browser = {
     let hasLocal = Util.isLocalScheme(currentURI);
 
     if (hasLocal != useLocal) {
       let oldTab = this.selectedTab;
       if (currentURI == "about:blank" && !browser.canGoBack && !browser.canGoForward) {
         this.closeTab(oldTab);
         oldTab = null;
       }
-      let tab = Browser.addTab(aURI, true, oldTab);
-      tab.browser.stop();
+      Browser.addTab(aURI, true, oldTab, aParams);
     }
-
-    let params = aParams || {};
-    let flags = params.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
-    getBrowser().loadURIWithFlags(aURI, flags, params.referrerURI, params.charset, params.postData);
+    else {
+      let params = aParams || {};
+      let flags = params.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+      getBrowser().loadURIWithFlags(aURI, flags, params.referrerURI, params.charset, params.postData);
+    }
   },
 
   /**
    * Return the currently active <browser> object
    */
   get selectedBrowser() {
     return this._selectedTab.browser;
   },
@@ -508,22 +508,22 @@ var Browser = {
   getTabFromChrome: function getTabFromChrome(chromeTab) {
     for (var t = 0; t < this._tabs.length; t++) {
       if (this._tabs[t].chromeTab == chromeTab)
         return this._tabs[t];
     }
     return null;
   },
 
-  addTab: function(uri, bringFront, aOwner) {
-    let newTab = new Tab(uri);
+  addTab: function(aURI, aBringFront, aOwner, aParams) {
+    let newTab = new Tab(aURI, aParams);
     newTab.owner = aOwner || null;
     this._tabs.push(newTab);
 
-    if (bringFront)
+    if (aBringFront)
       this.selectedTab = newTab;
 
     let event = document.createEvent("Events");
     event.initEvent("TabOpen", true, false);
     newTab.chromeTab.dispatchEvent(event);
     newTab.browser.messageManager.sendAsyncMessage("Browser:TabOpen");
 
     return newTab;
@@ -647,17 +647,17 @@ var Browser = {
   doCommand: function(cmd) {
     switch (cmd) {
       case "cmd_fullscreen":
         window.fullScreen = !window.fullScreen;
         break;
     }
   },
 
-  getNotificationBox: function getNotificationBox() {
+  getNotificationBox: function getNotificationBox(aBrowser) {
     return document.getElementById("notifications");
   },
 
   removeTransientNotificationsForTab: function removeTransientNotificationsForTab(aTab) {
     let notificationBox = this.getNotificationBox();
     let notifications = notificationBox.allNotifications;
     for (let n = notifications.length - 1; n >= 0; n--) {
       let notification = notifications[n];
@@ -1823,18 +1823,18 @@ const gSessionHistoryObserver = {
 var MemoryObserver = {
   observe: function mo_observe() {
     window.QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(Ci.nsIDOMWindowUtils).garbageCollect();
     Components.utils.forceGC();
   }
 };
 
-function getNotificationBox(aWindow) {
-  return Browser.getNotificationBox();
+function getNotificationBox(aBrowser) {
+  return Browser.getNotificationBox(aBrowser);
 }
 
 function importDialog(aParent, aSrc, aArguments) {
   // load the dialog with a synchronous XHR
   let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
   xhr.open("GET", aSrc, false);
   xhr.overrideMimeType("text/xml");
   xhr.send(null);
@@ -2170,30 +2170,32 @@ var OfflineApps = {
 
   receiveMessage: function receiveMessage(aMessage) {
     if (aMessage.name == "Browser:MozApplicationManifest") {
       this.offlineAppRequested(aMessage.json);
     }
   }
 };
 
-function Tab(aURI) {
+function Tab(aURI, aParams) {
   this._id = null;
   this._browser = null;
   this._state = null;
   this._listener = null;
   this._loading = false;
   this._chromeTab = null;
   this.owner = null;
 
   // Set to 0 since new tabs that have not been viewed yet are good tabs to
   // toss if app needs more memory.
   this.lastSelected = 0;
 
-  this.create(aURI);
+  // aParams is an object that contains some properties for the initial tab
+  // loading like flags, a referrerURI, a charset or even a postData.
+  this.create(aURI, aParams || {});
 }
 
 Tab.prototype = {
   get browser() {
     return this._browser;
   },
 
   get chromeTab() {
@@ -2274,19 +2276,30 @@ Tab.prototype = {
     if (!this._loading) throw "Not Loading!";
     this._loading = false;
   },
 
   isLoading: function isLoading() {
     return this._loading;
   },
 
-  create: function create(aURI) {
+  create: function create(aURI, aParams) {
     this._chromeTab = document.getElementById("tabs").addTab();
-    this._createBrowser(aURI);
+    let browser = this._createBrowser(aURI);
+
+    // Attach a separate progress listener to the browser
+    let flags = Ci.nsIWebProgress.NOTIFY_LOCATION |
+                Ci.nsIWebProgress.NOTIFY_SECURITY |
+                Ci.nsIWebProgress.NOTIFY_STATE_NETWORK |
+                Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT;
+    this._listener = new ProgressController(this);
+    browser.webProgress.addProgressListener(this._listener, flags);
+
+    let flags = aParams.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+    browser.loadURIWithFlags(aURI, flags, aParams.referrerURI, aParams.charset, aParams.postData);
   },
 
   destroy: function destroy() {
     document.getElementById("tabs").removeTab(this._chromeTab);
     this._chromeTab = null;
     this._destroyBrowser();
   },
 
@@ -2306,30 +2319,23 @@ Tab.prototype = {
     browser.setAttribute("remote", (!useLocal && useRemote) ? "true" : "false");
 
     // Append the browser to the document, which should start the page load
     document.getElementById("browsers").appendChild(browser);
 
     // stop about:blank from loading
     browser.stop();
 
-    // Attach a separate progress listener to the browser
-    let flags = Ci.nsIWebProgress.NOTIFY_LOCATION |
-                Ci.nsIWebProgress.NOTIFY_SECURITY |
-                Ci.nsIWebProgress.NOTIFY_STATE_NETWORK |
-                Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT;
-    this._listener = new ProgressController(this);
-    browser.webProgress.addProgressListener(this._listener, flags);
-
-    browser.setAttribute("src", aURI);
 
     let self = this;
     browser.messageManager.addMessageListener("MozScrolledAreaChanged", function() {
       self.updateDefaultZoomLevel();
     });
+
+   return browser;
   },
 
   _destroyBrowser: function _destroyBrowser() {
     if (this._browser) {
       var browser = this._browser;
       browser.removeProgressListener(this._listener);
 
       this._browser = null;
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -332,18 +332,20 @@
           <image id="identity-popup-encryption-icon"/>
           <description id="identity-popup-encryption-label"/>
         </vbox>
       </hbox>
 
       <hbox id="pageactions-container" class="window-width" hidden="true">
         <pageaction id="pageaction-findinpage" title="&pageactions.findInPage;"
           onclick="FindHelperUI.show();"/>
+#ifdef NS_PRINTING
         <pageaction id="pageaction-saveas" title="&pageactions.saveas.pdf;"
           onclick="PageActions.savePageAsPDF();"/>
+#endif
         <pageaction id="pageaction-share" title="&pageactions.share.page;"
           onclick="SharingUI.show(getBrowser().currentURI.spec, getBrowser().contentTitle);"/>
         <pageaction id="pageaction-password" title="&pageactions.password.forget;"
           onclick="PageActions.forgetPassword(); PageActions.hideItem(this);"/>
         <pageaction id="pageaction-reset" title="&pageactions.reset;"
           onclick="PageActions.clearPagePermissions(); PageActions.hideItem(this);"/>
         <pageaction id="pageaction-search" title="&pageactions.search.addNew;"/>
       </hbox>
--- a/mobile/chrome/content/content.js
+++ b/mobile/chrome/content/content.js
@@ -402,16 +402,17 @@ Content.prototype = {
       case "Browser:MouseUp": {
         let element = elementFromPoint(x, y);
         if (modifiers == Ci.nsIDOMNSEvent.CONTROL_MASK) {
           let uri = Util.getHrefForElement(element);
           if (uri)
             sendAsyncMessage("Browser:OpenURI", { uri: uri });
         } else if (!this._formAssistant.open(element)) {
           sendAsyncMessage("FindAssist:Hide", { });
+          this._sendMouseEvent("mousemove", element, x, y);
           this._sendMouseEvent("mousedown", element, x, y);
           this._sendMouseEvent("mouseup", element, x, y);
         }
         break;
       }
 
       case "Browser:MouseCancel":
         this._overlayTimeout.clear();
--- a/mobile/chrome/content/extensions.js
+++ b/mobile/chrome/content/extensions.js
@@ -309,17 +309,18 @@ var ExtensionsView = {
         listitem.setAttribute("isDisabled", !addon.isActive);
         listitem.setAttribute("appDisabled", addon.appDisabled);
         listitem.setAttribute("appManaged", appManaged);
         listitem.setAttribute("description", addon.description);
         listitem.setAttribute("optionsURL", addon.optionsURL);
         listitem.setAttribute("opType", opType);
         listitem.setAttribute("updateable", updateable);
         listitem.setAttribute("isReadonly", !uninstallable);
-        listitem.setAttribute("blockedStatus", blocked);
+        if (blocked)
+          listitem.setAttribute("blockedStatus", blocked);
         listitem.addon = addon;
         self._list.insertBefore(listitem, self._repoItem);
       }
 
       // Load the search engines
       let defaults = Services.search.getDefaultEngines({ }).map(function (e) e.name);
       function isDefault(aEngine)
         defaults.indexOf(aEngine.name) != -1
@@ -533,17 +534,18 @@ var ExtensionsView = {
       addon.type = types[addon.type];
 
       let listitem = this._createItem(addon, "search");
       listitem.setAttribute("description", addon.description);
       listitem.setAttribute("homepageURL", addon.homepageURL);
       listitem.install = addon.install;
       listitem.setAttribute("sourceURL", addon.install.sourceURI.spec);
       if (!aIsRecommended)
-        listitem.setAttribute("rating", addon.rating);
+        listitem.setAttribute("rating", addon.averageRating);
+
       let item = this._list.appendChild(listitem);
 
       if (aSelectFirstResult && !foundItem) {
         foundItem = true;
         this._list.selectItem(item);
         this._list.scrollBoxObject.scrollToElement(item);
       }
     }
--- a/mobile/components/AddonUpdateService.js
+++ b/mobile/components/AddonUpdateService.js
@@ -75,25 +75,30 @@ AddonUpdateService.prototype = {
       return;
 
     // If we already auto-upgraded and installed new versions, ignore this check
     if (gNeedsRestart)
       return;
 
     Services.io.offline = false;
 
+    // Assume we are doing a periodic update check
+    let reason = AddonManager.UPDATE_WHEN_PERIODIC_UPDATE;
+    if (!aTimer)
+      reason = AddonManager.UPDATE_WHEN_USER_REQUESTED;
+
     AddonManager.getAddonsByTypes(null, function(aAddonList) {
       aAddonList.forEach(function(aAddon) {
         if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE) {
           let data = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
           data.data = JSON.stringify({ id: aAddon.id, name: aAddon.name });
           Services.obs.notifyObservers(data, "addon-update-started", null);
 
           let listener = new UpdateCheckListener();
-          aAddon.findUpdates(listener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+          aAddon.findUpdates(listener, reason);
         }
       });
     });
   }
 };
 
 // -----------------------------------------------------------------------
 // Add-on update listener. Starts a download for any add-on with a viable
--- a/mobile/components/LoginManager.js
+++ b/mobile/components/LoginManager.js
@@ -156,20 +156,20 @@ LoginManager.prototype = {
 
     /*
      * receiveMessage
      *
      * Receives messages from content process.
      */
     receiveMessage: function (message) {
         // local helper function
-        function getPrompter(aWindow) {
+        function getPrompter(aBrowser) {
             var prompterSvc = Cc["@mozilla.org/login-manager/prompter;1"].
                               createInstance(Ci.nsILoginManagerPrompter);
-            prompterSvc.init(aWindow);
+            prompterSvc.init(aBrowser);
             return prompterSvc;
         }
 
         switch (message.name) {
             case "PasswordMgr:GetPasswords":
                 // If there are no logins for this site, bail out now.
                 if (!this.countLogins(message.json.formOrigin, "", null))
                     return { foundLogins: {} };
@@ -197,19 +197,17 @@ LoginManager.prototype = {
                 var formSubmitURL = json.formSubmitURL;
 
                 if (!this.getLoginSavingEnabled(hostname)) {
                     this.log("(form submission ignored -- saving is " +
                              "disabled for: " + hostname + ")");
                     return {};
                 }
 
-                // Since prompt services ask for window, we try to get the document
-                // window.  Prompt service needs to take browser elements.
-                var win = message.target.contentWindow || null;
+                var browser = message.target;
 
                 var formLogin = new this._nsLoginInfo();
 
                 formLogin.init(hostname, formSubmitURL, null,
                             json.usernameValue,
                             json.passwordValue,
                             json.usernameField,
                             json.passwordField);
@@ -223,17 +221,17 @@ LoginManager.prototype = {
 
                     if (logins.length == 0) {
                         // Could prompt to save this as a new password-only login.
                         // This seems uncommon, and might be wrong, so ignore.
                         this.log("(no logins for this host -- pwchange ignored)");
                         return {};
                     }
 
-                    var prompter = getPrompter(win);
+                    var prompter = getPrompter(browser);
 
                     if (logins.length == 1) {
                         var oldLogin = logins[0];
                         formLogin.username      = oldLogin.username;
                         formLogin.usernameField = oldLogin.usernameField;
 
                         prompter.promptToChangePassword(oldLogin, formLogin);
                     } else {
@@ -274,33 +272,33 @@ LoginManager.prototype = {
                     }
 
                     if (existingLogin) {
                         this.log("Found an existing login matching this form submission");
 
                         // Change password if needed.
                         if (existingLogin.password != formLogin.password) {
                             this.log("...passwords differ, prompting to change.");
-                            prompter = getPrompter(win);
+                            prompter = getPrompter(browser);
                             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);
                             this.modifyLogin(existingLogin, propBag);
                         }
 
                         return {};
                     }
 
 
                     // Prompt user to save login (via dialog or notification bar)
-                    prompter = getPrompter(win);
+                    prompter = getPrompter(browser);
                     prompter.promptToSavePassword(formLogin);
                 }
                 return {};
 
             default:
                 throw "Unexpected message " + message.name;
         }
     },
new file mode 100644
--- /dev/null
+++ b/mobile/components/LoginManagerPrompter.idl
@@ -0,0 +1,99 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Justin Dolske <dolske@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+#include "nsISupports.idl"
+
+interface nsILoginInfo;
+interface nsIFrameLoaderOwner;
+
+[scriptable, uuid(3f385080-aef5-11df-94e2-0800200c9a66)]
+
+interface nsILoginManagerPrompter : nsISupports {
+    /**
+     * Initialize the prompter. Must be called before using other interfaces.
+     *
+     * @param aBrowser
+     *        A browser element in which the user is doing some
+     *        login-related action in need to prompt them for something.
+     *        The prompt will be associated with browser.
+     */
+    void init(in nsIFrameLoaderOwner aBrowser);
+
+    /**
+     * Ask the user if they want to save a login (Yes, Never, Not Now)
+     *
+     * @param aLogin
+     *        The login to be saved.
+     */
+    void promptToSavePassword(in nsILoginInfo aLogin);
+
+    /**
+     * Ask the user if they want to change a login's password. If the
+     * user consents, modifyLogin() will be called.
+     *
+     * @param aOldLogin
+     *        The existing login (with the old password).
+     * @param aNewLogin
+     *        The new login.
+     */
+    void promptToChangePassword(in nsILoginInfo aOldLogin,
+                                in nsILoginInfo aNewLogin);
+
+    /**
+     * Ask the user if they want to change the password for one of
+     * multiple logins, when the caller can't determine exactly which
+     * login should be changed. If the user consents, modifyLogin() will
+     * be called.
+     *
+     * @param logins
+     *        An array of existing logins.
+     * @param count
+     *        (length of the array)
+     * @param aNewLogin
+     *        The new login. 
+     *
+     * Note: Because the caller does not know the username of the login
+     *       to be changed, aNewLogin.username and aNewLogin.usernameField
+     *       will be set (using the user's selection) before modifyLogin()
+     *       is called.
+     */
+    void promptToChangePasswordWithUsernames(
+            [array, size_is(count)] in nsILoginInfo logins,
+            in PRUint32 count,
+            in nsILoginInfo aNewLogin);
+};
+
new file mode 100644
--- /dev/null
+++ b/mobile/components/LoginManagerPrompter.js
@@ -0,0 +1,644 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Justin Dolske <dolske@mozilla.com> (original author)
+ *  Ehsan Akhgari <ehsan.akhgari@gmail.com>
+ *  Wes Johnston  <wjohnston@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+/* ==================== LoginManagerPrompter ==================== */
+/*
+ * LoginManagerPrompter
+ *
+ * Implements interfaces for prompting the user to enter/save/change auth info.
+ *
+ * nsILoginManagerPrompter: Used by Login Manager for saving/changing logins
+ * found in HTML forms.
+ */
+function LoginManagerPrompter() {
+}
+
+LoginManagerPrompter.prototype = {
+
+    classID : Components.ID("97d12931-abe2-11df-94e2-0800200c9a66"),
+    QueryInterface : XPCOMUtils.generateQI([Ci.nsILoginManagerPrompter]),
+
+    _factory       : null,
+    _browser       : null,
+    _debug         : false, // mirrors signon.debug
+
+    __pwmgr : null, // Password Manager service
+    get _pwmgr() {
+        if (!this.__pwmgr)
+            this.__pwmgr = Cc["@mozilla.org/login-manager;1"].
+                           getService(Ci.nsILoginManager);
+        return this.__pwmgr;
+    },
+
+    __promptService : null, // Prompt service for user interaction
+    get _promptService() {
+        if (!this.__promptService)
+            this.__promptService =
+                Cc["@mozilla.org/embedcomp/prompt-service;1"].
+                getService(Ci.nsIPromptService2);
+        return this.__promptService;
+    },
+    
+    __strBundle : null, // String bundle for L10N
+    get _strBundle() {
+        if (!this.__strBundle) {
+            var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
+                             getService(Ci.nsIStringBundleService);
+            this.__strBundle = bunService.createBundle(
+                        "chrome://passwordmgr/locale/passwordmgr.properties");
+            if (!this.__strBundle)
+                throw "String bundle for Login Manager not present!";
+        }
+
+        return this.__strBundle;
+    },
+
+    __brandBundle : null, // String bundle for L10N
+    get _brandBundle() {
+        if (!this.__brandBundle) {
+            var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
+                             getService(Ci.nsIStringBundleService);
+            this.__brandBundle = bunService.createBundle(
+                        "chrome://branding/locale/brand.properties");
+            if (!this.__brandBundle)
+                throw "Branding string bundle not present!";
+        }
+
+        return this.__brandBundle;
+    },
+
+
+    __ellipsis : null,
+    get _ellipsis() {
+        if (!this.__ellipsis) {
+            this.__ellipsis = "\u2026";
+            try {
+                this.__ellipsis = Services.prefs.getComplexValue(
+                                    "intl.ellipsis", Ci.nsIPrefLocalizedString).data;
+            } catch (e) { }
+        }
+        return this.__ellipsis;
+    },
+
+
+    /*
+     * log
+     *
+     * Internal function for logging debug messages to the Error Console window.
+     */
+    log : function (message) {
+        if (!this._debug)
+            return;
+
+        dump("Pwmgr Prompter: " + message + "\n");
+        Services.console.logStringMessage("Pwmgr Prompter: " + message);
+    },
+
+
+    /* ---------- nsILoginManagerPrompter prompts ---------- */
+
+
+
+
+    /*
+     * init
+     *
+     */
+    init : function (aBrowser, aFactory) {
+        this._browser = aBrowser;
+        this._factory = aFactory || null;
+
+        var prefBranch = Services.prefs.getBranch("signon.");
+        this._debug = prefBranch.getBoolPref("debug");
+        this.log("===== initialized =====");
+    },
+
+
+    /*
+     * promptToSavePassword
+     *
+     */
+    promptToSavePassword : function (aLogin) {
+        var notifyBox = this._getNotifyBox();
+
+        if (notifyBox)
+            this._showSaveLoginNotification(notifyBox, aLogin);
+        else
+            this._showSaveLoginDialog(aLogin);
+    },
+
+
+    /*
+     * _showLoginNotification
+     *
+     * Displays a notification bar.
+     *
+     */
+    _showLoginNotification : function (aNotifyBox, aName, aText, aButtons) {
+        var oldBar = aNotifyBox.getNotificationWithValue(aName);
+        const priority = aNotifyBox.PRIORITY_INFO_MEDIUM;
+
+        this.log("Adding new " + aName + " notification bar");
+        var newBar = aNotifyBox.appendNotification(
+                                aText, aName,
+                                "chrome://mozapps/skin/passwordmgr/key.png",
+                                priority, aButtons);
+
+        // The page we're going to hasn't loaded yet, so we want to persist
+        // across the first location change.
+        newBar.persistence++;
+
+        // Sites like Gmail perform a funky redirect dance before you end up
+        // at the post-authentication page. I don't see a good way to
+        // heuristically determine when to ignore such location changes, so
+        // we'll try ignoring location changes based on a time interval.
+        newBar.timeout = Date.now() + 20000; // 20 seconds
+
+        if (oldBar) {
+            this.log("(...and removing old " + aName + " notification bar)");
+            aNotifyBox.removeNotification(oldBar);
+        }
+    },
+
+
+    /*
+     * _showSaveLoginNotification
+     *
+     * Displays a notification bar (rather than a popup), to allow the user to
+     * save the specified login. This allows the user to see the results of
+     * their login, and only save a login which they know worked.
+     *
+     */
+    _showSaveLoginNotification : function (aNotifyBox, aLogin) {
+
+        // Ugh. We can't use the strings from the popup window, because they
+        // have the access key marked in the string (eg "Mo&zilla"), along
+        // with some weird rules for handling access keys that do not occur
+        // in the string, for L10N. See commonDialog.js's setLabelForNode().
+        var neverButtonText =
+              this._getLocalizedString("notifyBarNeverForSiteButtonText");
+        var neverButtonAccessKey =
+              this._getLocalizedString("notifyBarNeverForSiteButtonAccessKey");
+        var rememberButtonText =
+              this._getLocalizedString("notifyBarRememberButtonText");
+        var rememberButtonAccessKey =
+              this._getLocalizedString("notifyBarRememberButtonAccessKey");
+        var notNowButtonText =
+              this._getLocalizedString("notifyBarNotNowButtonText");
+        var notNowButtonAccessKey =
+              this._getLocalizedString("notifyBarNotNowButtonAccessKey");
+
+        var brandShortName =
+              this._brandBundle.GetStringFromName("brandShortName");
+        var displayHost = this._getShortDisplayHost(aLogin.hostname);
+        var notificationText;
+        if (aLogin.username) {
+            var displayUser = this._sanitizeUsername(aLogin.username);
+            notificationText  = this._getLocalizedString(
+                                        "saveLoginText",
+                                        [brandShortName, displayUser, displayHost]);
+        } else {
+            notificationText  = this._getLocalizedString(
+                                        "saveLoginTextNoUsername",
+                                        [brandShortName, displayHost]);
+        }
+
+        // The callbacks in |buttons| have a closure to access the variables
+        // in scope here; set one to |this._pwmgr| so we can get back to pwmgr
+        // without a getService() call.
+        var pwmgr = this._pwmgr;
+
+
+        var buttons = [
+            // "Remember" button
+            {
+                label:     rememberButtonText,
+                accessKey: rememberButtonAccessKey,
+                popup:     null,
+                callback: function(aNotificationBar, aButton) {
+                    pwmgr.addLogin(aLogin);
+                }
+            },
+
+            // "Never for this site" button
+            {
+                label:     neverButtonText,
+                accessKey: neverButtonAccessKey,
+                popup:     null,
+                callback: function(aNotificationBar, aButton) {
+                    pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
+                }
+            },
+
+            // "Not now" button
+            {
+                label:     notNowButtonText,
+                accessKey: notNowButtonAccessKey,
+                popup:     null,
+                callback:  function() { /* NOP */ } 
+            }
+        ];
+
+        this._showLoginNotification(aNotifyBox, "password-save",
+             notificationText, buttons);
+    },
+
+
+    /*
+     * _showSaveLoginDialog
+     *
+     * Called when we detect a new login in a form submission,
+     * asks the user what to do.
+     *
+     */
+    _showSaveLoginDialog : function (aLogin) {
+        const buttonFlags = Ci.nsIPrompt.BUTTON_POS_1_DEFAULT +
+            (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
+            (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1) +
+            (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2);
+
+        var brandShortName =
+                this._brandBundle.GetStringFromName("brandShortName");
+        var displayHost = this._getShortDisplayHost(aLogin.hostname);
+
+        var dialogText;
+        if (aLogin.username) {
+            var displayUser = this._sanitizeUsername(aLogin.username);
+            dialogText = this._getLocalizedString(
+                                 "saveLoginText",
+                                 [brandShortName, displayUser, displayHost]);
+        } else {
+            dialogText = this._getLocalizedString(
+                                 "saveLoginTextNoUsername",
+                                 [brandShortName, displayHost]);
+        }
+        var dialogTitle        = this._getLocalizedString(
+                                        "savePasswordTitle");
+        var neverButtonText    = this._getLocalizedString(
+                                        "neverForSiteButtonText");
+        var rememberButtonText = this._getLocalizedString(
+                                        "rememberButtonText");
+        var notNowButtonText   = this._getLocalizedString(
+                                        "notNowButtonText");
+
+        this.log("Prompting user to save/ignore login");
+        var userChoice = this._promptService.confirmEx(null,
+                                            dialogTitle, dialogText,
+                                            buttonFlags, rememberButtonText,
+                                            notNowButtonText, neverButtonText,
+                                            null, {});
+        //  Returns:
+        //   0 - Save the login
+        //   1 - Ignore the login this time
+        //   2 - Never save logins for this site
+        if (userChoice == 2) {
+            this.log("Disabling " + aLogin.hostname + " logins by request.");
+            this._pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
+        } else if (userChoice == 0) {
+            this.log("Saving login for " + aLogin.hostname);
+            this._pwmgr.addLogin(aLogin);
+        } else {
+            // userChoice == 1 --> just ignore the login.
+            this.log("Ignoring login.");
+        }
+    },
+
+    /*
+     * promptToChangePassword
+     *
+     * Called when we think we detect a password change for an existing
+     * login, when the form being submitted contains multiple password
+     * fields.
+     *
+     */
+    promptToChangePassword : function (aOldLogin, aNewLogin) {
+        var notifyBox = this._getNotifyBox();
+
+        if (notifyBox)
+            this._showChangeLoginNotification(notifyBox, aOldLogin, aNewLogin.password);
+        else
+            this._showChangeLoginDialog(aOldLogin, aNewLogin.password);
+    },
+
+    /*
+     * _showChangeLoginNotification
+     *
+     * Shows the Change Password notification bar.
+     *
+     */
+    _showChangeLoginNotification : function (aNotifyBox, aOldLogin, aNewPassword) {
+        var notificationText;
+        if (aOldLogin.username)
+            notificationText  = this._getLocalizedString(
+                                          "passwordChangeText",
+                                          [aOldLogin.username]);
+        else
+            notificationText  = this._getLocalizedString(
+                                          "passwordChangeTextNoUser");
+
+        var changeButtonText =
+              this._getLocalizedString("notifyBarChangeButtonText");
+        var changeButtonAccessKey =
+              this._getLocalizedString("notifyBarChangeButtonAccessKey");
+        var dontChangeButtonText =
+              this._getLocalizedString("notifyBarDontChangeButtonText");
+        var dontChangeButtonAccessKey =
+              this._getLocalizedString("notifyBarDontChangeButtonAccessKey");
+
+        // The callbacks in |buttons| have a closure to access the variables
+        // in scope here; set one to |this._pwmgr| so we can get back to pwmgr
+        // without a getService() call.
+        var self = this;
+
+        var buttons = [
+            // "Yes" button
+            {
+                label:     changeButtonText,
+                accessKey: changeButtonAccessKey,
+                popup:     null,
+                callback:  function(aNotificationBar, aButton) {
+                    self._updateLogin(aOldLogin, aNewPassword);
+                }
+            },
+
+            // "No" button
+            {
+                label:     dontChangeButtonText,
+                accessKey: dontChangeButtonAccessKey,
+                popup:     null,
+                callback:  function(aNotificationBar, aButton) {
+                    // do nothing
+                }
+            }
+        ];
+
+        this._showLoginNotification(aNotifyBox, "password-change",
+             notificationText, buttons);
+    },
+
+    /*
+     * _showChangeLoginDialog
+     *
+     * Shows the Change Password dialog.
+     *
+     */
+    _showChangeLoginDialog : function (aOldLogin, aNewPassword) {
+        const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
+
+        var dialogText;
+        if (aOldLogin.username)
+            dialogText  = this._getLocalizedString(
+                                    "passwordChangeText",
+                                    [aOldLogin.username]);
+        else
+            dialogText  = this._getLocalizedString(
+                                    "passwordChangeTextNoUser");
+
+        var dialogTitle = this._getLocalizedString(
+                                    "passwordChangeTitle");
+
+        // returns 0 for yes, 1 for no.
+        var ok = !this._promptService.confirmEx(null,
+                                dialogTitle, dialogText, buttonFlags,
+                                null, null, null,
+                                null, {});
+        if (ok) {
+            this.log("Updating password for user " + aOldLogin.username);
+            this._updateLogin(aOldLogin, aNewPassword);
+        }
+    },
+
+
+    /*
+     * promptToChangePasswordWithUsernames
+     *
+     * Called when we detect a password change in a form submission, but we
+     * don't know which existing login (username) it's for. Asks the user
+     * to select a username and confirm the password change.
+     *
+     * Note: The caller doesn't know the username for aNewLogin, so this
+     *       function fills in .username and .usernameField with the values
+     *       from the login selected by the user.
+     * 
+     * Note; XPCOM stupidity: |count| is just |logins.length|.
+     */
+    promptToChangePasswordWithUsernames : function (logins, count, aNewLogin) {
+        const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
+
+        var usernames = logins.map(function (l) l.username);
+        var dialogText  = this._getLocalizedString("userSelectText");
+        var dialogTitle = this._getLocalizedString("passwordChangeTitle");
+        var selectedIndex = { value: null };
+
+        // If user selects ok, outparam.value is set to the index
+        // of the selected username.
+        var ok = this._promptService.select(null,
+                                dialogTitle, dialogText,
+                                usernames.length, usernames,
+                                selectedIndex);
+        if (ok) {
+            // Now that we know which login to use, modify its password.
+            var selectedLogin = logins[selectedIndex.value];
+            this.log("Updating password for user " + selectedLogin.username);
+            this._updateLogin(selectedLogin, aNewLogin.password);
+        }
+    },
+
+
+
+
+    /* ---------- Internal Methods ---------- */
+
+
+
+
+    /*
+     * _updateLogin
+     */
+    _updateLogin : function (login, newPassword) {
+        var now = Date.now();
+        var propBag = Cc["@mozilla.org/hash-property-bag;1"].
+                      createInstance(Ci.nsIWritablePropertyBag);
+        if (newPassword) {
+            propBag.setProperty("password", newPassword);
+            // Explicitly set the password change time here (even though it would
+            // be changed automatically), to ensure that it's exactly the same
+            // value as timeLastUsed.
+            propBag.setProperty("timePasswordChanged", now);
+        }
+        propBag.setProperty("timeLastUsed", now);
+        propBag.setProperty("timesUsedIncrement", 1);
+        this._pwmgr.modifyLogin(login, propBag);
+    },
+
+    /*
+     * _getNotifyBox
+     *
+     * Returns the notification box to this prompter, or null if there isn't
+     * a notification box available.
+     */
+    _getNotifyBox : function () {
+        let notifyBox = null;
+        try {
+            let chromeWin = this._browser.ownerDocument.defaultView;
+            if (chromeWin.getNotificationBox) {
+                notifyBox = chromeWin.getNotificationBox(this._browser);
+            } else {
+                this.log("getNotificationBox() not available on window");
+            }
+
+        } catch (e) {
+            // If any errors happen, just assume no notification box.
+            this.log("No notification box available: " + e)
+        }
+        return notifyBox;
+    },
+
+    
+    /*
+     * _getLocalizedString
+     *
+     * Can be called as:
+     *   _getLocalizedString("key1");
+     *   _getLocalizedString("key2", ["arg1"]);
+     *   _getLocalizedString("key3", ["arg1", "arg2"]);
+     *   (etc)
+     *
+     * Returns the localized string for the specified key,
+     * formatted if required.
+     *
+     */ 
+    _getLocalizedString : function (key, formatArgs) {
+        if (formatArgs)
+            return this._strBundle.formatStringFromName(
+                                        key, formatArgs, formatArgs.length);
+        else
+            return this._strBundle.GetStringFromName(key);
+    },
+
+
+    /*
+     * _sanitizeUsername
+     *
+     * Sanitizes the specified username, by stripping quotes and truncating if
+     * it's too long. This helps prevent an evil site from messing with the
+     * "save password?" prompt too much.
+     */
+    _sanitizeUsername : function (username) {
+        if (username.length > 30) {
+            username = username.substring(0, 30);
+            username += this._ellipsis;
+        }
+        return username.replace(/['"]/g, "");
+    },
+
+
+    /*
+     * _getFormattedHostname
+     *
+     * The aURI parameter may either be a string uri, or an nsIURI instance.
+     *
+     * Returns the hostname to use in a nsILoginInfo object (for example,
+     * "http://example.com").
+     */
+    _getFormattedHostname : function (aURI) {
+        var uri;
+        if (aURI instanceof Ci.nsIURI) {
+            uri = aURI;
+        } else {
+            uri = Services.io.newURI(aURI, null, null);
+        }
+        var scheme = uri.scheme;
+
+        var hostname = scheme + "://" + uri.host;
+
+        // If the URI explicitly specified a port, only include it when
+        // it's not the default. (We never want "http://foo.com:80")
+        port = uri.port;
+        if (port != -1) {
+            var handler = Services.io.getProtocolHandler(scheme);
+            if (port != handler.defaultPort)
+                hostname += ":" + port;
+        }
+
+        return hostname;
+    },
+
+
+    /*
+     * _getShortDisplayHost
+     *
+     * Converts a login's hostname field (a URL) to a short string for
+     * prompting purposes. Eg, "http://foo.com" --> "foo.com", or
+     * "ftp://www.site.co.uk" --> "site.co.uk".
+     */
+    _getShortDisplayHost: function (aURIString) {
+        var displayHost;
+
+        var eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"].
+                          getService(Ci.nsIEffectiveTLDService);
+        var idnService = Cc["@mozilla.org/network/idn-service;1"].
+                         getService(Ci.nsIIDNService);
+        try {
+            var uri = Services.io.newURI(aURIString, null, null);
+            var baseDomain = eTLDService.getBaseDomain(uri);
+            displayHost = idnService.convertToDisplayIDN(baseDomain, {});
+        } catch (e) {
+            this.log("_getShortDisplayHost couldn't process " + aURIString);
+        }
+
+        if (!displayHost)
+            displayHost = aURIString;
+
+        return displayHost;
+    },
+
+}; // end of LoginManagerPrompter implementation
+
+
+var component = [LoginManagerPrompter];
+var NSGetFactory = XPCOMUtils.generateNSGetFactory(component);
+
--- a/mobile/components/Makefile.in
+++ b/mobile/components/Makefile.in
@@ -43,16 +43,17 @@ VPATH      = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = MobileComponents
 XPIDL_MODULE = MobileComponents
 
 XPIDLSRCS = \
         SessionStore.idl \
+        LoginManagerPrompter.idl \
         $(NULL)
 
 EXTRA_PP_COMPONENTS = \
         AboutRedirector.js \
         BrowserCLH.js \
         DirectoryProvider.js\
         HelperAppDialog.js \
         Sidebar.js \
@@ -66,16 +67,17 @@ EXTRA_COMPONENTS = \
         XPIDialogService.js \
         DownloadManagerUI.js \
         PromptService.js \
         ContentDispatchChooser.js \
         AutoCompleteCache.js \
         AddonUpdateService.js \
         FormAutoComplete.js \
         LoginManager.js \
+        LoginManagerPrompter.js \
         BlocklistPrompt.js \
 	$(NULL)
 
 ifndef ANDROID
 EXTRA_COMPONENTS += AlertsService.js
 endif
 
 ifneq (Android,$(OS_TARGET))
--- a/mobile/components/MobileComponents.manifest
+++ b/mobile/components/MobileComponents.manifest
@@ -88,11 +88,15 @@ category update-timer AddonUpdateService
 # FormAutoComplete.js
 component {cccd414c-3ec2-4cc5-9dc4-36c87cc3c4fe} FormAutoComplete.js
 contract @mozilla.org/satchel/form-autocomplete;1 {cccd414c-3ec2-4cc5-9dc4-36c87cc3c4fe}
 
 # LoginManager.js
 component {f9a0edde-2a8d-4bfd-a08c-3f9333213a85} LoginManager.js
 contract @mozilla.org/login-manager;1 {f9a0edde-2a8d-4bfd-a08c-3f9333213a85}
 
+# LoginManagerPrompter.js
+component {97d12931-abe2-11df-94e2-0800200c9a66} LoginManagerPrompter.js
+contract @mozilla.org/login-manager/prompter;1 {97d12931-abe2-11df-94e2-0800200c9a66}
+
 # BlocklistPrompt.js
 component {4e6ea350-b09a-11df-94e2-0800200c9a66} BlocklistPrompt.js
 contract @mozilla.org/addons/blocklist-prompt;1 {4e6ea350-b09a-11df-94e2-0800200c9a66}
--- a/mobile/components/PromptService.js
+++ b/mobile/components/PromptService.js
@@ -90,27 +90,16 @@ PromptService.prototype = {
   classID: Components.ID("{9a61149b-2276-4a0a-b79c-be994ad106cf}"),
   
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]),
 
   /* ----------  nsIPromptFactory  ---------- */
 
   // XXX Copied from nsPrompter.js.
   getPrompt: function getPrompt(domWin, iid) {
-    // This is still kind of dumb; the C++ code delegated to login manager
-    // here, which in turn calls back into us via nsIPromptService2.
-    if (iid.equals(Ci.nsIAuthPrompt2) || iid.equals(Ci.nsIAuthPrompt)) {
-      try {
-        let pwmgr = Cc["@mozilla.org/passwordmanager/authpromptfactory;1"].
-          getService(Ci.nsIPromptFactory);
-        return pwmgr.getPrompt(domWin, iid);
-      } catch (e) {
-        Cu.reportError("nsPrompter: Delegation to password manager failed: " + e);
-      }
-    }
 
     let doc = this.getDocument();
     if (!doc && !this.inContentProcess) {
       let fallback = this._getFallbackService();
       return fallback.getPrompt(domWin, iid);
     }
     let p = new Prompt(domWin, doc);
     p.QueryInterface(iid);
@@ -169,17 +158,17 @@ PromptService.prototype = {
     let domWin = aArguments[0];
     let p = new Prompt(domWin, doc);
     return p[aMethod].apply(p, Array.prototype.slice.call(aArguments, 1));
   },
 
   /* ----------  nsIPromptService  ---------- */
 
   alert: function() {
-    return this.callProxy("alert", arguments);
+    return this.callProxy("alert", arguments);IA
   },
   alertCheck: function() {
     return this.callProxy("alertCheck", arguments);
   },
   confirm: function() {
     return this.callProxy("confirm", arguments);
   },
   confirmCheck: function() {
@@ -528,126 +517,329 @@ Prompt.prototype = {
     
     dialog.waitForClose();
     return params.result;
   },
 
   /* ----------  nsIAuthPrompt  ---------- */
 
   nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) {
-    // The passwordRealm and savePassword args were ignored by nsPrompt.cpp
+    // TODO: Port functions from nsLoginManagerPrompter.js to here
     if (defaultText)
       result.value = defaultText;
     return this.nsIPrompt_prompt(title, text, result, null, {});
   },
 
-  nsIAuthPrompt_promptUsernameAndPassword : function (title, text, passwordRealm, savePassword, user, pass) {
-    // The passwordRealm and savePassword args were ignored by nsPrompt.cpp
-    return this.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, null, {});
+  nsIAuthPrompt_promptUsernameAndPassword : function (aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass) {
+    return nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass);
+  },
+
+  nsIAuthPrompt_promptPassword : function (aTitle, aText, aPasswordRealm, aSavePassword, aPass) {
+    return nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, null, aPass);
   },
 
-  nsIAuthPrompt_promptPassword : function (title, text, passwordRealm, savePassword, pass) {
-    // The passwordRealm and savePassword args were ignored by nsPrompt.cpp
-    return this.nsIPrompt_promptPassword(title, text, pass, null, {});
-  },
+  nsIAuthPrompt_loginPrompt: function(aTitle, aPasswordRealm, aSavePassword, aUser, aPass) {
+    let checkMsg = null;
+    let check = { value: false };
+    let [hostname, realm, aUser] = PromptUtils.getHostnameAndRealm(aPasswordRealm);
+
+    let canSave = PromptUtils.canSaveLogin(hostname, aSavePassword);
+    if (canSave) {
+      // Look for existing logins.
+      let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, realm);
+      [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, aUser, aPass);
+    }
+
+    let ok = false;
+    if (aUser)
+      ok = this.nsIPrompt_promptUsernameAndPassword(aTitle, aText, aUser, aPass, checkMsg, check);
+    else
+      ok = this.nsIPrompt_promptPassword(aTitle, aText, aPass, checkMsg, check);
+
+    if (ok && canSave && check.value)
+      PromptUtils.savePassword(hostname, realm, aUser, aPass);
+
+    return ok;  },
 
   /* ----------  nsIAuthPrompt2  ---------- */
   
-  promptAuth: function promptAuth(aChannel, aLevel, aAuthInfo, aCheckMsg, aCheckState) {
-    let res = false;
-    
-    let defaultUser = aAuthInfo.username;
-    if ((aAuthInfo.flags & aAuthInfo.NEED_DOMAIN) && (aAuthInfo.domain.length > 0))
-      defaultUser = aAuthInfo.domain + "\\" + defaultUser;
-    
-    let username = { value: defaultUser };
-    let password = { value: aAuthInfo.password };
-    
+  promptAuth: function promptAuth(aChannel, aLevel, aAuthInfo) {
+    let checkMsg = null;
+    let check = { value: false };
     let message = PromptUtils.makeDialogText(aChannel, aAuthInfo);
-    let title = PromptUtils.getLocaleString("PromptUsernameAndPassword2");
-    
-    if (aAuthInfo.flags & aAuthInfo.ONLY_PASSWORD)
-      res = this.promptPassword(title, message, password, aCheckMsg, aCheckState);
-    else
-      res = this.promptUsernameAndPassword(title, message, username, password, aCheckMsg, aCheckState);
-    
-    if (res) {
-      aAuthInfo.username = username.value;
-      aAuthInfo.password = password.value;
+    let [username, password] = PromptUtils.getAuthInfo(aAuthInfo);
+    let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo);
+    let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm);
+
+    let canSave = PromptUtils.canSaveLogin(hostname, null);
+    if (canSave)
+      [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, username, password);
+
+    if (username.value && password.value) {
+      PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value);
+      return true;
     }
     
-    return res;
+    let ok;
+    if (aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD)
+      ok = this.nsIPrompt_promptPassword(null, message, password, checkMsg, check);
+    else
+      ok = this.nsIPrompt_promptUsernameAndPassword(null, message, username, password, checkMsg, check);
+
+    PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value);
+
+    if (ok && canSave && check.value) {
+      PromptUtils.savePassword(foundLogins, username, password, hostname, httpRealm);
+    }
+
+    return ok;
   },
   
-  asyncPromptAuth: function asyncPromptAuth(aChannel, aCallback, aContext, aLevel, aAuthInfo, aCheckMsg, aCheckState) {
+  asyncPromptAuth: function asyncPromptAuth(aChannel, aCallback, aContext, aLevel, aAuthInfo) {
     // bug 514196
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   }
 };
 
 let PromptUtils = {
-  getLocaleString: function getLocaleString(key) {
-    return this.bundle.GetStringFromName(key);
+  getLocaleString: function pu_getLocaleString(aKey, aService) {
+    if (aService == "passwdmgr")
+      return this.passwdBundle.GetStringFromName(aKey);
+
+    return this.bundle.GetStringFromName(aKey);
+  },
+  
+  get pwmgr() {
+    delete this.pwmgr;
+    return this.pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
+  },
+
+  getHostnameAndRealm: function pu_getHostnameAndRealm(aRealmString) {
+    let httpRealm = /^.+ \(.+\)$/;
+    if (httpRealm.test(aRealmString))
+      return [null, null, null];
+
+    let uri = Services.io.newURI(aRealmString, null, null);
+    let pathname = "";
+
+    if (uri.path != "/")
+      pathname = uri.path;
+
+    let formattedHostname = this._getFormattedHostname(uri);
+    return [formattedHostname, formattedHostname + pathname, uri.username];
+  },
+
+  canSaveLogin: function pu_canSaveLogin(aHostname, aSavePassword) {
+    let canSave = !this._inPrivateBrowsing && this.pwmgr.getLoginSavingEnabled(aHostname)
+    if (aSavePassword)
+      canSave = canSave && (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY)
+    return canSave;
+  },
+
+  getUsernameAndPassword: function pu_getUsernameAndPassword(aFoundLogins, aUser, aPass) {
+    let checkLabel = null;
+    let check = { value: false };
+    let selectedLogin;
+
+    checkLabel = this.getLocaleString("rememberPassword", "passwdmgr");
+
+    // XXX Like the original code, we can't deal with multiple
+    // account selection. (bug 227632)
+    if (aFoundLogins.length > 0) {
+      selectedLogin = aFoundLogins[0];
+
+      // If the caller provided a username, try to use it. If they
+      // provided only a password, this will try to find a password-only
+      // login (or return null if none exists).
+      if (aUser.value)
+        selectedLogin = this.findLogin(aFoundLogins, "username", aUser.value);
+
+      if (selectedLogin) {
+        check.value = true;
+        aUser.value = selectedLogin.username;
+        // If the caller provided a password, prefer it.
+        if (!aPass.value)
+          aPass.value = selectedLogin.password;
+      }
+    }
+
+    return [checkLabel, check];
+  },
+
+  findLogin: function pu_findLogin(aLogins, aName, aValue) {
+    for (let i = 0; i < aLogins.length; i++)
+      if (aLogins[i][aName] == aValue)
+        return aLogins[i];
+    return null;
+  },
+
+  savePassword: function pu_savePassword(aLogins, aUser, aPass, aHostname, aRealm) {
+    let selectedLogin = this.findLogin(aLogins, "username", aUser.value);
+
+    // If we didn't find an existing login, or if the username
+    // changed, save as a new login.
+    if (!selectedLogin) {
+      // add as new
+      var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
+      newLogin.init(aHostname, null, aRealm, aUser.value, aPass.value, "", "");
+      this.pwmgr.addLogin(newLogin);
+    } else if (aPass.value != selectedLogin.password) {
+      // update password
+      this.updateLogin(selectedLogin, aPass.value);
+    } else {
+      this.updateLogin(selectedLogin);
+    }
+  },
+
+  updateLogin: function pu_updateLogin(aLogin, aPassword) {
+    let now = Date.now();
+    let propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
+    if (aPassword) {
+      propBag.setProperty("password", aPassword);
+      // Explicitly set the password change time here (even though it would
+      // be changed automatically), to ensure that it's exactly the same
+      // value as timeLastUsed.
+      propBag.setProperty("timePasswordChanged", now);
+    }
+    propBag.setProperty("timeLastUsed", now);
+    propBag.setProperty("timesUsedIncrement", 1);
+
+    this.pwmgr.modifyLogin(aLogin, propBag);
   },
   
   // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/src/nsPrompt.cpp#388
-  makeDialogText: function makeDialogText(aChannel, aAuthInfo) {
-    let HostPort = this.getAuthHostPort(aChannel, aAuthInfo);
-    let displayHost = HostPort.host;
-    let uri = aChannel.URI;
-    let scheme = uri.scheme;
+  makeDialogText: function pu_makeDialogText(aChannel, aAuthInfo) {
+    let isProxy    = (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY);
+    let isPassOnly = (aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD);
+
     let username = aAuthInfo.username;
-    let proxyAuth = (aAuthInfo.flags & aAuthInfo.AUTH_PROXY) != 0;
-    let realm = aAuthInfo.realm;
-    if (realm.length > 100) { // truncate and add ellipsis
-      let pref = Services.prefs;
-      let ellipsis = pref.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
-      if (!ellipsis)
-        ellipsis = "...";
-      realm = realm.substring(0, 100) + ellipsis;
+    let [displayHost, realm] = this.getAuthTarget(aChannel, aAuthInfo);
+
+    // Suppress "the site says: $realm" when we synthesized a missing realm.
+    if (!aAuthInfo.realm && !isProxy)
+    realm = "";
+
+    // Trim obnoxiously long realms.
+    if (realm.length > 150) {
+      realm = realm.substring(0, 150);
+      // Append "..." (or localized equivalent).
+      realm += this.ellipsis;
     }
-    
-    if (HostPort.port != -1)
-      displayHost += ":" + HostPort.port;
-    
-    let text = null;
-    if (proxyAuth) {
-      text = "EnterLoginForProxy";
-    } else {
-      text = "EnterLoginForRealm";
-      displayHost = scheme + "://" + displayHost;
-    }
-    
-    let strings = [realm, displayHost];
-    let count = 2;
-    if (aAuthInfo.flags & aAuthInfo.ONLY_PASSWORD) {
-      text = "EnterPasswordFor";
-      strings[0] = username;
-    } else if (!proxyAuth && (realm.length == 0)) {
-      text = "EnterUserPasswordFor";
-      count = 1;
-      strings[0] = strings[1];
-    }
-    
-    return this.bundle.formatStringFromName(text, strings, count);
+
+    let text;
+    if (isProxy)
+      text = this.bundle.formatStringFromName("EnterLoginForProxy", [realm, displayHost], 2);
+    else if (isPassOnly)
+      text = this.bundle.formatStringFromName("EnterPasswordFor", [username, displayHost], 2);
+    else if (!realm)
+      text = this.bundle.formatStringFromName("EnterUserPasswordFor", [displayHost], 1);
+    else
+      text = this.bundle.formatStringFromName("EnterLoginForRealm", [realm, displayHost], 2);
+
+    return text;
   },
   
   // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/public/nsPromptUtils.h#89
-  getAuthHostPort: function getAuthHostPort(aChannel, aAuthInfo) {
+  getAuthHostPort: function pu_getAuthHostPort(aChannel, aAuthInfo) {
     let uri = aChannel.URI;
     let res = { host: null, port: -1 };
     if (aAuthInfo.flags & aAuthInfo.AUTH_PROXY) {
       let proxy = aChannel.QueryInterface(Ci.nsIProxiedChannel);
       res.host = proxy.proxyInfo.host;
       res.port = proxy.proxyInfo.port;
     } else {
       res.host = uri.host;
       res.port = uri.port;
     }
     return res;
-  }
+  },
+
+  getAuthTarget : function pu_getAuthTarget(aChannel, aAuthInfo) {
+    let hostname, realm;
+    // If our proxy is demanding authentication, don't use the
+    // channel's actual destination.
+    if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) {
+        if (!(aChannel instanceof Ci.nsIProxiedChannel))
+          throw "proxy auth needs nsIProxiedChannel";
+  
+      let info = aChannel.proxyInfo;
+      if (!info)
+        throw "proxy auth needs nsIProxyInfo";
+  
+      // Proxies don't have a scheme, but we'll use "moz-proxy://"
+      // so that it's more obvious what the login is for.
+      let idnService = Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService);
+      hostname = "moz-proxy://" + idnService.convertUTF8toACE(info.host) + ":" + info.port;
+      realm = aAuthInfo.realm;
+      if (!realm)
+        realm = hostname;
+  
+      return [hostname, realm];
+    }
+    hostname = this.getFormattedHostname(aChannel.URI);
+
+    // If a HTTP WWW-Authenticate header specified a realm, that value
+    // will be available here. If it wasn't set or wasn't HTTP, we'll use
+    // the formatted hostname instead.
+    realm = aAuthInfo.realm;
+    if (!realm)
+      realm = hostname;
+
+    return [hostname, realm];
+  },
+
+  getAuthInfo : function pu_getAuthInfo(aAuthInfo) {
+    let flags = aAuthInfo.flags;
+    let username = {value: ""};
+    let password = {value: ""};
+
+    if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && aAuthInfo.domain)
+      username.value = aAuthInfo.domain + "\\" + aAuthInfo.username;
+    else
+      username.value = aAuthInfo.username;
+
+    password.value = aAuthInfo.password
+
+    return [username, password];
+  },
+
+  setAuthInfo : function (aAuthInfo, username, password) {
+    var flags = aAuthInfo.flags;
+    if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) {
+      // Domain is separated from username by a backslash
+      var idx = username.indexOf("\\");
+      if (idx == -1) {
+        aAuthInfo.username = username;
+      } else {
+        aAuthInfo.domain   =  username.substring(0, idx);
+        aAuthInfo.username =  username.substring(idx+1);
+      }
+    } else {
+      aAuthInfo.username = username;
+    }
+    aAuthInfo.password = password;
+  },
+
+  getFormattedHostname : function pu_getFormattedHostname(uri) {
+    let scheme = uri.scheme;
+    let hostname = scheme + "://" + uri.host;
+
+    // If the URI explicitly specified a port, only include it when
+    // it's not the default. (We never want "http://foo.com:80")
+    port = uri.port;
+    if (port != -1) {
+      let handler = Services.io.getProtocolHandler(scheme);
+      if (port != handler.defaultPort)
+        hostname += ":" + port;
+    }
+    return hostname;
+  },
 };
 
+XPCOMUtils.defineLazyGetter(PromptUtils, "passwdBundle", function () {
+  return Services.strings.createBundle("chrome://passwordmgr/locale/passwordmgr.properties");
+});
+
 XPCOMUtils.defineLazyGetter(PromptUtils, "bundle", function () {
   return Services.strings.createBundle("chrome://global/locale/commonDialogs.properties");
 });
 
 const NSGetFactory = XPCOMUtils.generateNSGetFactory([PromptService]);