Bug 477917 - expose login GUIDs through login manager API. r=gavin

This commit is contained in:
Justin Dolske 2009-02-25 23:40:38 -08:00
Родитель 5283b55f60
Коммит 9384b7cc85
9 изменённых файлов: 514 добавлений и 31 удалений

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

@ -46,6 +46,7 @@ XPIDL_MODULE = loginmgr
XPIDLSRCS = \
nsILoginInfo.idl \
nsILoginMetaInfo.idl \
nsILoginManager.idl \
nsILoginManagerStorage.idl \
nsILoginManagerPrompter.idl \

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

@ -37,7 +37,7 @@
#include "nsISupports.idl"
[scriptable, uuid(9c87a9bd-bf8b-4fae-bdb8-70513b2877df)]
[scriptable, uuid(c41b7dff-6b9b-42fe-b78d-113051facb05)]
/**
* An object containing information for a login stored by the
@ -134,6 +134,15 @@ interface nsILoginInfo : nsISupports {
* If true, ignore the password when checking for match.
*/
boolean matches(in nsILoginInfo aLoginInfo, in boolean ignorePassword);
/**
* Create an identical copy of the login, duplicating all of the login's
* nsILoginInfo and nsILoginMetaInfo properties.
*
* This allows code to be forwards-compatible, when additional properties
* are added to nsILoginMetaInfo (or nsILoginInfo) in the future.
*/
nsILoginInfo clone();
};
%{C++

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

@ -43,7 +43,7 @@ interface nsIAutoCompleteResult;
interface nsIDOMHTMLInputElement;
interface nsIDOMHTMLFormElement;
[scriptable, uuid(04dbfa30-4238-11dd-ae16-0800200c9a66)]
[scriptable, uuid(9c78bfc1-422b-4f4f-ba09-f7eb3c4e72b2)]
interface nsILoginManager : nsISupports {
@ -52,6 +52,10 @@ interface nsILoginManager : nsISupports {
*
* @param aLogin
* The login to be added.
*
* Default values for the login's nsILoginMetaInfo properties will be
* created. However, if the caller specifies non-default values, they will
* be used instead.
*/
void addLogin(in nsILoginInfo aLogin);
@ -61,6 +65,9 @@ interface nsILoginManager : nsISupports {
*
* @param aLogin
* The login to be removed.
*
* The specified login must exactly match a stored login. However, the
* values of any nsILoginMetaInfo properties are ignored.
*/
void removeLogin(in nsILoginInfo aLogin);
@ -68,10 +75,20 @@ interface nsILoginManager : nsISupports {
/**
* Modify an existing login in the login manager.
*
* @param aLogin
* @param oldLogin
* The login to be modified.
* @param newLoginData
* The new login values (either a nsILoginInfo or nsIProperyBag)
*
* If newLoginData is a nsILoginInfo, all of the old login's nsILoginInfo
* properties are changed to the values from newLoginData (but the old
* login's nsILoginMetaInfo properties are unmodified).
*
* If newLoginData is a nsIPropertyBag, only the specified properties
* will be changed. The nsILoginMetaInfo properties of oldLogin can be
* changed in this manner.
*/
void modifyLogin(in nsILoginInfo oldLogin, in nsILoginInfo newLogin);
void modifyLogin(in nsILoginInfo oldLogin, in nsISupports newLoginData);
/**

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

@ -40,7 +40,7 @@
interface nsIFile;
interface nsILoginInfo;
[scriptable, uuid(dec624d1-18ea-40ea-8ca5-69002153c8b8)]
[scriptable, uuid(199ebbff-4656-4a18-8da9-9401c64619f9)]
/*
* NOTE: This interface is intended to be implemented by modules
@ -71,30 +71,47 @@ interface nsILoginManagerStorage : nsISupports {
/**
* Store a new login.
* Store a new login in the storage module.
*
* @param aLogin
* The login to be added.
*
* Default values for the login's nsILoginMetaInfo properties will be
* created. However, if the caller specifies non-default values, they will
* be used instead.
*/
void addLogin(in nsILoginInfo aLogin);
/**
* Remove a login from the login manager.
* Remove a login from the storage module.
*
* @param aLogin
* The login to be removed.
*
* The specified login must exactly match a stored login. However, the
* values of any nsILoginMetaInfo properties are ignored.
*/
void removeLogin(in nsILoginInfo aLogin);
/**
* Modify an existing login in the login manager.
* Modify an existing login in the storage module.
*
* @param aLogin
* @param oldLogin
* The login to be modified.
* @param newLoginData
* The new login values (either a nsILoginInfo or nsIProperyBag)
*
* If newLoginData is a nsILoginInfo, all of the old login's nsILoginInfo
* properties are changed to the values from newLoginData (but the old
* login's nsILoginMetaInfo properties are unmodified).
*
* If newLoginData is a nsIPropertyBag, only the specified properties
* will be changed. The nsILoginMetaInfo properties of oldLogin can be
* changed in this manner.
*/
void modifyLogin(in nsILoginInfo oldLogin, in nsILoginInfo newLogin);
void modifyLogin(in nsILoginInfo oldLogin, in nsISupports newLoginData);
/**

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

@ -0,0 +1,62 @@
/* ***** 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 Corporation.
* Portions created by the Initial Developer are Copyright (C) 2009
* 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"
[scriptable, uuid(867407d5-10e0-43a0-bc81-a324740534ca)]
/**
* An object containing metainfo for a login stored by the login manager.
*
* Code using login manager can generally ignore this interface. When adding
* logins, default value will be created. When modifying logins, these
* properties will be unchanged unless a change is explicitly requested [by
* using modifyLogin() with a nsIPropertyBag]. When deleting a login or
* comparing logins, these properties are ignored.
*/
interface nsILoginMetaInfo : nsISupports {
/**
* The GUID to uniquely identify the login. This can be any arbitrary
* string, but a format as created by nsIUUIDGenerator is recommended.
* For example, "{d4e1a1f6-5ea0-40ee-bff5-da57982f21cf}"
*
* addLogin will generate a random value unless a value is provided.
*
* addLogin and modifyLogin will throw if the GUID already exists.
*/
attribute AString guid;
};

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

@ -47,7 +47,7 @@ nsLoginInfo.prototype = {
classDescription : "LoginInfo",
contractID : "@mozilla.org/login-manager/loginInfo;1",
classID : Components.ID("{0f2f347c-1e4f-40cc-8efd-792dea70a85e}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsILoginInfo]),
QueryInterface: XPCOMUtils.generateQI([Ci.nsILoginInfo, Ci.nsILoginMetaInfo]),
// Allow storage-Legacy.js to get at the JS object so it can
// slap on a few extra properties for internal use.
@ -55,6 +55,10 @@ nsLoginInfo.prototype = {
return this;
},
//
// nsILoginInfo interfaces...
//
hostname : null,
formSubmitURL : null,
httpRealm : null,
@ -105,7 +109,27 @@ nsLoginInfo.prototype = {
return false;
return true;
}
},
clone : function() {
let clone = Cc["@mozilla.org/login-manager/loginInfo;1"].
createInstance(Ci.nsILoginInfo);
clone.init(this.hostname, this.formSubmitURL, this.httpRealm,
this.username, this.password,
this.usernameField, this.passwordField);
// Copy nsILoginMetaInfo props
clone.QueryInterface(Ci.nsILoginMetaInfo);
clone.guid = this.guid;
return clone;
},
//
// nsILoginMetaInfo interfaces...
//
guid : null
}; // end of nsLoginInfo implementation

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

@ -311,6 +311,9 @@ LoginManagerStorage_legacy.prototype = {
*
*/
modifyLogin : function (oldLogin, newLogin) {
if (newLogin instanceof Ci.nsIPropertyBag)
throw "legacy modifyLogin with propertybag not implemented.";
newLogin.QueryInterface(Ci.nsILoginInfo);
// Throws if there are bogus values.
this._checkLoginValues(newLogin);

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

@ -271,7 +271,17 @@ LoginManagerStorage_mozStorage.prototype = {
throw "User canceled master password entry, login not added.";
}
let guid = this._uuidService.generateUUID().toString();
// Clone the login, so we don't modify the caller's object.
let loginClone = login.clone();
// Initialize the nsILoginMetaInfo fields, unless the caller gave us values
loginClone.QueryInterface(Ci.nsILoginMetaInfo);
if (loginClone.guid) {
if (!this._isGuidUnique(loginClone.guid))
throw "specified GUID already exists";
} else {
loginClone.guid = this._uuidService.generateUUID().toString();
}
let query =
"INSERT INTO moz_logins " +
@ -283,14 +293,14 @@ LoginManagerStorage_mozStorage.prototype = {
":guid)";
let params = {
hostname: login.hostname,
httpRealm: login.httpRealm,
formSubmitURL: login.formSubmitURL,
usernameField: login.usernameField,
passwordField: login.passwordField,
hostname: loginClone.hostname,
httpRealm: loginClone.httpRealm,
formSubmitURL: loginClone.formSubmitURL,
usernameField: loginClone.usernameField,
passwordField: loginClone.passwordField,
encryptedUsername: encUsername,
encryptedPassword: encPassword,
guid: guid
guid: loginClone.guid
};
let stmt;
@ -311,7 +321,7 @@ LoginManagerStorage_mozStorage.prototype = {
*
*/
removeLogin : function (login) {
let idToDelete = this._getIdForLogin(login);
let [idToDelete, storedLogin] = this._getIdForLogin(login);
if (!idToDelete)
throw "No matching logins";
@ -335,13 +345,60 @@ LoginManagerStorage_mozStorage.prototype = {
* modifyLogin
*
*/
modifyLogin : function (oldLogin, newLogin) {
// Throws if there are bogus values.
this._checkLoginValues(newLogin);
let idToModify = this._getIdForLogin(oldLogin);
modifyLogin : function (oldLogin, newLoginData) {
let [idToModify, oldStoredLogin] = this._getIdForLogin(oldLogin);
if (!idToModify)
throw "No matching logins";
oldStoredLogin.QueryInterface(Ci.nsILoginMetaInfo);
let newLogin;
if (newLoginData instanceof Ci.nsILoginInfo) {
// Clone the existing login to get its nsILoginMetaInfo, then init it
// with the replacement nsILoginInfo data from the new login.
newLogin = oldStoredLogin.clone();
newLogin.init(newLoginData.hostname,
newLoginData.formSubmitURL, newLoginData.httpRealm,
newLoginData.username, newLoginData.password,
newLoginData.usernameField, newLoginData.passwordField);
newLogin.QueryInterface(Ci.nsILoginMetaInfo);
} else if (newLoginData instanceof Ci.nsIPropertyBag) {
// Clone the existing login, along with all its properties.
newLogin = oldStoredLogin.clone();
newLogin.QueryInterface(Ci.nsILoginMetaInfo);
let propEnum = newLoginData.enumerator;
while (propEnum.hasMoreElements()) {
let prop = propEnum.getNext().QueryInterface(Ci.nsIProperty);
switch (prop.name) {
// nsILoginInfo properties...
case "hostname":
case "httpRealm":
case "formSubmitURL":
case "username":
case "password":
case "usernameField":
case "passwordField":
newLogin[prop.name] = prop.value;
break;
// nsILoginMetaInfo properties...
case "guid":
newLogin.guid = prop.value;
if (!this._isGuidUnique(newLogin.guid))
throw "specified GUID already exists";
break;
// Fail if caller requests setting an unknown property.
default:
throw "Unexpected propertybag item: " + prop.name;
}
}
} else {
throw "newLoginData needs an expected interface!";
}
// Throws if there are bogus values.
this._checkLoginValues(newLogin);
// Get the encrypted value of the username and password.
let [encUsername, encPassword, userCanceled] = this._encryptLogin(newLogin);
@ -356,10 +413,12 @@ LoginManagerStorage_mozStorage.prototype = {
"usernameField = :usernameField, " +
"passwordField = :passwordField, " +
"encryptedUsername = :encryptedUsername, " +
"encryptedPassword = :encryptedPassword " +
"encryptedPassword = :encryptedPassword, " +
"guid = :guid " +
"WHERE id = :id";
let params = {
id: idToModify,
hostname: newLogin.hostname,
httpRealm: newLogin.httpRealm,
formSubmitURL: newLogin.formSubmitURL,
@ -367,8 +426,7 @@ LoginManagerStorage_mozStorage.prototype = {
passwordField: newLogin.passwordField,
encryptedUsername: encUsername,
encryptedPassword: encPassword,
id: idToModify
// guid not changed
guid: newLogin.guid
};
let stmt;
@ -552,13 +610,15 @@ LoginManagerStorage_mozStorage.prototype = {
/*
* _getIdForLogin
*
* Returns the |id| for the specified login, or null if the login was not
* found.
* Returns an array with two items: [id, login]. If the login was not
* found, both items will be null. The returned login contains the actual
* stored login (useful for looking at the actual nsILoginMetaInfo values).
*/
_getIdForLogin : function (login) {
let [logins, ids] =
this._queryLogins(login.hostname, login.formSubmitURL, login.httpRealm);
let id = null;
let foundLogin = null;
// The specified login isn't encrypted, so we need to ensure
// the logins we're comparing with are decrypted. We decrypt one entry
@ -575,11 +635,12 @@ LoginManagerStorage_mozStorage.prototype = {
continue;
// We've found a match, set id and break
foundLogin = decryptedLogin;
id = ids[i];
break;
}
return id;
return [id, foundLogin];
},
@ -614,6 +675,9 @@ LoginManagerStorage_mozStorage.prototype = {
stmt.row.httpRealm, stmt.row.encryptedUsername,
stmt.row.encryptedPassword, stmt.row.usernameField,
stmt.row.passwordField);
// set nsILoginMetaInfo values
login.QueryInterface(Ci.nsILoginMetaInfo);
login.guid = stmt.row.guid;
logins.push(login);
ids.push(stmt.row.id);
}
@ -758,6 +822,30 @@ LoginManagerStorage_mozStorage.prototype = {
},
/*
* _isGuidUnique
*
* Checks to see if the specified GUID already exists.
*/
_isGuidUnique : function (guid) {
let query = "SELECT COUNT(1) AS numLogins FROM moz_logins WHERE guid = :guid";
let params = { guid: guid };
let stmt, numLogins;
try {
stmt = this._dbCreateStatement(query, params);
stmt.step();
numLogins = stmt.row.numLogins;
} catch (e) {
this.log("_isGuidUnique failed: " + e.name + " : " + e.message);
} finally {
stmt.reset();
}
return (numLogins == 0);
},
/*
* _importLegacySignons
*

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

@ -0,0 +1,262 @@
/*
* Test suite for storage-mozStorage.js
*
* This test interfaces directly with the mozStorage password storage module,
* bypassing the normal password manager usage.
*
*/
const STORAGE_TYPE = "mozStorage";
function run_test() {
try {
var testnum = 0;
var testdesc = "Setup of nsLoginInfo test-users";
var nsLoginInfo = new Components.Constructor(
"@mozilla.org/login-manager/loginInfo;1",
Components.interfaces.nsILoginInfo);
do_check_true(nsLoginInfo != null);
var testuser1 = new nsLoginInfo;
testuser1.QueryInterface(Ci.nsILoginMetaInfo);
testuser1.init("http://testhost1", "", null,
"dummydude", "itsasecret", "put_user_here", "put_pw_here");
var guid1;
var testuser2 = new nsLoginInfo;
testuser2.init("http://testhost2", "", null,
"dummydude2", "itsasecret2", "put_user2_here", "put_pw2_here");
testuser2.QueryInterface(Ci.nsILoginMetaInfo);
var guid2 = "{12345678-abcd-1234-abcd-987654321000}";
testuser2.guid = guid2;
var testuser3 = new nsLoginInfo;
testuser3.QueryInterface(Ci.nsILoginMetaInfo);
testuser3.init("http://testhost3", "", null,
"dummydude3", "itsasecret3", "put_user3_here", "put_pw3_here");
var guid3 = "{99999999-abcd-9999-abcd-999999999999}";
// This login is different than testuser2, except it has the same guid.
var testuser2dupeguid = new nsLoginInfo;
testuser2dupeguid.QueryInterface(Ci.nsILoginMetaInfo);
testuser2dupeguid.init("http://dupe-testhost2", "", null,
"dupe-dummydude2", "dupe-itsasecret2", "put_user2_here", "put_pw2_here");
testuser2dupeguid.QueryInterface(Ci.nsILoginMetaInfo);
testuser2dupeguid.guid = guid2;
var isGUID = /^\{[0-9a-f\d]{8}-[0-9a-f\d]{4}-[0-9a-f\d]{4}-[0-9a-f\d]{4}-[0-9a-f\d]{12}\}$/;
/* ========== 1 ========== */
var testnum = 1;
var testdesc = "Initial connection to storage module"
LoginTest.deleteFile(OUTDIR, "signons-unittest6.sqlite");
var storage;
storage = LoginTest.initStorage(INDIR, "signons-empty.txt", OUTDIR, "signons-unittest6.sqlite");
var logins = storage.getAllLogins({});
do_check_eq(logins.length, 0, "Checking for no initial logins");
var disabledHosts = storage.getAllDisabledHosts({});
do_check_eq(disabledHosts.length, 0, "Checking for no initial disabled hosts");
/* ========== 2 ========== */
testnum++;
testdesc = "add user1 w/o guid";
storage.addLogin(testuser1);
LoginTest.checkStorageData(storage, [], [testuser1]);
// Check guid
do_check_eq(testuser1.guid, null, "caller's login shouldn't be modified");
logins = storage.findLogins({}, "http://testhost1", "", null);
do_check_eq(logins.length, 1, "expecting 1 login");
logins[0].QueryInterface(Ci.nsILoginMetaInfo);
do_check_true(isGUID.test(logins[0].guid), "testuser1 guid is set");
guid1 = logins[0].guid;
/* ========== 3 ========== */
testnum++;
testdesc = "add user2 WITH guid";
storage.addLogin(testuser2);
LoginTest.checkStorageData(storage, [], [testuser1, testuser2]);
// Check guid
do_check_eq(testuser2.guid, guid2, "caller's login shouldn't be modified");
logins = storage.findLogins({}, "http://testhost2", "", null);
do_check_eq(logins.length, 1, "expecting 1 login");
logins[0].QueryInterface(Ci.nsILoginMetaInfo);
do_check_true(isGUID.test(logins[0].guid), "testuser2 guid is set");
do_check_eq(logins[0].guid, guid2, "checking guid2");
/* ========== 4 ========== */
testnum++;
testdesc = "add user3 w/o guid";
storage.addLogin(testuser3);
LoginTest.checkStorageData(storage, [], [testuser1, testuser2, testuser3]);
logins = storage.findLogins({}, "http://testhost3", "", null);
do_check_eq(logins.length, 1, "expecting 1 login");
logins[0].QueryInterface(Ci.nsILoginMetaInfo);
do_check_true(isGUID.test(logins[0].guid), "testuser3 guid is set");
do_check_neq(logins[0].guid, guid3, "testuser3 guid is different");
/* ========== 5 ========== */
testnum++;
testdesc = "(don't) modify user1";
// When newlogin.guid is blank, the GUID shouldn't be changed.
testuser1.guid = "";
storage.modifyLogin(testuser1, testuser1);
// Check it
do_check_eq(testuser1.guid, "", "caller's login shouldn't be modified");
logins = storage.findLogins({}, "http://testhost1", "", null);
do_check_eq(logins.length, 1, "expecting 1 login");
logins[0].QueryInterface(Ci.nsILoginMetaInfo);
do_check_eq(logins[0].guid, guid1, "checking guid1");
/* ========== 6 ========== */
testnum++;
testdesc = "modify user3";
// change the GUID to our known value
var propbag = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag);
propbag.setProperty("guid", guid3);
storage.modifyLogin(testuser3, propbag);
// Check it
logins = storage.findLogins({}, "http://testhost3", "", null);
do_check_eq(logins.length, 1, "expecting 1 login");
logins[0].QueryInterface(Ci.nsILoginMetaInfo);
do_check_eq(logins[0].guid, guid3, "checking guid3");
/* ========== 7 ========== */
testnum++;
testdesc = "try adding a duplicate guid";
var ex = null;
try {
storage.addLogin(testuser2dupeguid);
} catch (e) {
ex = e;
}
do_check_true(/specified GUID already exists/.test(ex), "ensuring exception thrown when adding duplicate GUID");
LoginTest.checkStorageData(storage, [], [testuser1, testuser2, testuser3]);
/* ========== 8 ========== */
testnum++;
testdesc = "try modifing to a duplicate guid";
propbag = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag);
propbag.setProperty("guid", testuser2dupeguid.guid);
ex = null;
try {
storage.modifyLogin(testuser1, propbag);
} catch (e) {
ex = e;
}
do_check_true(/specified GUID already exists/.test(ex), "ensuring exception thrown when modifying to duplicate GUID");
LoginTest.checkStorageData(storage, [], [testuser1, testuser2, testuser3]);
/* ========== 9 ========== */
testnum++;
testdesc = "check propertybag nulls/empty strings";
// Set formSubmitURL to a null, and usernameField to a empty-string.
do_check_eq(testuser3.formSubmitURL, "");
do_check_eq(testuser3.httpRealm, null);
do_check_eq(testuser3.usernameField, "put_user3_here");
propbag = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag);
propbag.setProperty("formSubmitURL", null);
propbag.setProperty("httpRealm", "newRealm");
propbag.setProperty("usernameField", "");
storage.modifyLogin(testuser3, propbag);
// Fixup testuser3 to match the new values.
testuser3.formSubmitURL = null;
testuser3.httpRealm = "newRealm";
testuser3.usernameField = "";
LoginTest.checkStorageData(storage, [], [testuser1, testuser2, testuser3]);
/* ========== 10 ========== */
testnum++;
testdesc = "[reinit storage, look for expected guids]";
storage = LoginTest.reloadStorage(OUTDIR, "signons-unittest6.sqlite");
LoginTest.checkStorageData(storage, [], [testuser1, testuser2, testuser3]);
logins = storage.findLogins({}, "http://testhost1", "", null);
do_check_eq(logins.length, 1, "expecting 1 login");
logins[0].QueryInterface(Ci.nsILoginMetaInfo);
do_check_eq(logins[0].guid, guid1, "checking guid1");
logins = storage.findLogins({}, "http://testhost2", "", null);
do_check_eq(logins.length, 1, "expecting 1 login");
logins[0].QueryInterface(Ci.nsILoginMetaInfo);
do_check_eq(logins[0].guid, guid2, "checking guid2");
logins = storage.findLogins({}, "http://testhost3", null, "newRealm");
do_check_eq(logins.length, 1, "expecting 1 login");
logins[0].QueryInterface(Ci.nsILoginMetaInfo);
do_check_eq(logins[0].guid, guid3, "checking guid3");
/* ========== 11 ========== */
testnum++;
testdesc = "login w/o nsILoginMetaInfo impl";
var wonkyDelegate = new nsLoginInfo;
wonkyDelegate.init("http://wonky", null, "wonkyness",
"wonkyuser", "wonkypass", "u", "p");
var wonkyLogin = {
QueryInterface : function (iid) {
var interfaces = [Ci.nsILoginInfo, Ci.nsISupports];
if (!interfaces.some( function(v) { return iid.equals(v) }))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
},
hostname: wonkyDelegate.hostname,
formSubmitURL: wonkyDelegate.formSubmitURL,
httpRealm: wonkyDelegate.httpRealm,
username: wonkyDelegate.username,
password: wonkyDelegate.password,
usernameField: wonkyDelegate.usernameField,
passwordField: wonkyDelegate.passwordField,
equals: wonkyDelegate.equals,
matches: wonkyDelegate.matches,
clone: wonkyDelegate.clone
};
storage.addLogin(wonkyLogin);
LoginTest.checkStorageData(storage, [], [testuser1, testuser2, testuser3, wonkyLogin]);
logins = storage.findLogins({}, "http://wonky", null, "");
do_check_eq(logins.length, 1, "expecting 1 login");
logins[0].QueryInterface(Ci.nsILoginMetaInfo);
do_check_true(isGUID.test(logins[0].guid), "wonky guid is set");
storage.modifyLogin(wonkyLogin, wonkyLogin);
LoginTest.checkStorageData(storage, [], [testuser1, testuser2, testuser3, wonkyLogin]);
storage.removeLogin(wonkyLogin);
LoginTest.checkStorageData(storage, [], [testuser1, testuser2, testuser3]);
LoginTest.deleteFile(OUTDIR, "signons-unittest6.sqlite");
} catch (e) {
throw "FAILED in test #" + testnum + " -- " + testdesc + ": " + e;
}
};