зеркало из https://github.com/mozilla/pjs.git
Bug 58724 - should autofill password while page is loading, not onload. Patch by Michael Wu <michael.wu@mozilla.com>, r=enndeakin
This commit is contained in:
Родитель
3ebd809b3a
Коммит
eee4f86360
|
@ -76,6 +76,7 @@
|
|||
static const char kPMPropertiesURL[] = "chrome://passwordmgr/locale/passwordmgr.properties";
|
||||
static PRBool sRememberPasswords = PR_FALSE;
|
||||
static PRBool sPrefsInitialized = PR_FALSE;
|
||||
static PRBool sPasswordsLoaded = PR_FALSE;
|
||||
|
||||
static nsIStringBundle* sPMBundle;
|
||||
static nsISecretDecoderRing* sDecoderRing;
|
||||
|
@ -237,6 +238,11 @@ nsPasswordManager::GetInstance()
|
|||
}
|
||||
}
|
||||
|
||||
// We fail to load passwords during early initialization
|
||||
// This wrapper function allows us to handle that error and defer
|
||||
// password loading until later
|
||||
sPasswordManager->LoadPasswords();
|
||||
|
||||
NS_ADDREF(sPasswordManager); // addref the return result
|
||||
return sPasswordManager;
|
||||
}
|
||||
|
@ -278,20 +284,6 @@ nsPasswordManager::Init()
|
|||
NS_ASSERTION(progress, "No web progress service");
|
||||
|
||||
progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
|
||||
|
||||
// Now read in the signon file
|
||||
nsXPIDLCString signonFile;
|
||||
mPrefBranch->GetCharPref("SignonFileName", getter_Copies(signonFile));
|
||||
NS_ASSERTION(!signonFile.IsEmpty(), "Fallback for signon filename not present");
|
||||
|
||||
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSignonFile));
|
||||
mSignonFile->AppendNative(signonFile);
|
||||
|
||||
nsCAutoString path;
|
||||
mSignonFile->GetNativePath(path);
|
||||
|
||||
ReadPasswords(mSignonFile);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -329,6 +321,13 @@ nsPasswordManager::Register(nsIComponentManager* aCompMgr,
|
|||
PR_TRUE,
|
||||
getter_Copies(prevEntry));
|
||||
|
||||
catman->AddCategoryEntry("app-startup",
|
||||
"Password Manager",
|
||||
NS_PASSWORDMANAGER_CONTRACTID,
|
||||
PR_TRUE,
|
||||
PR_TRUE,
|
||||
getter_Copies(prevEntry));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -765,7 +764,8 @@ nsPasswordManager::Observe(nsISupports* aSubject,
|
|||
NS_ASSERTION(branch == mPrefBranch, "unexpected pref change notification");
|
||||
|
||||
branch->GetBoolPref("rememberSignons", &sRememberPasswords);
|
||||
}
|
||||
} else if (!strcmp(aTopic, "app-startup"))
|
||||
GetInstance();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -777,10 +777,8 @@ nsPasswordManager::OnStateChange(nsIWebProgress* aWebProgress,
|
|||
PRUint32 aStateFlags,
|
||||
nsresult aStatus)
|
||||
{
|
||||
// We're only interested in successful document loads.
|
||||
if (NS_FAILED(aStatus) ||
|
||||
!(aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT) ||
|
||||
!(aStateFlags & nsIWebProgressListener::STATE_STOP))
|
||||
if (!(aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT) ||
|
||||
!(aStateFlags & nsIWebProgressListener::STATE_TRANSFERRING))
|
||||
return NS_OK;
|
||||
|
||||
// Don't do anything if the global signon pref is disabled
|
||||
|
@ -791,185 +789,12 @@ nsPasswordManager::OnStateChange(nsIWebProgress* aWebProgress,
|
|||
nsresult rv = aWebProgress->GetDOMWindow(getter_AddRefs(domWin));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||
domWin->GetDocument(getter_AddRefs(domDoc));
|
||||
NS_ASSERTION(domDoc, "DOM window should always have a document!");
|
||||
nsCOMPtr<nsIDOMEventTarget> targ = do_QueryInterface(domWin);
|
||||
|
||||
// For now, only prefill forms in HTML documents.
|
||||
nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(domDoc);
|
||||
if (!htmlDoc)
|
||||
return NS_OK;
|
||||
nsIDOMEventListener* listener = NS_STATIC_CAST(nsIDOMFocusListener*, this);
|
||||
if (aStateFlags & nsIWebProgressListener::STATE_TRANSFERRING && NS_SUCCEEDED(aStatus))
|
||||
targ->AddEventListener(NS_LITERAL_STRING("DOMContentLoaded"), listener, PR_TRUE);
|
||||
|
||||
nsCOMPtr<nsIDOMHTMLCollection> forms;
|
||||
htmlDoc->GetForms(getter_AddRefs(forms));
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
|
||||
|
||||
nsCAutoString realm;
|
||||
if (!GetPasswordRealm(doc->GetDocumentURI(), realm))
|
||||
return NS_OK;
|
||||
|
||||
SignonHashEntry* hashEnt;
|
||||
if (!mSignonTable.Get(realm, &hashEnt))
|
||||
return NS_OK;
|
||||
|
||||
PRUint32 formCount;
|
||||
forms->GetLength(&formCount);
|
||||
|
||||
// We can auto-prefill the username and password if there is only
|
||||
// one stored login that matches the username and password field names
|
||||
// on the form in question. Note that we only need to worry about a
|
||||
// single login per form.
|
||||
|
||||
for (PRUint32 i = 0; i < formCount; ++i) {
|
||||
nsCOMPtr<nsIDOMNode> formNode;
|
||||
forms->Item(i, getter_AddRefs(formNode));
|
||||
|
||||
nsCOMPtr<nsIForm> form = do_QueryInterface(formNode);
|
||||
SignonDataEntry* firstMatch = nsnull;
|
||||
PRBool attachedToInput = PR_FALSE;
|
||||
PRBool prefilledUser = PR_FALSE;
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> userField, passField;
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> temp;
|
||||
nsAutoString fieldType;
|
||||
|
||||
for (SignonDataEntry* e = hashEnt->head; e; e = e->next) {
|
||||
|
||||
nsCOMPtr<nsISupports> foundNode;
|
||||
if (!(e->userField).IsEmpty()) {
|
||||
form->ResolveName(e->userField, getter_AddRefs(foundNode));
|
||||
temp = do_QueryInterface(foundNode);
|
||||
}
|
||||
|
||||
nsAutoString oldUserValue;
|
||||
|
||||
if (temp) {
|
||||
temp->GetType(fieldType);
|
||||
if (!fieldType.Equals(NS_LITERAL_STRING("text")))
|
||||
continue;
|
||||
|
||||
temp->GetValue(oldUserValue);
|
||||
userField = temp;
|
||||
}
|
||||
|
||||
if (!(e->passField).IsEmpty()) {
|
||||
form->ResolveName(e->passField, getter_AddRefs(foundNode));
|
||||
temp = do_QueryInterface(foundNode);
|
||||
}
|
||||
else if (userField) {
|
||||
// No password field name was supplied, try to locate one in the form,
|
||||
// but only if we have a username field.
|
||||
nsCOMPtr<nsIFormControl> fc(do_QueryInterface(foundNode));
|
||||
PRInt32 index = -1;
|
||||
form->IndexOfControl(fc, &index);
|
||||
if (index >= 0) {
|
||||
PRUint32 count;
|
||||
form->GetElementCount(&count);
|
||||
|
||||
PRUint32 i;
|
||||
temp = nsnull;
|
||||
|
||||
// Search forwards
|
||||
nsCOMPtr<nsIFormControl> passField;
|
||||
for (i = index + 1; i < count; ++i) {
|
||||
form->GetElementAt(i, getter_AddRefs(passField));
|
||||
|
||||
if (passField && passField->GetType() == NS_FORM_INPUT_PASSWORD) {
|
||||
foundNode = passField;
|
||||
temp = do_QueryInterface(foundNode);
|
||||
}
|
||||
}
|
||||
|
||||
if (!temp && index != 0) {
|
||||
// Search backwards
|
||||
i = index;
|
||||
do {
|
||||
form->GetElementAt(i, getter_AddRefs(passField));
|
||||
|
||||
if (passField && passField->GetType() == NS_FORM_INPUT_PASSWORD) {
|
||||
foundNode = passField;
|
||||
temp = do_QueryInterface(foundNode);
|
||||
}
|
||||
} while (i-- != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString oldPassValue;
|
||||
|
||||
if (temp) {
|
||||
temp->GetType(fieldType);
|
||||
if (!fieldType.Equals(NS_LITERAL_STRING("password")))
|
||||
continue;
|
||||
|
||||
temp->GetValue(oldPassValue);
|
||||
passField = temp;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!oldUserValue.IsEmpty()) {
|
||||
// The page has prefilled a username.
|
||||
// If it matches any of our saved usernames, prefill the password
|
||||
// for that username. If there are multiple saved usernames,
|
||||
// we will also attach the autocomplete listener.
|
||||
|
||||
prefilledUser = PR_TRUE;
|
||||
nsAutoString userValue;
|
||||
if (NS_FAILED(DecryptData(e->userValue, userValue)))
|
||||
goto done;
|
||||
|
||||
if (userValue.Equals(oldUserValue)) {
|
||||
nsAutoString passValue;
|
||||
if (NS_FAILED(DecryptData(e->passValue, passValue)))
|
||||
goto done;
|
||||
|
||||
passField->SetValue(passValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (firstMatch && userField && !attachedToInput) {
|
||||
// We've found more than one possible signon for this form.
|
||||
|
||||
// Listen for blur and autocomplete events on the username field so
|
||||
// that we can attempt to prefill the password after the user has
|
||||
// entered the username.
|
||||
|
||||
AttachToInput(userField);
|
||||
attachedToInput = PR_TRUE;
|
||||
} else {
|
||||
firstMatch = e;
|
||||
}
|
||||
}
|
||||
|
||||
// If we found more than one match, attachedToInput will be true,
|
||||
// but if we found just one, we need to attach the autocomplete listener,
|
||||
// and fill in the username and password only if the HTML didn't prefill
|
||||
// the username.
|
||||
if (firstMatch && !attachedToInput) {
|
||||
if (userField)
|
||||
AttachToInput(userField);
|
||||
|
||||
if (!prefilledUser){
|
||||
nsAutoString buffer;
|
||||
|
||||
if (userField) {
|
||||
if (NS_FAILED(DecryptData(firstMatch->userValue, buffer)))
|
||||
goto done;
|
||||
|
||||
userField->SetValue(buffer);
|
||||
}
|
||||
|
||||
if (NS_FAILED(DecryptData(firstMatch->passValue, buffer)))
|
||||
goto done;
|
||||
|
||||
passField->SetValue(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
nsCOMPtr<nsIDOMEventTarget> targ = do_QueryInterface(domDoc);
|
||||
targ->AddEventListener(NS_LITERAL_STRING("unload"),
|
||||
NS_STATIC_CAST(nsIDOMLoadListener*, this), PR_FALSE);
|
||||
|
||||
|
@ -1366,7 +1191,190 @@ nsPasswordManager::Blur(nsIDOMEvent* aEvent)
|
|||
NS_IMETHODIMP
|
||||
nsPasswordManager::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
return FillPassword(aEvent);
|
||||
nsAutoString type;
|
||||
aEvent->GetType(type);
|
||||
|
||||
if (!type.EqualsLiteral("DOMContentLoaded"))
|
||||
return FillPassword(aEvent);
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
aEvent->GetTarget(getter_AddRefs(target));
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(target);
|
||||
// For now, only prefill forms in HTML documents.
|
||||
nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(domDoc);
|
||||
if (!htmlDoc)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIDOMHTMLCollection> forms;
|
||||
htmlDoc->GetForms(getter_AddRefs(forms));
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
|
||||
|
||||
nsCAutoString realm;
|
||||
if (!GetPasswordRealm(doc->GetDocumentURI(), realm))
|
||||
return NS_OK;
|
||||
|
||||
SignonHashEntry* hashEnt;
|
||||
if (!mSignonTable.Get(realm, &hashEnt))
|
||||
return NS_OK;
|
||||
|
||||
PRUint32 formCount;
|
||||
forms->GetLength(&formCount);
|
||||
|
||||
// We can auto-prefill the username and password if there is only
|
||||
// one stored login that matches the username and password field names
|
||||
// on the form in question. Note that we only need to worry about a
|
||||
// single login per form.
|
||||
|
||||
for (PRUint32 i = 0; i < formCount; ++i) {
|
||||
nsCOMPtr<nsIDOMNode> formNode;
|
||||
forms->Item(i, getter_AddRefs(formNode));
|
||||
|
||||
nsCOMPtr<nsIForm> form = do_QueryInterface(formNode);
|
||||
SignonDataEntry* firstMatch = nsnull;
|
||||
PRBool attachedToInput = PR_FALSE;
|
||||
PRBool prefilledUser = PR_FALSE;
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> userField, passField;
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> temp;
|
||||
nsAutoString fieldType;
|
||||
|
||||
for (SignonDataEntry* e = hashEnt->head; e; e = e->next) {
|
||||
|
||||
nsCOMPtr<nsISupports> foundNode;
|
||||
if (!(e->userField).IsEmpty()) {
|
||||
form->ResolveName(e->userField, getter_AddRefs(foundNode));
|
||||
temp = do_QueryInterface(foundNode);
|
||||
}
|
||||
|
||||
nsAutoString oldUserValue;
|
||||
|
||||
if (temp) {
|
||||
temp->GetType(fieldType);
|
||||
if (!fieldType.Equals(NS_LITERAL_STRING("text")))
|
||||
continue;
|
||||
|
||||
temp->GetValue(oldUserValue);
|
||||
userField = temp;
|
||||
}
|
||||
|
||||
if (!(e->passField).IsEmpty()) {
|
||||
form->ResolveName(e->passField, getter_AddRefs(foundNode));
|
||||
temp = do_QueryInterface(foundNode);
|
||||
}
|
||||
else if (userField) {
|
||||
// No password field name was supplied, try to locate one in the form,
|
||||
// but only if we have a username field.
|
||||
nsCOMPtr<nsIFormControl> fc(do_QueryInterface(foundNode));
|
||||
PRInt32 index = -1;
|
||||
form->IndexOfControl(fc, &index);
|
||||
if (index >= 0) {
|
||||
PRUint32 count;
|
||||
form->GetElementCount(&count);
|
||||
|
||||
PRUint32 i;
|
||||
temp = nsnull;
|
||||
|
||||
// Search forwards
|
||||
nsCOMPtr<nsIFormControl> passField;
|
||||
for (i = index + 1; i < count; ++i) {
|
||||
form->GetElementAt(i, getter_AddRefs(passField));
|
||||
|
||||
if (passField && passField->GetType() == NS_FORM_INPUT_PASSWORD) {
|
||||
foundNode = passField;
|
||||
temp = do_QueryInterface(foundNode);
|
||||
}
|
||||
}
|
||||
|
||||
if (!temp && index != 0) {
|
||||
// Search backwards
|
||||
i = index;
|
||||
do {
|
||||
form->GetElementAt(i, getter_AddRefs(passField));
|
||||
|
||||
if (passField && passField->GetType() == NS_FORM_INPUT_PASSWORD) {
|
||||
foundNode = passField;
|
||||
temp = do_QueryInterface(foundNode);
|
||||
}
|
||||
} while (i-- != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString oldPassValue;
|
||||
|
||||
if (temp) {
|
||||
temp->GetType(fieldType);
|
||||
if (!fieldType.Equals(NS_LITERAL_STRING("password")))
|
||||
continue;
|
||||
|
||||
temp->GetValue(oldPassValue);
|
||||
passField = temp;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!oldUserValue.IsEmpty()) {
|
||||
// The page has prefilled a username.
|
||||
// If it matches any of our saved usernames, prefill the password
|
||||
// for that username. If there are multiple saved usernames,
|
||||
// we will also attach the autocomplete listener.
|
||||
|
||||
prefilledUser = PR_TRUE;
|
||||
nsAutoString userValue;
|
||||
if (NS_FAILED(DecryptData(e->userValue, userValue)))
|
||||
return NS_OK;
|
||||
|
||||
if (userValue.Equals(oldUserValue)) {
|
||||
nsAutoString passValue;
|
||||
if (NS_FAILED(DecryptData(e->passValue, passValue)))
|
||||
return NS_OK;
|
||||
|
||||
passField->SetValue(passValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (firstMatch && userField && !attachedToInput) {
|
||||
// We've found more than one possible signon for this form.
|
||||
|
||||
// Listen for blur and autocomplete events on the username field so
|
||||
// that we can attempt to prefill the password after the user has
|
||||
// entered the username.
|
||||
|
||||
AttachToInput(userField);
|
||||
attachedToInput = PR_TRUE;
|
||||
} else {
|
||||
firstMatch = e;
|
||||
}
|
||||
}
|
||||
|
||||
// If we found more than one match, attachedToInput will be true,
|
||||
// but if we found just one, we need to attach the autocomplete listener,
|
||||
// and fill in the username and password only if the HTML didn't prefill
|
||||
// the username.
|
||||
if (firstMatch && !attachedToInput) {
|
||||
if (userField)
|
||||
AttachToInput(userField);
|
||||
|
||||
if (!prefilledUser){
|
||||
nsAutoString buffer;
|
||||
|
||||
if (userField) {
|
||||
if (NS_FAILED(DecryptData(firstMatch->userValue, buffer)))
|
||||
return NS_OK;
|
||||
|
||||
userField->SetValue(buffer);
|
||||
}
|
||||
|
||||
if (NS_FAILED(DecryptData(firstMatch->passValue, buffer)))
|
||||
return NS_OK;
|
||||
|
||||
passField->SetValue(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Autocomplete implementation
|
||||
|
@ -1610,6 +1618,10 @@ nsPasswordManager::Unload(nsIDOMEvent* aEvent)
|
|||
if (domDoc)
|
||||
mAutoCompleteInputs.Enumerate(RemoveForDOMDocumentEnumerator, domDoc);
|
||||
|
||||
nsIDOMEventListener* listener = NS_STATIC_CAST(nsIDOMFocusListener*, this);
|
||||
if (target)
|
||||
target->RemoveEventListener(NS_LITERAL_STRING("DOMContentLoaded"), listener, PR_TRUE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1704,6 +1716,30 @@ nsPasswordManager::WriteSignonEntryEnumerator(const nsACString& aKey,
|
|||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// Wrapper function for ReadPasswords
|
||||
void
|
||||
nsPasswordManager::LoadPasswords()
|
||||
{
|
||||
if (sPasswordsLoaded)
|
||||
return;
|
||||
|
||||
nsXPIDLCString signonFile;
|
||||
mPrefBranch->GetCharPref("SignonFileName", getter_Copies(signonFile));
|
||||
NS_ASSERTION(!signonFile.IsEmpty(), "Fallback for signon filename not present");
|
||||
|
||||
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSignonFile));
|
||||
if (!mSignonFile)
|
||||
return;
|
||||
|
||||
mSignonFile->AppendNative(signonFile);
|
||||
|
||||
nsCAutoString path;
|
||||
mSignonFile->GetNativePath(path);
|
||||
|
||||
if (NS_SUCCEEDED(ReadPasswords(mSignonFile)));
|
||||
sPasswordsLoaded = PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsPasswordManager::WritePasswords(nsIFile* aPasswordFile)
|
||||
{
|
||||
|
|
|
@ -144,6 +144,7 @@ public:
|
|||
nsIAutoCompleteResult** aResult);
|
||||
|
||||
protected:
|
||||
void LoadPasswords();
|
||||
void WritePasswords(nsIFile* aPasswordFile);
|
||||
void AddSignonData(const nsACString& aRealm, SignonDataEntry* aEntry);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче