Bug 439365 – need a notification to fire when a form is available to be filled in. r=dolske

This commit is contained in:
Paul O'Shannessy 2008-06-26 18:04:49 -07:00
parent ce4913a634
commit 1a1ab238f9
6 changed files with 285 additions and 54 deletions

View File

@ -43,7 +43,7 @@ interface nsIAutoCompleteResult;
interface nsIDOMHTMLInputElement;
interface nsIDOMHTMLFormElement;
[scriptable, uuid(5247f630-38a5-11dd-ae16-0800200c9a66)]
[scriptable, uuid(04dbfa30-4238-11dd-ae16-0800200c9a66)]
interface nsILoginManager : nsISupports {
@ -219,8 +219,9 @@ interface nsILoginManager : nsISupports {
*
* @param aForm
* The form to fill
* @return Success of attempt fill form
*/
void fillForm(in nsIDOMHTMLFormElement aForm);
boolean fillForm(in nsIDOMHTMLFormElement aForm);
};
%{C++

View File

@ -84,6 +84,15 @@ LoginManager.prototype = {
},
__observerService : null, // Observer Service, for notifications
get _observerService() {
if (!this.__observerService)
this.__observerService = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
return this.__observerService;
},
__storage : null, // Storage component which contains the saved logins
get _storage() {
if (!this.__storage) {
@ -155,10 +164,8 @@ LoginManager.prototype = {
// Form submit observer checks forms for new logins and pw changes.
var observerService = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
observerService.addObserver(this._observer, "earlyformsubmit", false);
observerService.addObserver(this._observer, "xpcom-shutdown", false);
this._observerService.addObserver(this._observer, "earlyformsubmit", false);
this._observerService.addObserver(this._observer, "xpcom-shutdown", false);
// WebProgressListener for getting notification of new doc loads.
var progress = Cc["@mozilla.org/docloaderservice;1"].
@ -966,7 +973,7 @@ LoginManager.prototype = {
previousActionOrigin = actionOrigin;
}
this.log("_fillDocument processing form[" + i + "]");
foundLogins = this._fillForm(form, autofillForm, foundLogins);
foundLogins = this._fillForm(form, autofillForm, false, foundLogins)[1];
} // foreach form
},
@ -976,10 +983,14 @@ LoginManager.prototype = {
*
* Fill the form with login information if we can find it. This will find
* an array of logins if not given any, otherwise it will use the logins
* passed in. The logins are returned so they can be reused for
* optimization.
* passed in. The logins are returned so they can be reused for
* optimization. Success of action is also returned in format
* [success, foundLogins]. autofillForm denotes if we should fill the form
* in automatically, ignoreAutocomplete denotes if we should ignore
* autocomplete=off attributes, and foundLogins is an array of nsILoginInfo
* for optimization
*/
_fillForm : function (form, autofillForm, foundLogins) {
_fillForm : function (form, autofillForm, ignoreAutocomplete, foundLogins) {
// Heuristically determine what the user/pass fields are
// We do this before checking to see if logins are stored,
// so that the user isn't prompted for a master password
@ -989,7 +1000,7 @@ LoginManager.prototype = {
// Need a valid password field to do anything.
if (passwordField == null)
return foundLogins;
return [false, foundLogins];
// Need to get a list of logins if we weren't given them
if (foundLogins == null) {
@ -1027,7 +1038,7 @@ LoginManager.prototype = {
// Nothing to do if we have no matching logins available.
if (logins.length == 0)
return foundLogins;
return [false, foundLogins];
// Attach autocomplete stuff to the username field, if we have
@ -1041,55 +1052,72 @@ LoginManager.prototype = {
// the autocomplete stuff to the username field, so the user can
// still manually select a login to be filled in.
var isFormDisabled = false;
if (this._isAutocompleteDisabled(form) ||
this._isAutocompleteDisabled(usernameField) ||
this._isAutocompleteDisabled(passwordField)) {
if (!ignoreAutocomplete &&
(this._isAutocompleteDisabled(form) ||
this._isAutocompleteDisabled(usernameField) ||
this._isAutocompleteDisabled(passwordField))) {
isFormDisabled = true;
this.log("form not filled, has autocomplete=off");
}
if (autofillForm && !isFormDisabled) {
// Variable such that we reduce code duplication and can be sure we
// should be firing notifications if and only if we can fill the form.
var selectedLogin = null;
if (usernameField && usernameField.value) {
// If username was specified in the form, only fill in the
// password if we find a matching login.
if (usernameField && usernameField.value) {
// If username was specified in the form, only fill in the
// password if we find a matching login.
var username = usernameField.value;
var username = usernameField.value;
var matchingLogin;
var found = logins.some(function(l) {
matchingLogin = l;
return (l.username == username);
});
if (found)
passwordField.value = matchingLogin.password;
else
this.log("Password not filled. None of the stored " +
"logins match the username already present.");
var matchingLogin;
var found = logins.some(function(l) {
matchingLogin = l;
return (l.username == username);
});
if (found)
selectedLogin = matchingLogin;
else
this.log("Password not filled. None of the stored " +
"logins match the username already present.");
} else if (usernameField && logins.length == 2) {
// Special case, for sites which have a normal user+pass
// login *and* a password-only login (eg, a PIN)...
// When we have a username field and 1 of 2 available
// logins is password-only, go ahead and prefill the
// one with a username.
if (!logins[0].username && logins[1].username) {
usernameField.value = logins[1].username;
passwordField.value = logins[1].password;
} else if (!logins[1].username && logins[0].username) {
usernameField.value = logins[0].username;
passwordField.value = logins[0].password;
}
} else if (logins.length == 1) {
if (usernameField)
usernameField.value = logins[0].username;
passwordField.value = logins[0].password;
} else {
this.log("Multiple logins for form, so not filling any.");
}
} else if (usernameField && logins.length == 2) {
// Special case, for sites which have a normal user+pass
// login *and* a password-only login (eg, a PIN)...
// When we have a username field and 1 of 2 available
// logins is password-only, go ahead and prefill the
// one with a username.
if (!logins[0].username && logins[1].username)
selectedLogin = logins[1];
else if (!logins[1].username && logins[0].username)
selectedLogin = logins[0];
} else if (logins.length == 1) {
selectedLogin = logins[0];
} else {
this.log("Multiple logins for form, so not filling any.");
}
return foundLogins;
var didFillForm = false;
if (selectedLogin && autofillForm && !isFormDisabled) {
// Fill the form
if (usernameField)
usernameField.value = selectedLogin.username;
passwordField.value = selectedLogin.password;
didFillForm = true;
} else if (selectedLogin && !autofillForm) {
// For when autofillForm is false, but we still have the information
// to fill a form, we notify observers.
this._observerService.notifyObservers(form, "passwordmgr-found-form", "noAutofillForms");
this.log("autofillForms=false but form can be filled; notified observers");
} else if (selectedLogin && isFormDisabled) {
// For when autocomplete is off, but we still have the information
// to fill a form, we notify observers.
this._observerService.notifyObservers(form, "passwordmgr-found-form", "autocompleteOff");
this.log("autocomplete=off but form can be filled; notified observers");
}
return [didFillForm, foundLogins];
},
@ -1100,7 +1128,7 @@ LoginManager.prototype = {
*/
fillForm : function (form) {
this.log("fillForm processing form[id=" + form.id + "]");
this._fillForm(form, true, null)
return this._fillForm(form, true, true, null)[0];
},

View File

@ -57,6 +57,8 @@ MOCHI_TESTS = \
test_basic_form_2pw_2.html \
test_basic_form_3pw_1.html \
test_basic_form_autocomplete.html \
test_basic_form_observer_autofillForms.html \
test_basic_form_observer_autocomplete.html \
test_basic_form_pwonly.html \
test_bug_227640.html \
test_bug_242956.html \

View File

@ -45,8 +45,9 @@ function startTest(){
is($_(1, "uname").value, "", "Checking for blank username");
is($_(1, "pword").value, "", "Checking for blank password");
// Call the public method
pwmgr.fillForm(document.getElementById("form1"));
// Call the public method, check return value
is(pwmgr.fillForm(document.getElementById("form1")), true,
"Checking return value of fillForm");
// Check that the form was filled
is($_(1, "uname").value, "testuser", "Checking for filled username");

View File

@ -0,0 +1,107 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Login Manager</title>
<script type="text/javascript" src="/MochiKit/MochiKit.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: simple form with autocomplete off and notifying observers & normal form
<p id="display"></p>
<div id="content" style="display: block">
<form id="form1" action="formtest.js" autocomplete="off">
<p>This is form 1.</p>
<input type="text" name="uname">
<input type="password" name="pword">
<button type="submit">Submit</button>
<button type="reset"> Reset </button>
</form>
<form id="form2" action="formtest.js">
<p>This is form 2.</p>
<input type="text" name="uname">
<input type="password" name="pword">
<button type="submit">Submit</button>
<button type="reset"> Reset </button>
</form>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Login Manager: simple form with autocomplete off and notifying observers & normal form **/
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var TestObserver = {
receivedNotification1 : false,
receivedNotification2 : false,
data1 : "",
data2 : "",
QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
observe : function (subject, topic, data) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var pwmgr = Cc["@mozilla.org/login-manager;1"].
getService(Ci.nsILoginManager);
if (topic == "passwordmgr-found-form") {
if (subject.id == "form1") {
this.receivedNotification1 = true;
this.data1 = data;
} else if (subject.id == "form2") {
this.receivedNotification2 = true;
this.data2 = data;
}
// Now fill the form
pwmgr.fillForm(subject);
}
}
};
// Add the observer
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver(TestObserver, "passwordmgr-found-form", false);
function startTest(){
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
// Test that observer is notified & got correct data
is(TestObserver.receivedNotification1, true, "Checking observer was notified");
is(TestObserver.data1, "autocompleteOff", "Checking observer got correct data");
// Check that form1 was filled
is($_(1, "uname").value, "testuser", "Checking for filled username 1");
is($_(1, "pword").value, "testpass", "Checking for filled password 1");
// Test that observer wasn't notified & didn't get data
is(TestObserver.receivedNotification2, false, "Checking observer was not notified");
is(TestObserver.data2, "", "Checking observer didn't get data");
// Check that form2 was filled
is($_(2, "uname").value, "testuser", "Checking for filled username 2");
is($_(2, "pword").value, "testpass", "Checking for filled password 2");
// Remove the observer
os.removeObserver(TestObserver, "passwordmgr-found-form");
SimpleTest.finish();
}
window.onload = startTest;
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,92 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Login Manager</title>
<script type="text/javascript" src="/MochiKit/MochiKit.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: simple form with autofillForms disabled and notifying observers
<p id="display"></p>
<div id="content" style="display: block">
<form id="form1" action="formtest.js">
<p>This is form 1.</p>
<input type="text" name="uname">
<input type="password" name="pword">
<button type="submit">Submit</button>
<button type="reset"> Reset </button>
</form>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Login Manager: simple form with autofillForms disabled and notifying observers **/
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService);
prefs = prefs.getBranch("signon.");
// Assume that the pref starts out true, so set to false
prefs.setBoolPref("autofillForms", false);
var TestObserver = {
receivedNotification : false,
data : "",
QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
observe : function (subject, topic, data) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var pwmgr = Cc["@mozilla.org/login-manager;1"].
getService(Ci.nsILoginManager);
if (topic == "passwordmgr-found-form") {
this.receivedNotification = true;
this.data = data;
// Now fill the form
pwmgr.fillForm(subject);
}
}
};
// Add the observer
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver(TestObserver, "passwordmgr-found-form", false);
function startTest(){
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
// Test that observer is notified & got correct data
is(TestObserver.receivedNotification, true, "Checking observer was notified");
is(TestObserver.data, "noAutofillForms", "Checking observer got correct data");
// Check that form1 was filled
is($_(1, "uname").value, "testuser", "Checking for filled username");
is($_(1, "pword").value, "testpass", "Checking for filled password");
// Reset pref (since we assumed it was true to start)
prefs.setBoolPref("autofillForms", true);
// Remove the observer
os.removeObserver(TestObserver, "passwordmgr-found-form");
SimpleTest.finish();
}
window.onload = startTest;
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>