Bug 499233 - multiple master password prompts triggered by filling form logins in multiple tabs. r=zpao, a=sdwilsh

This commit is contained in:
Justin Dolske 2010-08-02 16:02:20 -07:00
Родитель 5772854f67
Коммит cc499d58a9
13 изменённых файлов: 595 добавлений и 28 удалений

Просмотреть файл

@ -44,7 +44,7 @@ interface nsIDOMHTMLInputElement;
interface nsIDOMHTMLFormElement;
interface nsIPropertyBag;
[scriptable, uuid(30534ff7-fb95-45c5-8336-5448638f2aa1)]
[scriptable, uuid(1f02142f-7e3f-4d02-b3e0-495c5f83ad7d)]
interface nsILoginManager : nsISupports {
@ -267,6 +267,11 @@ interface nsILoginManager : nsISupports {
*/
void searchLogins(out unsigned long count, in nsIPropertyBag matchData,
[retval, array, size_is(count)] out nsILoginInfo logins);
/**
* True when a master password prompt is being displayed.
*/
readonly attribute boolean uiBusy;
};
%{C++

Просмотреть файл

@ -37,7 +37,7 @@
#include "nsISupports.idl"
[scriptable, uuid(1ddbe67d-a216-4915-9e0a-3e1b95b7126c)]
[scriptable, uuid(73f85239-421d-4d34-8d9c-79cf820ea1e6)]
interface nsILoginManagerCrypto : nsISupports {
@ -70,4 +70,20 @@ interface nsILoginManagerCrypto : nsISupports {
* encrypted with some other key).
*/
AString decrypt(in AString cipherText);
/**
* uiBusy
*
* True when a master password prompt is being displayed.
*/
readonly attribute boolean uiBusy;
/**
* isLoggedIn
*
* Current login state of the token used for encryption. If the user is
* not logged in, performing a crypto operation will result in a master
* password prompt.
*/
readonly attribute boolean isLoggedIn;
};

Просмотреть файл

@ -41,7 +41,7 @@ interface nsIFile;
interface nsILoginInfo;
interface nsIPropertyBag;
[scriptable, uuid(e66c97cd-3bcf-4eee-9937-38f650372d77)]
[scriptable, uuid(32a4f9f1-60a8-4971-b54e-71ad661483ae)]
/*
* NOTE: This interface is intended to be implemented by modules
@ -284,4 +284,8 @@ interface nsILoginManagerStorage : nsISupports {
*/
unsigned long countLogins(in AString aHostname, in AString aActionURL,
in AString aHttpRealm);
/**
* True when a master password prompt is being shown.
*/
readonly attribute boolean uiBusy;
};

Просмотреть файл

@ -51,6 +51,16 @@ LoginManagerCrypto_SDR.prototype = {
classID : Components.ID("{dc6c2976-0f73-4f1f-b9ff-3d72b4e28309}"),
QueryInterface : XPCOMUtils.generateQI([Ci.nsILoginManagerCrypto]),
__sdrSlot : null, // PKCS#11 slot being used by the SDR.
get _sdrSlot() {
if (!this.__sdrSlot) {
let modules = Cc["@mozilla.org/security/pkcs11moduledb;1"].
getService(Ci.nsIPKCS11ModuleDB);
this.__sdrSlot = modules.findSlotByName("");
}
return this.__sdrSlot;
},
__decoderRing : null, // nsSecretDecoderRing service
get _decoderRing() {
if (!this.__decoderRing)
@ -73,7 +83,8 @@ LoginManagerCrypto_SDR.prototype = {
this.__utfConverter = null;
},
_debug : false, // mirrors signon.debug
_debug : false, // mirrors signon.debug
_uiBusy : false,
/*
@ -120,6 +131,10 @@ LoginManagerCrypto_SDR.prototype = {
encrypt : function (plainText) {
let cipherText = null;
let wasLoggedIn = this.isLoggedIn;
let canceledMP = false;
this._uiBusy = true;
try {
let plainOctet = this._utfConverter.ConvertFromUnicode(plainText);
plainOctet += this._utfConverter.Finish();
@ -128,10 +143,19 @@ LoginManagerCrypto_SDR.prototype = {
this.log("Failed to encrypt string. (" + e.name + ")");
// If the user clicks Cancel, we get NS_ERROR_FAILURE.
// (unlike decrypting, which gets NS_ERROR_NOT_AVAILABLE).
if (e.result == Cr.NS_ERROR_FAILURE)
if (e.result == Cr.NS_ERROR_FAILURE) {
canceledMP = true;
throw Components.Exception("User canceled master password entry", Cr.NS_ERROR_ABORT);
else
} else {
throw Components.Exception("Couldn't encrypt string", Cr.NS_ERROR_FAILURE);
}
} finally {
this._uiBusy = false;
// If we triggered a master password prompt, notify observers.
if (!wasLoggedIn && this.isLoggedIn)
this._notifyObservers("passwordmgr-crypto-login");
else if (canceledMP)
this._notifyObservers("passwordmgr-crypto-loginCanceled");
}
return cipherText;
},
@ -148,6 +172,10 @@ LoginManagerCrypto_SDR.prototype = {
decrypt : function (cipherText) {
let plainText = null;
let wasLoggedIn = this.isLoggedIn;
let canceledMP = false;
this._uiBusy = true;
try {
let plainOctet;
if (cipherText.charAt(0) == '~') {
@ -170,14 +198,55 @@ LoginManagerCrypto_SDR.prototype = {
// If the cipherText is bad / wrong key, we get NS_ERROR_FAILURE
// Wrong passwords are handled by the decoderRing reprompting;
// we get no notification.
if (e.result == Cr.NS_ERROR_NOT_AVAILABLE)
if (e.result == Cr.NS_ERROR_NOT_AVAILABLE) {
canceledMP = true;
throw Components.Exception("User canceled master password entry", Cr.NS_ERROR_ABORT);
else
} else {
throw Components.Exception("Couldn't decrypt string", Cr.NS_ERROR_FAILURE);
}
} finally {
this._uiBusy = false;
// If we triggered a master password prompt, notify observers.
if (!wasLoggedIn && this.isLoggedIn)
this._notifyObservers("passwordmgr-crypto-login");
else if (canceledMP)
this._notifyObservers("passwordmgr-crypto-loginCanceled");
}
return plainText;
}
},
/*
* uiBusy
*/
get uiBusy() {
return this._uiBusy;
},
/*
* isLoggedIn
*/
get isLoggedIn() {
let status = this._sdrSlot.status;
this.log("SDR slot status is " + status);
if (status == Ci.nsIPKCS11Slot.SLOT_READY ||
status == Ci.nsIPKCS11Slot.SLOT_LOGGED_IN)
return true;
if (status == Ci.nsIPKCS11Slot.SLOT_NOT_LOGGED_IN)
return false;
throw Components.Exception("unexpected slot status: " + status, Cr.NS_ERROR_FAILURE);
},
/*
* _notifyObservers
*/
_notifyObservers : function(topic) {
this.log("Prompted for a master password, notifying for " + topic);
Services.obs.notifyObservers(null, topic, null);
},
}; // end of nsLoginManagerCrypto_SDR implementation
let component = [LoginManagerCrypto_SDR];

Просмотреть файл

@ -169,8 +169,6 @@ LoginManager.prototype = {
getService(Ci.nsIWebProgress);
progress.addProgressListener(this._webProgressListener,
Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
},
@ -347,6 +345,9 @@ LoginManager.prototype = {
var [usernameField, passwordField, ignored] =
this._pwmgr._getFormFields(acForm, false);
if (usernameField == acInputField && passwordField) {
// 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.
this._pwmgr._fillForm(acForm, true, true, true, null);
} else {
this._pwmgr.log("Oops, form changed before AC invoked");
@ -517,6 +518,14 @@ LoginManager.prototype = {
},
/*
* uiBusy
*/
get uiBusy() {
return this._storage.uiBusy;
},
/*
* getLoginSavingEnabled
*
@ -561,7 +570,7 @@ LoginManager.prototype = {
// aElement is nsIDOMHTMLInputElement
if (!this._remember)
return false;
return null;
this.log("AutoCompleteSearch invoked. Search is: " + aSearchString);
@ -596,6 +605,9 @@ LoginManager.prototype = {
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 = [];
@ -993,6 +1005,37 @@ LoginManager.prototype = {
if (!this.countLogins(formOrigin, "", null))
return;
// If we're currently displaying a master password prompt, defer
// processing this document until the user handles the prompt.
if (this.uiBusy) {
this.log("deferring fillDoc for " + doc.documentURI);
let self = this;
let observer = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
observe: function (subject, topic, data) {
self.log("Got deferred fillDoc notification: " + topic);
// Only run observer once.
Services.obs.removeObserver(this, "passwordmgr-crypto-login");
Services.obs.removeObserver(this, "passwordmgr-crypto-loginCanceled");
if (topic == "passwordmgr-crypto-loginCanceled")
return;
self._fillDocument(doc);
},
handleEvent : function (event) {
// Not expected to be called
}
};
// Trickyness follows: We want an observer, but don't want it to
// cause leaks. So add the observer with a weak reference, and use
// a dummy event listener (a strong reference) to keep it alive
// until the document is destroyed.
Services.obs.addObserver(observer, "passwordmgr-crypto-login", true);
Services.obs.addObserver(observer, "passwordmgr-crypto-loginCanceled", true);
doc.addEventListener("mozCleverClosureHack", observer, false);
return;
}
this.log("fillDocument processing " + forms.length +
" forms on " + doc.documentURI);

Просмотреть файл

@ -52,6 +52,8 @@ Components.utils.import("resource://gre/modules/Services.jsm");
*/
function LoginManagerPromptFactory() {
Services.obs.addObserver(this, "quit-application-granted", true);
Services.obs.addObserver(this, "passwordmgr-crypto-login", true);
Services.obs.addObserver(this, "passwordmgr-crypto-loginCanceled", true);
}
LoginManagerPromptFactory.prototype = {
@ -64,19 +66,16 @@ LoginManagerPromptFactory.prototype = {
_asyncPromptInProgress : false,
observe : function (subject, topic, data) {
this.log("Observed: " + topic);
if (topic == "quit-application-granted") {
var asyncPrompts = this._asyncPrompts;
this.__proto__._asyncPrompts = {};
for each (var asyncPrompt in asyncPrompts) {
for each (var consumer in asyncPrompt.consumers) {
if (consumer.callback) {
this.log("Canceling async auth prompt callback " + consumer.callback);
try {
consumer.callback.onAuthCancelled(consumer.context, true);
} catch (e) { /* Just ignore exceptions from the callback */ }
}
}
}
this._cancelPendingPrompts();
} else if (topic == "passwordmgr-crypto-login") {
// Start processing the deferred prompters.
this._doAsyncPrompt();
} else if (topic == "passwordmgr-crypto-loginCanceled") {
// User canceled a Master Password prompt, so go ahead and cancel
// all pending auth prompts to avoid nagging over and over.
this._cancelPendingPrompts();
}
},
@ -105,24 +104,37 @@ LoginManagerPromptFactory.prototype = {
return;
}
// If login manger has logins for this host, defer prompting if we're
// already waiting on a master password entry.
var prompt = this._asyncPrompts[hashKey];
var prompter = prompt.prompter;
var [hostname, httpRealm] = prompter._getAuthTarget(prompt.channel, prompt.authInfo);
var hasLogins = (prompter._pwmgr.countLogins(hostname, null, httpRealm) > 0);
if (hasLogins && prompter._pwmgr.uiBusy) {
this.log("_doAsyncPrompt:run bypassed, master password UI busy");
return;
}
this._asyncPromptInProgress = true;
prompt.inProgress = true;
var self = this;
var runnable = {
run : function() {
var ok = false;
var prompt = self._asyncPrompts[hashKey];
try {
self.log("_doAsyncPrompt:run - performing the prompt for '" + hashKey + "'");
ok = prompt.prompter.promptAuth(prompt.channel,
prompt.level,
prompt.authInfo);
ok = prompter.promptAuth(prompt.channel,
prompt.level,
prompt.authInfo);
} catch (e) {
Components.utils.reportError("LoginManagerPrompter: " +
"_doAsyncPrompt:run: " + e + "\n");
}
delete self._asyncPrompts[hashKey];
prompt.inProgress = false;
self._asyncPromptInProgress = false;
for each (var consumer in prompt.consumers) {
@ -147,6 +159,34 @@ LoginManagerPromptFactory.prototype = {
this.log("_doAsyncPrompt:run dispatched");
},
_cancelPendingPrompts : function() {
this.log("Canceling all pending prompts...");
var asyncPrompts = this._asyncPrompts;
this.__proto__._asyncPrompts = {};
for each (var prompt in asyncPrompts) {
// Watch out! If this prompt is currently prompting, let it handle
// notifying the callbacks of success/failure, since it's already
// asking the user for input. Reusing a callback can be crashy.
if (prompt.inProgress) {
this.log("skipping a prompt in progress");
continue;
}
for each (var consumer in prompt.consumers) {
if (!consumer.callback)
continue;
this.log("Canceling async auth prompt callback " + consumer.callback);
try {
consumer.callback.onAuthCancelled(consumer.context, true);
} catch (e) { /* Just ignore exceptions from the callback */ }
}
}
},
log : function (message) {
if (!this._debug)
return;
@ -672,6 +712,7 @@ LoginManagerPrompter.prototype = {
channel: aChannel,
authInfo: aAuthInfo,
level: aLevel,
inProgress : false,
prompter: this
}

Просмотреть файл

@ -498,6 +498,10 @@ LoginManagerStorage_legacy.prototype = {
return count;
},
get uiBusy() {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
},

Просмотреть файл

@ -772,6 +772,14 @@ LoginManagerStorage_mozStorage.prototype = {
},
/*
* uiBusy
*/
get uiBusy() {
return this._crypto.uiBusy;
},
/*
* _sendNotification
*

Просмотреть файл

@ -75,6 +75,8 @@ MOCHI_TESTS = \
test_bug_391514.html \
test_bug_427033.html \
test_bug_444968.html \
test_master_password.html \
test_master_password_cleanup.html \
test_prompt_async.html \
test_notifications.html \
test_prompt.html \
@ -93,6 +95,7 @@ MOCHI_CONTENT = \
subtst_privbrowsing_2.html \
subtst_privbrowsing_3.html \
subtst_privbrowsing_4.html \
subtst_master_pass.html \
subtst_notifications_1.html \
subtst_notifications_2.html \
subtst_notifications_3.html \

Просмотреть файл

@ -163,3 +163,38 @@ function commonInit() {
disabledHosts = pwmgr.getAllDisabledHosts();
is(disabledHosts.length, 0, "Checking for no disabled hosts");
}
const masterPassword = "omgsecret!";
function enableMasterPassword() {
setMasterPassword(true);
}
function disableMasterPassword() {
setMasterPassword(false);
}
function setMasterPassword(enable) {
var oldPW, newPW;
if (enable) {
oldPW = "";
newPW = masterPassword;
} else {
oldPW = masterPassword;
newPW = "";
}
// Set master password. Note that this does not log you in, so the next
// invocation of pwmgr can trigger a MP prompt.
var pk11db = Cc["@mozilla.org/security/pk11tokendb;1"].
getService(Ci.nsIPK11TokenDB)
var token = pk11db.findTokenByName("");
ok(true, "change from " + oldPW + " to " + newPW);
token.changePassword(oldPW, newPW);
}
function logoutMasterPassword() {
var sdr = Cc["@mozilla.org/security/sdr;1"].
getService(Ci.nsISecretDecoderRing);
sdr.logoutAndTeardown();
}

Просмотреть файл

@ -0,0 +1,6 @@
<h2>MP subtest</h2>
This form triggers a MP and gets filled in.<br>
<form>
Username: <input type="text" id="userfield" name="u"><br>
Password: <input type="password" id="passfield" name="p"><br>
</form>

Просмотреть файл

@ -0,0 +1,279 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Login Manager</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="pwmgr_common.js"></script>
<script type="text/javascript" src="prompt_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
Login Manager test: master password.
<p id="display"></p>
<div id="content" style="display: none">
<iframe id="iframe1"></iframe>
<iframe id="iframe2"></iframe>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
commonInit();
var testNum = 1;
/*
* handleDialog
*
* Invoked a short period of time after calling startCallbackTimer(), and
* allows testing the actual auth dialog while it's being displayed. Tests
* should call startCallbackTimer() each time the auth dialog is expected (the
* timer is a one-shot).
*/
function handleDialog(doc, testNum) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
ok(true, "handleDialog running for test " + testNum);
var clickOK = true;
var doNothing = false;
var passfield = doc.getElementById("password1Textbox");
var dialog = doc.getElementById("commonDialog");
switch(testNum) {
case 1:
is(passfield.getAttribute("value"), "", "Checking empty prompt");
passfield.setAttribute("value", masterPassword);
is(passfield.getAttribute("value"), masterPassword, "Checking filled prompt");
break;
case 2:
clickOK = false;
break;
case 3:
is(passfield.getAttribute("value"), "", "Checking empty prompt");
passfield.setAttribute("value", masterPassword);
break;
case 4:
doNothing = true;
break;
case 5:
is(passfield.getAttribute("value"), "", "Checking empty prompt");
passfield.setAttribute("value", masterPassword);
break;
default:
ok(false, "Uhh, unhandled switch for testNum #" + testNum);
break;
}
if (!doNothing) {
if (clickOK)
dialog.acceptDialog();
else
dialog.cancelDialog();
}
ok(true, "handleDialog done");
didDialog = true;
if (testNum == 4)
checkTest4A();
}
function startTest1() {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
ok(pwcrypt.isLoggedIn, "should be initially logged in (no MP)");
enableMasterPassword();
ok(!pwcrypt.isLoggedIn, "should be logged out after setting MP");
// --- Test 1 ---
// Trigger a MP prompt via the API
startCallbackTimer();
var logins = pwmgr.getAllLogins();
ok(didDialog, "handleDialog was invoked");
is(logins.length, 3, "expected number of logins");
ok(pwcrypt.isLoggedIn, "should be logged in after MP prompt");
logoutMasterPassword();
ok(!pwcrypt.isLoggedIn, "should be logged out");
// --- Test 2 ---
// Try again but click cancel.
testNum++;
startCallbackTimer();
var failedAsExpected = false;
logins = null;
try {
logins = pwmgr.getAllLogins();
} catch (e) { failedAsExpected = true; }
ok(didDialog, "handleDialog was invoked");
ok(failedAsExpected, "getAllLogins should have thrown");
is(logins, null, "shouldn't have gotten logins");
ok(!pwcrypt.isLoggedIn, "should still be logged out");
// --- Test 3 ---
// Load a single iframe to trigger a MP
testNum++;
// Note that because DOMContentLoaded is dispatched synchronously, the
// document's load event is blocked until after the MP entry (because
// pwmgr's listener doesn't return until after it processes the form,
// which is blocked waiting on a MP entry).
iframe1.onload = checkTest3;
iframe1.src = exampleCom + "subtst_master_pass.html";
startCallbackTimer();
}
function checkTest3() {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
ok(true, "checkTest3 starting");
ok(didDialog, "handleDialog was invoked");
// check contents of iframe1 fields
var u = iframe1.contentDocument.getElementById("userfield");
var p = iframe1.contentDocument.getElementById("passfield");
is(u.value, "user1", "checking expected user to have been filled in");
is(p.value, "pass1", "checking expected pass to have been filled in");
ok(pwcrypt.isLoggedIn, "should be logged in");
logoutMasterPassword();
ok(!pwcrypt.isLoggedIn, "should be logged out");
// --- Test 4 ---
// first part of loading 2 MP-triggering iframes
testNum++;
iframe1.onload = checkTest4C;
iframe1.src = exampleOrg + "subtst_master_pass.html";
// start the callback, but we'll not enter the MP, just call checkTest4A
startCallbackTimer();
}
function checkTest4A() {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
ok(true, "checkTest4A starting");
ok(didDialog, "handleDialog was invoked");
// check contents of iframe1 fields
var u = iframe1.contentDocument.getElementById("userfield");
var p = iframe1.contentDocument.getElementById("passfield");
is(u.value, "", "checking expected empty user");
is(p.value, "", "checking expected empty pass");
ok(!pwcrypt.isLoggedIn, "should be logged out");
// XXX check that there's 1 MP window open
// Load another iframe with a login form
// This should detect that there's already a pending MP prompt, and not
// put up a second one. The load event will fire.
iframe2.onload = checkTest4B;
iframe2.src = exampleCom + "subtst_master_pass.html";
}
function checkTest4B() {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
ok(true, "checkTest4B starting");
// iframe2 should load without having triggered a MP prompt (because one
// is already waiting)
// check contents of iframe2 fields
var u = iframe2.contentDocument.getElementById("userfield");
var p = iframe2.contentDocument.getElementById("passfield");
is(u.value, "", "checking expected empty user");
is(p.value, "", "checking expected empty pass");
// XXX check that there's 1 MP window open
ok(!pwcrypt.isLoggedIn, "should be logged out");
// Ok, now enter the MP. The MP prompt is already up, but we'll just reuse startCallBackTimer.
// --- Test 5 ---
testNum++;
startCallbackTimer();
}
function checkTest4C() {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
// iframe1 finally loads after the MP entry.
ok(true, "checkTest4C starting");
ok(didDialog, "handleDialog was invoked");
// We shouldn't have to worry about iframe1's load event racing with
// filling of iframe2's data. We notify observers synchronously, so
// iframe2's observer will process iframe2 before iframe1 even finishes
// processing the form (which is blocking its load event).
ok(pwcrypt.isLoggedIn, "should be logged in");
// check contents of iframe1 fields
var u = iframe1.contentDocument.getElementById("userfield");
var p = iframe1.contentDocument.getElementById("passfield");
is(u.value, "user2", "checking expected user to have been filled in");
is(p.value, "pass2", "checking expected pass to have been filled in");
// check contents of iframe2 fields
u = iframe2.contentDocument.getElementById("userfield");
p = iframe2.contentDocument.getElementById("passfield");
is(u.value, "user1", "checking expected user to have been filled in");
is(p.value, "pass1", "checking expected pass to have been filled in");
finishTest();
}
// XXX do a test5ABC with clicking cancel?
function finishTest() {
disableMasterPassword();
ok(pwcrypt.isLoggedIn, "should be logged in");
pwmgr.removeLogin(login1);
pwmgr.removeLogin(login2);
SimpleTest.finish();
}
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
// Get the pwmgr service
var pwmgr = Cc["@mozilla.org/login-manager;1"].
getService(Ci.nsILoginManager);
ok(pwmgr != null, "pwmgr getService()");
var pwcrypt = Cc["@mozilla.org/login-manager/crypto/SDR;1"].
getService(Ci.nsILoginManagerCrypto);
ok(pwcrypt != null, "pwcrypt getService()");
var nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo);
ok(nsLoginInfo != null, "nsLoginInfo constructor");
var exampleCom = "http://example.com/tests/toolkit/components/passwordmgr/test/";
var exampleOrg = "http://example.org/tests/toolkit/components/passwordmgr/test/";
var login1 = new nsLoginInfo();
var login2 = new nsLoginInfo();
login1.init("http://example.com", "http://example.com", null,
"user1", "pass1", "uname", "pword");
login2.init("http://example.org", "http://example.org", null,
"user2", "pass2", "uname", "pword");
pwmgr.addLogin(login1);
pwmgr.addLogin(login2);
var iframe1 = document.getElementById("iframe1");
var iframe2 = document.getElementById("iframe2");
window.onload = startTest1;
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,54 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Login Manager</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="pwmgr_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
Login Manager test: master password cleanup
<p id="display"></p>
<div id="content" style="display: none">
<iframe id="iframe1"></iframe>
<iframe id="iframe2"></iframe>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/*
* The entire purpose of this test is to make sure that, if the previous master
* password test did not complete successfully, we don't screw up other pwmgr
* tests by having a master password unexpectedly set.
*/
function cleanup() {
ok(true, "ensuring MP is cleared");
var pk11db = Cc["@mozilla.org/security/pk11tokendb;1"].
getService(Ci.nsIPK11TokenDB);
var token = pk11db.getInternalKeyToken();
if (!token.checkPassword("")) {
ok(true, "Oops! MP still set, clearing it...");
disableMasterPassword();
} else {
ok(true, "Master password already cleared.");
}
ok(true, "done.");
}
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
const Cc = Components.classes;
const Ci = Components.interfaces;
cleanup();
</script>
</pre>
</body>
</html>