зеркало из https://github.com/mozilla/gecko-dev.git
Bug 587934 - Login manager: use notifications (part 1) [r=mfinkle]
This commit is contained in:
Родитель
264b2938f5
Коммит
4c5bb9bbf6
|
@ -891,7 +891,7 @@ var Browser = {
|
|||
}
|
||||
},
|
||||
|
||||
getNotificationBox: function getNotificationBox() {
|
||||
getNotificationBox: function getNotificationBox(aBrowser) {
|
||||
return document.getElementById("notifications");
|
||||
},
|
||||
|
||||
|
@ -2125,8 +2125,8 @@ var MemoryObserver = {
|
|||
}
|
||||
};
|
||||
|
||||
function getNotificationBox(aWindow) {
|
||||
return Browser.getNotificationBox();
|
||||
function getNotificationBox(aBrowser) {
|
||||
return Browser.getNotificationBox(aBrowser);
|
||||
}
|
||||
|
||||
function importDialog(aParent, aSrc, aArguments) {
|
||||
|
|
|
@ -161,10 +161,10 @@ LoginManager.prototype = {
|
|||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -202,9 +202,7 @@ LoginManager.prototype = {
|
|||
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();
|
||||
|
||||
|
@ -228,7 +226,7 @@ LoginManager.prototype = {
|
|||
return {};
|
||||
}
|
||||
|
||||
var prompter = getPrompter(win);
|
||||
var prompter = getPrompter(browser);
|
||||
|
||||
if (logins.length == 1) {
|
||||
var oldLogin = logins[0];
|
||||
|
@ -279,7 +277,7 @@ LoginManager.prototype = {
|
|||
// 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.
|
||||
|
@ -295,7 +293,7 @@ LoginManager.prototype = {
|
|||
|
||||
|
||||
// Prompt user to save login (via dialog or notification bar)
|
||||
prompter = getPrompter(win);
|
||||
prompter = getPrompter(browser);
|
||||
prompter.promptToSavePassword(formLogin);
|
||||
}
|
||||
return {};
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
@ -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);
|
||||
|
|
@ -48,6 +48,7 @@ XPIDL_MODULE = MobileComponents
|
|||
|
||||
XPIDLSRCS = \
|
||||
SessionStore.idl \
|
||||
LoginManagerPrompter.idl \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_PP_COMPONENTS = \
|
||||
|
@ -71,6 +72,7 @@ EXTRA_COMPONENTS = \
|
|||
AddonUpdateService.js \
|
||||
FormAutoComplete.js \
|
||||
LoginManager.js \
|
||||
LoginManagerPrompter.js \
|
||||
BlocklistPrompt.js \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -93,6 +93,10 @@ contract @mozilla.org/satchel/form-autocomplete;1 {cccd414c-3ec2-4cc5-9dc4-36c87
|
|||
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}
|
||||
|
|
|
@ -95,17 +95,6 @@ PromptService.prototype = {
|
|||
|
||||
// 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) {
|
||||
|
@ -488,6 +477,8 @@ Prompt.prototype = {
|
|||
aTitle, aText, aUsername, aPassword, aCheckMsg, aCheckState) {
|
||||
var params = new Object();
|
||||
params.result = false;
|
||||
//if(!aCheckState)
|
||||
//aCheckState = { value: false };
|
||||
params.checkbox = aCheckState;
|
||||
params.user = aUsername;
|
||||
params.password = aPassword;
|
||||
|
@ -533,27 +524,27 @@ Prompt.prototype = {
|
|||
/* ---------- 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, {});
|
||||
// TODO: Port functions from nsLoginManagerPrompter.js to here
|
||||
return this.nsIPrompt_promptUsernameAndPassword(title, text, user, pass, null, {value: false});
|
||||
},
|
||||
|
||||
nsIAuthPrompt_promptPassword : function (title, text, passwordRealm, savePassword, pass) {
|
||||
// The passwordRealm and savePassword args were ignored by nsPrompt.cpp
|
||||
// TODO: Port functions from nsLoginManagerPrompter.js to here
|
||||
return this.nsIPrompt_promptPassword(title, text, pass, null, {});
|
||||
},
|
||||
|
||||
/* ---------- nsIAuthPrompt2 ---------- */
|
||||
|
||||
promptAuth: function promptAuth(aChannel, aLevel, aAuthInfo, aCheckMsg, aCheckState) {
|
||||
promptAuth: function promptAuth(aChannel, aLevel, aAuthInfo) {
|
||||
let res = false;
|
||||
|
||||
|
||||
let defaultUser = aAuthInfo.username;
|
||||
if ((aAuthInfo.flags & aAuthInfo.NEED_DOMAIN) && (aAuthInfo.domain.length > 0))
|
||||
defaultUser = aAuthInfo.domain + "\\" + defaultUser;
|
||||
|
@ -563,11 +554,12 @@ Prompt.prototype = {
|
|||
|
||||
let message = PromptUtils.makeDialogText(aChannel, aAuthInfo);
|
||||
let title = PromptUtils.getLocaleString("PromptUsernameAndPassword2");
|
||||
let checkMsg = PromptUtils.getLocaleString("rememberButtonText", "passwdmgr");
|
||||
|
||||
if (aAuthInfo.flags & aAuthInfo.ONLY_PASSWORD)
|
||||
res = this.promptPassword(title, message, password, aCheckMsg, aCheckState);
|
||||
res = this.promptPassword(title, message, password, checkMsg, {});
|
||||
else
|
||||
res = this.promptUsernameAndPassword(title, message, username, password, aCheckMsg, aCheckState);
|
||||
res = this.promptUsernameAndPassword(title, message, username, password, checkMsg, {});
|
||||
|
||||
if (res) {
|
||||
aAuthInfo.username = username.value;
|
||||
|
@ -577,15 +569,18 @@ Prompt.prototype = {
|
|||
return res;
|
||||
},
|
||||
|
||||
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 getLocaleString(aKey, aService) {
|
||||
if(aService && aService == "passwdmgr")
|
||||
return this.passwdBundle.GetStringFromName(aKey);
|
||||
|
||||
return this.bundle.GetStringFromName(aKey);
|
||||
},
|
||||
|
||||
// JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/src/nsPrompt.cpp#388
|
||||
|
@ -646,6 +641,10 @@ let PromptUtils = {
|
|||
}
|
||||
};
|
||||
|
||||
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");
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче