Bug 695446 - Password manager/Form Fill [r=mbrubeck]

This commit is contained in:
Mark Finkle 2011-10-27 00:29:46 -04:00
parent f79ee03bc3
commit ed48b7285f
9 changed files with 11 additions and 833 deletions

View File

@ -180,6 +180,7 @@ pref("browser.helperApps.deleteTempFileOnExit", false);
pref("signon.rememberSignons", true);
pref("signon.expireMasterPassword", false);
pref("signon.SignonFileName", "signons.txt");
pref("signon.debug", false);
/* form helper */
// 0 = disabled, 1 = enabled, 2 = dynamic depending on screen size

View File

@ -137,6 +137,10 @@ var BrowserApp = {
Services.obs.addObserver(XPInstallObserver, "addon-install-started", false);
NativeWindow.init();
Downloads.init();
// Init LoginManager
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
let uri = "about:support";
if ("arguments" in window && window.arguments[0])
@ -147,8 +151,6 @@ var BrowserApp = {
let newTab = this.addTab(uri);
newTab.active = true;
Downloads.init();
// Broadcast a UIReady message so add-ons know we are finished with startup
let event = document.createEvent("Events");
event.initEvent("UIReady", true, false);

View File

@ -1,719 +0,0 @@
/* ***** 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>
*
* 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;
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
function LoginManager() {
this.init();
}
LoginManager.prototype = {
classID: Components.ID("{f9a0edde-2a8d-4bfd-a08c-3f9333213a85}"),
QueryInterface : XPCOMUtils.generateQI([Ci.nsILoginManager,
Ci.nsIObserver,
Ci.nsISupportsWeakReference]),
/* ---------- private members ---------- */
__storage : null, // Storage component which contains the saved logins
get _storage() {
if (!this.__storage) {
var contractID = "@mozilla.org/login-manager/storage/mozStorage;1";
try {
var catMan = Cc["@mozilla.org/categorymanager;1"].
getService(Ci.nsICategoryManager);
contractID = catMan.getCategoryEntry("login-manager-storage",
"nsILoginManagerStorage");
this.log("Found alternate nsILoginManagerStorage with " +
"contract ID: " + contractID);
} catch (e) {
this.log("No alternate nsILoginManagerStorage registered");
}
this.__storage = Cc[contractID].
createInstance(Ci.nsILoginManagerStorage);
try {
this.__storage.init();
} catch (e) {
this.log("Initialization of storage component failed: " + e);
this.__storage = null;
}
}
return this.__storage;
},
_nsLoginInfo : null, // Constructor for nsILoginInfo implementation
_debug : false, // mirrors signon.debug
_remember : true, // mirrors signon.rememberSignons preference
/*
* init
*
* Initialize the Login Manager. Automatically called when service
* is created.
*
* Note: Service created in /browser/base/content/browser.js,
* delayedStartup()
*/
init : function () {
// Add content listener.
var messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
getService(Ci.nsIChromeFrameMessageManager);
messageManager.loadFrameScript("chrome://browser/content/LoginManagerChild.js", true);
messageManager.addMessageListener("PasswordMgr:FormSubmitted", this);
messageManager.addMessageListener("PasswordMgr:GetPasswords", this);
// Get constructor for nsILoginInfo
this._nsLoginInfo = new Components.Constructor(
"@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo);
// Preferences. Add observer so we get notified of changes.
Services.prefs.addObserver("signon.", this, false);
// Get current preference values.
this._debug = Services.prefs.getBoolPref("signon.debug");
this._remember = Services.prefs.getBoolPref("signon.rememberSignons");
// Listen for shutdown to clean up
Services.obs.addObserver(this, "xpcom-shutdown", false);
},
/*
* log
*
* Internal function for logging debug messages to the Error Console window
*/
log : function (message) {
if (!this._debug)
return;
dump("PasswordUtils: " + message + "\n");
Services.console.logStringMessage("PasswordUtils: " + message);
},
/*
* observe
*
* Implements nsIObserver for preferences and shutdown.
*/
observe : function (subject, topic, data) {
if (topic == "nsPref:changed") {
this._pwmgr._debug = Services.prefs.getBoolPref("signon.debug");
this._pwmgr._remember = Services.prefs.getBoolPref("signon.rememberSignons");
} else if (topic == "xpcom-shutdown") {
// Circular reference forms when we mark an input field as managed
// by the password manager
this._formFillService = null;
} else {
this._pwmgr.log("Oops! Unexpected notification: " + topic);
}
},
/*
* receiveMessage
*
* Receives messages from content process.
*/
receiveMessage: function (message) {
// local helper function
function getPrompter(aBrowser) {
var prompterSvc = Cc["@mozilla.org/login-manager/prompter;1"].
createInstance(Ci.nsILoginManagerPrompter);
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: {} };
var foundLogins = {};
if (!this.uiBusy) {
for (var i = 0; i < message.json.actionOrigins.length; i++) {
var actionOrigin = message.json.actionOrigins[i];
var logins = this.findLogins({}, message.json.formOrigin, actionOrigin, null);
if (logins.length) {
foundLogins[actionOrigin] = logins;
}
}
}
return {
uiBusy: this.uiBusy,
foundLogins: foundLogins
};
case "PasswordMgr:FormSubmitted":
var json = message.json;
var hostname = json.hostname;
var formSubmitURL = json.formSubmitURL;
if (!this.getLoginSavingEnabled(hostname)) {
this.log("(form submission ignored -- saving is " +
"disabled for: " + hostname + ")");
return {};
}
var browser = message.target;
var formLogin = new this._nsLoginInfo();
formLogin.init(hostname, formSubmitURL, null,
json.usernameValue,
json.passwordValue,
json.usernameField,
json.passwordField);
// 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 (!json.usernameField && json.hasOldPasswordField) {
var logins = this.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.
this.log("(no logins for this host -- pwchange ignored)");
return {};
}
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 {
prompter.promptToChangePasswordWithUsernames(
logins, logins.length, formLogin);
}
} else {
// Look for an existing login that matches the form login.
var existingLogin = null;
var logins = this.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) {
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(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(browser);
prompter.promptToSavePassword(formLogin);
}
return {};
default:
throw "Unexpected message " + message.name;
}
},
/*
* _getPasswordOrigin
*
* Get the parts of the URL we want for identification.
*/
_getPasswordOrigin : function (uriString, allowJS) {
var realm = "";
try {
var uri = Services.io.newURI(uriString, null, null);
if (allowJS && uri.scheme == "javascript")
return "javascript:"
realm = uri.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")
var port = uri.port;
if (port != -1) {
var handler = Services.io.getProtocolHandler(uri.scheme);
if (port != handler.defaultPort)
realm += ":" + port;
}
} catch (e) {
// bug 159484 - disallow url types that don't support a hostPort.
// (although we handle "javascript:..." as a special case above.)
this.log("Couldn't parse origin for " + uriString);
realm = null;
}
return realm;
},
_getActionOrigin : function (form) {
var uriString = form.action;
// A blank or mission action submits to where it came from.
if (uriString == "")
uriString = form.baseURI; // ala bug 297761
return this._getPasswordOrigin(uriString, true);
},
/* ---------- Primary Public interfaces ---------- */
/*
* fillForm
*
* Fill the form with login information if we can find it.
*/
fillForm : function (form) {
// XXX figure out what to do about fillForm
return false;
},
/*
* addLogin
*
* Add a new login to login storage.
*/
addLogin : function (login) {
// Sanity check the login
if (login.hostname == null || login.hostname.length == 0)
throw "Can't add a login with a null or empty hostname.";
// For logins w/o a username, set to "", not null.
if (login.username == null)
throw "Can't add a login with a null username.";
if (login.password == null || login.password.length == 0)
throw "Can't add a login with a null or empty password.";
if (login.formSubmitURL || login.formSubmitURL == "") {
// We have a form submit URL. Can't have a HTTP realm.
if (login.httpRealm != null)
throw "Can't add a login with both a httpRealm and formSubmitURL.";
} else if (login.httpRealm) {
// We have a HTTP realm. Can't have a form submit URL.
if (login.formSubmitURL != null)
throw "Can't add a login with both a httpRealm and formSubmitURL.";
} else {
// Need one or the other!
throw "Can't add a login without a httpRealm or formSubmitURL.";
}
// Look for an existing entry.
var logins = this.findLogins({}, login.hostname, login.formSubmitURL,
login.httpRealm);
if (logins.some(function(l) login.matches(l, true)))
throw "This login already exists.";
this.log("Adding login: " + login);
return this._storage.addLogin(login);
},
/*
* removeLogin
*
* Remove the specified login from the stored logins.
*/
removeLogin : function (login) {
this.log("Removing login: " + login);
return this._storage.removeLogin(login);
},
/*
* modifyLogin
*
* Change the specified login to match the new login.
*/
modifyLogin : function (oldLogin, newLogin) {
this.log("Modifying oldLogin: " + oldLogin + " newLogin: " + newLogin);
return this._storage.modifyLogin(oldLogin, newLogin);
},
/*
* getAllLogins
*
* Get a dump of all stored logins. Used by the login manager UI.
*
* |count| is only needed for XPCOM.
*
* Returns an array of logins. If there are no logins, the array is empty.
*/
getAllLogins : function (count) {
this.log("Getting a list of all logins");
return this._storage.getAllLogins(count);
},
/*
* removeAllLogins
*
* Remove all stored logins.
*/
removeAllLogins : function () {
this.log("Removing all logins");
this._storage.removeAllLogins();
},
/*
* getAllDisabledHosts
*
* Get a list of all hosts for which logins are disabled.
*
* |count| is only needed for XPCOM.
*
* Returns an array of disabled logins. If there are no disabled logins,
* the array is empty.
*/
getAllDisabledHosts : function (count) {
this.log("Getting a list of all disabled hosts");
return this._storage.getAllDisabledHosts(count);
},
/*
* findLogins
*
* Search for the known logins for entries matching the specified criteria.
*/
findLogins : function (count, hostname, formSubmitURL, httpRealm) {
this.log("Searching for logins matching host: " + hostname +
", formSubmitURL: " + formSubmitURL + ", httpRealm: " + httpRealm);
return this._storage.findLogins(count, hostname, formSubmitURL,
httpRealm);
},
/*
* searchLogins
*
* Public wrapper around _searchLogins to convert the nsIPropertyBag to a
* JavaScript object and decrypt the results.
*
* Returns an array of decrypted nsILoginInfo.
*/
searchLogins : function(count, matchData) {
this.log("Searching for logins");
return this._storage.searchLogins(count, matchData);
},
/*
* countLogins
*
* Search for the known logins for entries matching the specified criteria,
* returns only the count.
*/
countLogins : function (hostname, formSubmitURL, httpRealm) {
this.log("Counting logins matching host: " + hostname +
", formSubmitURL: " + formSubmitURL + ", httpRealm: " + httpRealm);
return this._storage.countLogins(hostname, formSubmitURL, httpRealm);
},
/*
* uiBusy
*/
get uiBusy() {
return this._storage.uiBusy;
},
/*
* getLoginSavingEnabled
*
* Check to see if user has disabled saving logins for the host.
*/
getLoginSavingEnabled : function (host) {
this.log("Checking if logins to " + host + " can be saved.");
if (!this._remember)
return false;
return this._storage.getLoginSavingEnabled(host);
},
/*
* setLoginSavingEnabled
*
* Enable or disable storing logins for the specified host.
*/
setLoginSavingEnabled : function (hostname, enabled) {
// Nulls won't round-trip with getAllDisabledHosts().
if (hostname.indexOf("\0") != -1)
throw "Invalid hostname";
this.log("Saving logins for " + hostname + " enabled? " + enabled);
return this._storage.setLoginSavingEnabled(hostname, enabled);
},
/*
* autoCompleteSearch
*
* Yuck. This is called directly by satchel:
* nsFormFillController::StartSearch()
* [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.
*/
autoCompleteSearch : function (aSearchString, aPreviousResult, aElement) {
// aPreviousResult & aResult are nsIAutoCompleteResult,
// aElement is nsIDOMHTMLInputElement
if (!this._remember)
return null;
this.log("AutoCompleteSearch invoked. Search is: " + aSearchString);
var result = null;
if (aPreviousResult &&
aSearchString.substr(0, aPreviousResult.searchString.length) == aPreviousResult.searchString) {
this.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())
{
this.log("Removing autocomplete entry '" + match + "'");
result.removeValueAt(i, false);
}
}
} else {
this.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]);
}
}
this.log(matchingLogins.length + " autocomplete logins avail.");
result = new UserAutoCompleteResult(aSearchString, matchingLogins);
}
return result;
}
}; // 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 "";
},
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);
}
}
};
var NSGetFactory = XPCOMUtils.generateNSGetFactory([LoginManager]);

