diff --git a/toolkit/components/passwordmgr/base/Makefile.in b/toolkit/components/passwordmgr/base/Makefile.in index 4966bb6601c3..4f41a9898288 100644 --- a/toolkit/components/passwordmgr/base/Makefile.in +++ b/toolkit/components/passwordmgr/base/Makefile.in @@ -58,6 +58,7 @@ REQUIRES = \ dom \ content \ widget \ + autocomplete \ $(NULL) XPIDLSRCS = nsIPassword.idl diff --git a/toolkit/components/passwordmgr/base/nsPasswordManager.cpp b/toolkit/components/passwordmgr/base/nsPasswordManager.cpp index 3386dd582c5e..ca7cf21ec97e 100644 --- a/toolkit/components/passwordmgr/base/nsPasswordManager.cpp +++ b/toolkit/components/passwordmgr/base/nsPasswordManager.cpp @@ -68,6 +68,7 @@ #include "nsAppDirectoryServiceDefs.h" #include "nsIDOMEventTarget.h" #include "nsIDOMHTMLFormElement.h" +#include "nsIAutoCompleteResult.h" static const char kPMPropertiesURL[] = "chrome://passwordmgr/locale/passwordmgr.properties"; static PRBool sRememberPasswords = PR_FALSE; @@ -75,6 +76,7 @@ static PRBool sPrefsInitialized = PR_FALSE; static nsIStringBundle* sPMBundle; static nsISecretDecoderRing* sDecoderRing; +static nsPasswordManager* sPasswordManager; static void PMLocalizedString(const nsAString& key, nsAString& aResult, PRBool aFormatted = PR_FALSE, @@ -170,6 +172,9 @@ nsPasswordManager::PasswordEntry::GetPassword(nsAString& aPassword) } + + + NS_IMPL_ADDREF(nsPasswordManager) NS_IMPL_RELEASE(nsPasswordManager) @@ -180,7 +185,8 @@ NS_INTERFACE_MAP_BEGIN(nsPasswordManager) NS_INTERFACE_MAP_ENTRY(nsIFormSubmitObserver) NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) NS_INTERFACE_MAP_ENTRY(nsIDOMFocusListener) - NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) + NS_INTERFACE_MAP_ENTRY(nsIDOMLoadListener) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMFocusListener) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPasswordManager) NS_INTERFACE_MAP_END @@ -194,11 +200,32 @@ nsPasswordManager::~nsPasswordManager() } +/* static */ nsPasswordManager* +nsPasswordManager::GetInstance() +{ + if (!sPasswordManager) { + sPasswordManager = new nsPasswordManager(); + if (!sPasswordManager) + return nsnull; + + NS_ADDREF(sPasswordManager); // addref the global + + if (NS_FAILED(sPasswordManager->Init())) { + NS_RELEASE(sPasswordManager); + return nsnull; + } + } + + NS_ADDREF(sPasswordManager); // addref the return result + return sPasswordManager; +} + nsresult nsPasswordManager::Init() { mSignonTable.Init(); mRejectTable.Init(); + mAutoCompleteInputs.Init(); sPrefsInitialized = PR_TRUE; @@ -310,6 +337,7 @@ nsPasswordManager::Shutdown() { NS_IF_RELEASE(sDecoderRing); NS_IF_RELEASE(sPMBundle); + NS_IF_RELEASE(sPasswordManager); } // nsIPasswordManager implementation @@ -618,9 +646,7 @@ nsPasswordManager::OnStateChange(nsIWebProgress* aWebProgress, // that we can attempt to prefill the password after the user has // entered the username. - nsCOMPtr targ = do_QueryInterface(userField); - targ->AddEventListener(NS_LITERAL_STRING("blur"), this, PR_FALSE); - targ->AddEventListener(NS_LITERAL_STRING("DOMAutoComplete"), this, PR_FALSE); + AttachToInput(userField); firstMatch = nsnull; break; // on to the next form } else { @@ -637,12 +663,14 @@ nsPasswordManager::OnStateChange(nsIWebProgress* aWebProgress, DecryptData(firstMatch->passValue, buffer); passField->SetValue(buffer); - nsCOMPtr targ = do_QueryInterface(userField); - targ->AddEventListener(NS_LITERAL_STRING("blur"), this, PR_FALSE); - targ->AddEventListener(NS_LITERAL_STRING("DOMAutoComplete"), this, PR_FALSE); + AttachToInput(userField); } } + nsCOMPtr targ = do_QueryInterface(domDoc); + targ->AddEventListener(NS_LITERAL_STRING("unload"), + NS_STATIC_CAST(nsIDOMLoadListener*, this), PR_FALSE); + return NS_OK; } @@ -968,6 +996,227 @@ nsPasswordManager::HandleEvent(nsIDOMEvent* aEvent) return FillPassword(aEvent); } +// Autocomplete implementation + +class UserAutoComplete : public nsIAutoCompleteResult +{ +public: + UserAutoComplete(const nsAString& aSearchString); + virtual ~UserAutoComplete(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIAUTOCOMPLETERESULT + + nsVoidArray mArray; + nsString mSearchString; + PRInt32 mDefaultIndex; + PRUint16 mResult; +}; + +UserAutoComplete::UserAutoComplete(const nsAString& aSearchString) + : mSearchString(aSearchString), + mDefaultIndex(-1), + mResult(RESULT_FAILURE) +{ +} + +UserAutoComplete::~UserAutoComplete() +{ + for (PRUint32 i = 0; i < mArray.Count(); ++i) + nsMemory::Free(mArray.ElementAt(i)); +} + +NS_IMPL_ISUPPORTS1(UserAutoComplete, nsIAutoCompleteResult) + +NS_IMETHODIMP +UserAutoComplete::GetSearchString(nsAString& aString) +{ + aString.Assign(mSearchString); + return NS_OK; +} + +NS_IMETHODIMP +UserAutoComplete::GetSearchResult(PRUint16* aResult) +{ + *aResult = mResult; + return NS_OK; +} + +NS_IMETHODIMP +UserAutoComplete::GetDefaultIndex(PRInt32* aDefaultIndex) +{ + *aDefaultIndex = mDefaultIndex; + return NS_OK; +} + +NS_IMETHODIMP +UserAutoComplete::GetErrorDescription(nsAString& aDescription) +{ + aDescription.Truncate(); + return NS_OK; +} + +NS_IMETHODIMP +UserAutoComplete::GetMatchCount(PRUint32* aCount) +{ + *aCount = mArray.Count(); + return NS_OK; +} + +NS_IMETHODIMP +UserAutoComplete::GetValueAt(PRInt32 aIndex, nsAString& aValue) +{ + aValue.Assign(NS_STATIC_CAST(PRUnichar*, mArray.ElementAt(aIndex))); + return NS_OK; +} + +NS_IMETHODIMP +UserAutoComplete::GetCommentAt(PRInt32 aIndex, nsAString& aComment) +{ + aComment.Truncate(); + return NS_OK; +} + +NS_IMETHODIMP +UserAutoComplete::GetStyleAt(PRInt32 aIndex, nsAString& aHint) +{ + aHint.Truncate(); + return NS_OK; +} + +PR_STATIC_CALLBACK(int) +SortPRUnicharComparator(const void* aElement1, + const void* aElement2, + void* aData) +{ + return nsCRT::strcmp(NS_STATIC_CAST(const PRUnichar*, aElement1), + NS_STATIC_CAST(const PRUnichar*, aElement2)); +} + +PRBool +nsPasswordManager::AutoCompleteSearch(const nsAString& aSearchString, + nsIAutoCompleteResult* aPreviousResult, + nsIDOMHTMLInputElement* aElement, + nsIAutoCompleteResult** aResult) +{ + PRInt32 dummy; + if (!mAutoCompleteInputs.Get(aElement, &dummy)) + return PR_FALSE; + + UserAutoComplete* result = nsnull; + + if (aPreviousResult) { + + // We have a list of results for a shorter search string, so just + // filter them further based on the new search string. + + result = NS_STATIC_CAST(UserAutoComplete*, aPreviousResult); + + if (result->mArray.Count()) { + for (PRUint32 i = result->mArray.Count(); i > 0; --i) { + nsDependentString match(NS_STATIC_CAST(PRUnichar*, result->mArray.ElementAt(i - 1))); + if (aSearchString.Length() >= match.Length() || + !StringBeginsWith(match, aSearchString)) { + nsMemory::Free(result->mArray.ElementAt(i - 1)); + result->mArray.RemoveElementAt(i - 1); + } + } + } + } else { + + nsCOMPtr domDoc; + aElement->GetOwnerDocument(getter_AddRefs(domDoc)); + + nsCOMPtr doc = do_QueryInterface(domDoc); + nsCOMPtr uri; + doc->GetDocumentURL(getter_AddRefs(uri)); + + nsCAutoString realm; + uri->GetPrePath(realm); + + // Get all of the matches into an array that we can sort. + + result = new UserAutoComplete(aSearchString); + + SignonHashEntry* hashEnt; + if (mSignonTable.Get(realm, &hashEnt)) { + for (SignonDataEntry* e = hashEnt->head; e; e = e->next) { + + nsAutoString userValue; + DecryptData(e->userValue, userValue); + + if (aSearchString.Length() < userValue.Length() && + StringBeginsWith(userValue, aSearchString)) { + result->mArray.AppendElement(ToNewUnicode(userValue)); + } + } + } + + if (result->mArray.Count()) { + result->mArray.Sort(SortPRUnicharComparator, nsnull); + result->mResult = nsIAutoCompleteResult::RESULT_SUCCESS; + result->mDefaultIndex = 0; + } else { + result->mResult = nsIAutoCompleteResult::RESULT_NOMATCH; + result->mDefaultIndex = -1; + } + } + + *aResult = result; + NS_ADDREF(*aResult); + + return PR_TRUE; +} + +// nsIDOMLoadListener implementation + +NS_IMETHODIMP +nsPasswordManager::Load(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + +/* static */ PLDHashOperator PR_CALLBACK +nsPasswordManager::RemoveForDOMDocumentEnumerator(nsISupports* aKey, + PRInt32& aEntry, + void* aUserData) +{ + nsIDOMDocument* domDoc = NS_STATIC_CAST(nsIDOMDocument*, aUserData); + nsCOMPtr element = do_QueryInterface(aKey); + nsCOMPtr elementDoc; + element->GetOwnerDocument(getter_AddRefs(elementDoc)); + if (elementDoc == domDoc) + return PL_DHASH_REMOVE; + + return PL_DHASH_NEXT; +} + +NS_IMETHODIMP +nsPasswordManager::Unload(nsIDOMEvent* aEvent) +{ + nsCOMPtr target; + aEvent->GetTarget(getter_AddRefs(target)); + + nsCOMPtr domDoc = do_QueryInterface(target); + if (domDoc) + mAutoCompleteInputs.Enumerate(RemoveForDOMDocumentEnumerator, domDoc); + + return NS_OK; +} + +NS_IMETHODIMP +nsPasswordManager::Abort(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsPasswordManager::Error(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + + // internal methods /* @@ -1370,6 +1619,18 @@ nsPasswordManager::FillPassword(nsIDOMEvent* aEvent) return NS_OK; } +void +nsPasswordManager::AttachToInput(nsIDOMHTMLInputElement* aElement) +{ + nsCOMPtr targ = do_QueryInterface(aElement); + nsIDOMEventListener* listener = NS_STATIC_CAST(nsIDOMFocusListener*, this); + + targ->AddEventListener(NS_LITERAL_STRING("blur"), listener, PR_FALSE); + targ->AddEventListener(NS_LITERAL_STRING("DOMAutoComplete"), listener, PR_FALSE); + + mAutoCompleteInputs.Put(aElement, 1); +} + ////////////////////////////////////// // nsSingleSignonPrompt @@ -1389,14 +1650,16 @@ nsSingleSignonPrompt::Prompt(const PRUnichar* aDialogTitle, { nsAutoString checkMsg; nsString emptyString; - PRBool checkValue = (aSavePassword == SAVE_PASSWORD_PERMANENTLY); + PRBool checkValue = PR_FALSE; PRBool *checkPtr = nsnull; PRUnichar* value = nsnull; nsCOMPtr mgrInternal; - if (nsPasswordManager::SingleSignonEnabled()) { - PMLocalizedString(NS_LITERAL_STRING("rememberValue"), checkMsg); - checkPtr = &checkValue; + if (nsPasswordManager::SingleSignonEnabled() && aPasswordRealm) { + if (aSavePassword == SAVE_PASSWORD_PERMANENTLY) { + PMLocalizedString(NS_LITERAL_STRING("rememberValue"), checkMsg); + checkPtr = &checkValue; + } mgrInternal = do_GetService(NS_PASSWORDMANAGER_CONTRACTID); nsCAutoString outHost; @@ -1419,20 +1682,25 @@ nsSingleSignonPrompt::Prompt(const PRUnichar* aDialogTitle, checkPtr, aConfirm); - if (*aConfirm && checkValue && value[0] != '\0') { - // The user requested that we save the value - // TODO: support SAVE_PASSWORD_FOR_SESSION + if (*aConfirm) { + if (checkValue && value[0] != '\0') { + // The user requested that we save the value + // TODO: support SAVE_PASSWORD_FOR_SESSION - nsCOMPtr manager = do_QueryInterface(mgrInternal); + nsCOMPtr manager = do_QueryInterface(mgrInternal); - manager->AddUser(NS_ConvertUCS2toUTF8(aPasswordRealm), - nsDependentString(value), - emptyString); + manager->AddUser(NS_ConvertUCS2toUTF8(aPasswordRealm), + nsDependentString(value), + emptyString); + } + + *aResult = value; + } else { + if (value) + nsMemory::Free(value); + *aResult = nsnull; } - if (value) - nsMemory::Free(value); - return NS_OK; } @@ -1447,14 +1715,16 @@ nsSingleSignonPrompt::PromptUsernameAndPassword(const PRUnichar* aDialogTitle, { nsAutoString checkMsg; nsString emptyString; - PRBool checkValue = (aSavePassword == SAVE_PASSWORD_PERMANENTLY); + PRBool checkValue = PR_FALSE; PRBool *checkPtr = nsnull; PRUnichar *user = nsnull, *password = nsnull; nsCOMPtr mgrInternal; - if (nsPasswordManager::SingleSignonEnabled()) { - PMLocalizedString(NS_LITERAL_STRING("rememberPassword"), checkMsg); - checkPtr = &checkValue; + if (nsPasswordManager::SingleSignonEnabled() && aPasswordRealm) { + if (aSavePassword == SAVE_PASSWORD_PERMANENTLY) { + PMLocalizedString(NS_LITERAL_STRING("rememberPassword"), checkMsg); + checkPtr = &checkValue; + } mgrInternal = do_GetService(NS_PASSWORDMANAGER_CONTRACTID); nsCAutoString outHost; @@ -1479,22 +1749,30 @@ nsSingleSignonPrompt::PromptUsernameAndPassword(const PRUnichar* aDialogTitle, checkPtr, aConfirm); - if (*aConfirm && checkValue && user[0] != '\0') { - // The user requested that we save the values - // TODO: support SAVE_PASSWORD_FOR_SESSION + if (*aConfirm) { + if (checkValue && user[0] != '\0') { + // The user requested that we save the values + // TODO: support SAVE_PASSWORD_FOR_SESSION - nsCOMPtr manager = do_QueryInterface(mgrInternal); + nsCOMPtr manager = do_QueryInterface(mgrInternal); - manager->AddUser(NS_ConvertUCS2toUTF8(aPasswordRealm), - nsDependentString(user), - nsDependentString(password)); + manager->AddUser(NS_ConvertUCS2toUTF8(aPasswordRealm), + nsDependentString(user), + nsDependentString(password)); + } + + *aUser = user; + *aPassword = password; + + } else { + if (user) + nsMemory::Free(user); + if (password) + nsMemory::Free(password); + + *aUser = *aPassword = nsnull; } - if (user) - nsMemory::Free(user); - if (password) - nsMemory::Free(password); - return NS_OK; } @@ -1508,14 +1786,16 @@ nsSingleSignonPrompt::PromptPassword(const PRUnichar* aDialogTitle, { nsAutoString checkMsg; nsString emptyString; - PRBool checkValue = (aSavePassword == SAVE_PASSWORD_PERMANENTLY); + PRBool checkValue = PR_FALSE; PRBool *checkPtr = nsnull; PRUnichar* password = nsnull; nsCOMPtr mgrInternal; - if (nsPasswordManager::SingleSignonEnabled()) { - PMLocalizedString(NS_LITERAL_STRING("rememberPassword"), checkMsg); - checkPtr = &checkValue; + if (nsPasswordManager::SingleSignonEnabled() && aPasswordRealm) { + if (aSavePassword == SAVE_PASSWORD_PERMANENTLY) { + PMLocalizedString(NS_LITERAL_STRING("rememberPassword"), checkMsg); + checkPtr = &checkValue; + } mgrInternal = do_GetService(NS_PASSWORDMANAGER_CONTRACTID); nsCAutoString outHost; @@ -1538,20 +1818,26 @@ nsSingleSignonPrompt::PromptPassword(const PRUnichar* aDialogTitle, checkPtr, aConfirm); - if (*aConfirm && checkValue && password[0] != '\0') { - // The user requested that we save the password - // TODO: support SAVE_PASSWORD_FOR_SESSION + if (*aConfirm) { + if (checkValue && password[0] != '\0') { + // The user requested that we save the password + // TODO: support SAVE_PASSWORD_FOR_SESSION - nsCOMPtr manager = do_QueryInterface(mgrInternal); + nsCOMPtr manager = do_QueryInterface(mgrInternal); - manager->AddUser(NS_ConvertUCS2toUTF8(aPasswordRealm), - emptyString, - nsDependentString(password)); + manager->AddUser(NS_ConvertUCS2toUTF8(aPasswordRealm), + emptyString, + nsDependentString(password)); + } + + *aPassword = password; + + } else { + if (password) + nsMemory::Free(password); + *aPassword = nsnull; } - if (password) - nsMemory::Free(password); - return NS_OK; } diff --git a/toolkit/components/passwordmgr/base/nsPasswordManager.h b/toolkit/components/passwordmgr/base/nsPasswordManager.h index 7d0f161d3f42..311f8a4713fc 100644 --- a/toolkit/components/passwordmgr/base/nsPasswordManager.h +++ b/toolkit/components/passwordmgr/base/nsPasswordManager.h @@ -47,6 +47,8 @@ #include "nsIFormSubmitObserver.h" #include "nsIWebProgressListener.h" #include "nsIDOMFocusListener.h" +#include "nsIDOMLoadListener.h" +#include "nsIStringBundle.h" /* 360565c4-2ef3-4f6a-bab9-94cca891b2a7 */ #define NS_PASSWORDMANAGER_CID \ @@ -64,6 +66,8 @@ class nsIComponentManager; class nsIContent; class nsIDOMWindowInternal; class nsIURI; +class nsIDOMHTMLInputElement; +class nsIAutoCompleteResult; struct nsModuleComponentInfo; class nsPasswordManager : public nsIPasswordManager, @@ -72,6 +76,7 @@ class nsPasswordManager : public nsIPasswordManager, public nsIFormSubmitObserver, public nsIWebProgressListener, public nsIDOMFocusListener, + public nsIDOMLoadListener, public nsSupportsWeakReference { public: @@ -82,6 +87,8 @@ public: nsPasswordManager(); virtual ~nsPasswordManager(); + static nsPasswordManager* GetInstance(); + nsresult Init(); static PRBool SingleSignonEnabled(); @@ -125,6 +132,17 @@ public: // nsIDOMEventListener NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent); + // nsIDOMLoadListener + NS_IMETHOD Load(nsIDOMEvent* aEvent); + NS_IMETHOD Unload(nsIDOMEvent* aEvent); + NS_IMETHOD Abort(nsIDOMEvent* aEvent); + NS_IMETHOD Error(nsIDOMEvent* aEvent); + + // Autocomplete + PRBool AutoCompleteSearch(const nsAString& aSearchString, + nsIAutoCompleteResult* aPreviousResult, + nsIDOMHTMLInputElement* aElement, + nsIAutoCompleteResult** aResult); protected: void ReadSignonFile(); @@ -139,6 +157,7 @@ protected: nsresult FillPassword(nsIDOMEvent* aEvent); + void AttachToInput(nsIDOMHTMLInputElement* aElement); static PLDHashOperator PR_CALLBACK FindEntryEnumerator(const nsACString& aKey, SignonHashEntry* aEntry, @@ -160,10 +179,15 @@ protected: PRInt32 aEntry, void* aUserData); + static PLDHashOperator PR_CALLBACK RemoveForDOMDocumentEnumerator(nsISupports* aKey, + PRInt32& aEntry, + void* aUserData); + static void EnsureDecoderRing(); nsClassHashtable mSignonTable; nsDataHashtable mRejectTable; + nsDataHashtable mAutoCompleteInputs; nsCOMPtr mSignonFile; };