View File

@ -1,99 +0,0 @@
/* ***** 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);
};

View File

@ -318,11 +318,11 @@ LoginManagerPrompter.prototype = {
var dialogTitle = this._getLocalizedString(
"savePasswordTitle");
var neverButtonText = this._getLocalizedString(
"neverForSiteButtonText");
"promptNeverForSiteButtonText");
var rememberButtonText = this._getLocalizedString(
"rememberButtonText");
"promptRememberButtonText");
var notNowButtonText = this._getLocalizedString(
"notNowButtonText");
"promptNotNowButtonText");
this.log("Prompting user to save/ignore login");
var userChoice = this._promptService.confirmEx(null,

View File

@ -48,7 +48,6 @@ XPIDL_MODULE = MobileComponents
XPIDLSRCS = \
SessionStore.idl \
LoginManagerPrompter.idl \
$(NULL)
EXTRA_PP_COMPONENTS = \
@ -72,7 +71,6 @@ EXTRA_COMPONENTS = \
AutoCompleteCache.js \
AddonUpdateService.js \
FormAutoComplete.js \
LoginManager.js \
LoginManagerPrompter.js \
BlocklistPrompt.js \
CapturePicker.js \

View File

@ -96,10 +96,6 @@ category update-timer AddonUpdateService @mozilla.org/browser/addon-update-servi
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}

View File

@ -604,7 +604,6 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
@BINPATH@/components/DownloadManagerUI.js
@BINPATH@/components/FormAutoComplete.js
@BINPATH@/components/HelperAppDialog.js
@BINPATH@/components/LoginManager.js
@BINPATH@/components/LoginManagerPrompter.js
@BINPATH@/components/MobileComponents.manifest
@BINPATH@/components/MobileComponents.xpt

View File

@ -44,13 +44,13 @@ savePasswordTitle = Confirm
saveLoginText = Do you want %1$S to remember the password for "%2$S" on %3$S?
# 1st string is product name, 2nd is the login's hostname
saveLoginTextNoUsername = Do you want %1$S to remember this password on %2$S?
notNowButtonText = &Not Now
promptNotNowButtonText = Not Now
notifyBarNotNowButtonText = Not Now
notifyBarNotNowButtonAccessKey =
neverForSiteButtonText = Ne&ver for This Site
promptNeverForSiteButtonText = Never
notifyBarNeverForSiteButtonText = Never
notifyBarNeverForSiteButtonAccessKey =
rememberButtonText = &Remember
promptRememberButtonText = Remember
notifyBarRememberButtonText = Remember
notifyBarRememberButtonAccessKey =
passwordChangeTitle = Confirm Password Change