--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -75,33 +75,57 @@
NS_IMPL_ISUPPORTS5(nsFormFillController,
nsIFormFillController,
nsIAutoCompleteInput,
nsIAutoCompleteSearch,
nsIDOMEventListener,
nsIMutationObserver)
nsFormFillController::nsFormFillController() :
+ mFocusedInput(nsnull),
+ mFocusedInputNode(nsnull),
+ mListNode(nsnull),
mTimeout(50),
mMinResultsForPopup(1),
mMaxRows(0),
mDisableAutoComplete(false),
mCompleteDefaultIndex(false),
mCompleteSelectedIndex(false),
mForceComplete(false),
mSuppressOnInput(false)
{
mController = do_GetService("@mozilla.org/autocomplete/controller;1");
mDocShells = do_CreateInstance("@mozilla.org/supports-array;1");
mPopups = do_CreateInstance("@mozilla.org/supports-array;1");
mPwmgrInputs.Init();
}
+struct PwmgrInputsEnumData
+{
+ PwmgrInputsEnumData(nsFormFillController* aFFC, nsIDocument* aDoc)
+ : mFFC(aFFC), mDoc(aDoc) {}
+
+ nsFormFillController* mFFC;
+ nsCOMPtr<nsIDocument> mDoc;
+};
+
nsFormFillController::~nsFormFillController()
{
+ if (mListNode) {
+ mListNode->RemoveMutationObserver(this);
+ mListNode = nsnull;
+ }
+ if (mFocusedInputNode) {
+ MaybeRemoveMutationObserver(mFocusedInputNode);
+ mFocusedInputNode = nsnull;
+ mFocusedInput = nsnull;
+ }
+ PwmgrInputsEnumData ed(this, nsnull);
+ mPwmgrInputs.Enumerate(RemoveForDocumentEnumerator, &ed);
+
// Remove ourselves as a focus listener from all cached docShells
PRUint32 count;
mDocShells->Count(&count);
for (PRUint32 i = 0; i < count; ++i) {
nsCOMPtr<nsIDocShell> docShell;
mDocShells->GetElementAt(i, getter_AddRefs(docShell));
nsCOMPtr<nsIDOMWindow> domWindow = GetWindowForDocShell(docShell);
RemoveWindowListeners(domWindow);
@@ -113,45 +137,53 @@ nsFormFillController::~nsFormFillControl
//
void
nsFormFillController::AttributeChanged(nsIDocument* aDocument,
mozilla::dom::Element* aElement,
PRInt32 aNameSpaceID,
nsIAtom* aAttribute, PRInt32 aModType)
{
- RevalidateDataList();
+ if (mListNode && mListNode->Contains(aElement)) {
+ RevalidateDataList();
+ }
}
void
nsFormFillController::ContentAppended(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer)
{
- RevalidateDataList();
+ if (mListNode && mListNode->Contains(aContainer)) {
+ RevalidateDataList();
+ }
}
void
nsFormFillController::ContentInserted(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer)
{
- RevalidateDataList();
+ if (mListNode && mListNode->Contains(aContainer)) {
+ RevalidateDataList();
+ }
}
void
nsFormFillController::ContentRemoved(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer,
nsIContent* aPreviousSibling)
{
- RevalidateDataList();
+ if (mListNode && mListNode->Contains(aContainer)) {
+ RevalidateDataList();
+ }
}
void
nsFormFillController::CharacterDataWillChange(nsIDocument* aDocument,
nsIContent* aContent,
CharacterDataChangeInfo* aInfo)
{
}
@@ -174,16 +206,35 @@ nsFormFillController::AttributeWillChang
void
nsFormFillController::ParentChainChanged(nsIContent* aContent)
{
}
void
nsFormFillController::NodeWillBeDestroyed(const nsINode* aNode)
{
+ mPwmgrInputs.Remove(aNode);
+ if (aNode == mListNode) {
+ mListNode = nsnull;
+ RevalidateDataList();
+ } else if (aNode == mFocusedInputNode) {
+ mFocusedInputNode = nsnull;
+ mFocusedInput = nsnull;
+ }
+}
+
+void
+nsFormFillController::MaybeRemoveMutationObserver(nsINode* aNode)
+{
+ // Nodes being tracked in mPwmgrInputs will have their observers removed when
+ // they stop being tracked.
+ bool dummy;
+ if (!mPwmgrInputs.Get(aNode, &dummy)) {
+ aNode->RemoveMutationObserver(this);
+ }
}
////////////////////////////////////////////////////////////////////////
//// nsIFormFillController
NS_IMETHODIMP
nsFormFillController::AttachToBrowser(nsIDocShell *aDocShell, nsIAutoCompletePopup *aPopup)
{
@@ -223,17 +274,20 @@ nsFormFillController::MarkAsLoginManager
{
/*
* The Login Manager can supply autocomplete results for username fields,
* when a user has multiple logins stored for a site. It uses this
* interface to indicate that the form manager shouldn't handle the
* autocomplete. The form manager also checks for this tag when saving
* form history (so it doesn't save usernames).
*/
- mPwmgrInputs.Put(aInput, 1);
+ nsCOMPtr<nsINode> node = do_QueryInterface(aInput);
+ NS_ENSURE_STATE(node);
+ mPwmgrInputs.Put(node, true);
+ node->AddMutationObserverUnlessExists(this);
if (!mLoginManager)
mLoginManager = do_GetService("@mozilla.org/login-manager;1");
return NS_OK;
}
@@ -558,18 +612,18 @@ NS_IMETHODIMP
nsFormFillController::StartSearch(const nsAString &aSearchString, const nsAString &aSearchParam,
nsIAutoCompleteResult *aPreviousResult, nsIAutoCompleteObserver *aListener)
{
nsresult rv;
nsCOMPtr<nsIAutoCompleteResult> result;
// If the login manager has indicated it's responsible for this field, let it
// handle the autocomplete. Otherwise, handle with form history.
- PRInt32 dummy;
- if (mPwmgrInputs.Get(mFocusedInput, &dummy)) {
+ bool dummy;
+ if (mPwmgrInputs.Get(mFocusedInputNode, &dummy)) {
// XXX aPreviousResult shouldn't ever be a historyResult type, since we're not letting
// satchel manage the field?
rv = mLoginManager->AutoCompleteSearch(aSearchString,
aPreviousResult,
mFocusedInput,
getter_AddRefs(result));
} else {
nsCOMPtr<nsIAutoCompleteResult> formHistoryResult;
@@ -600,18 +654,25 @@ nsFormFillController::StartSearch(const
mFocusedInput,
getter_AddRefs(result));
if (mFocusedInput) {
nsCOMPtr<nsIDOMHTMLElement> list;
mFocusedInput->GetList(getter_AddRefs(list));
nsCOMPtr<nsINode> node = do_QueryInterface(list);
- if(node) {
- node->AddMutationObserverUnlessExists(this);
+ if (mListNode != node) {
+ if (mListNode) {
+ mListNode->RemoveMutationObserver(this);
+ mListNode = nsnull;
+ }
+ if (node) {
+ node->AddMutationObserverUnlessExists(this);
+ mListNode = node;
+ }
}
}
}
NS_ENSURE_SUCCESS(rv, rv);
aListener->OnSearchResult(this, result);
return NS_OK;
@@ -638,16 +699,19 @@ public:
private:
nsCOMPtr<nsIAutoCompleteObserver> mObserver;
nsCOMPtr<nsIAutoCompleteSearch> mSearch;
nsCOMPtr<nsIAutoCompleteResult> mResult;
};
void nsFormFillController::RevalidateDataList()
{
+ if (!mLastListener) {
+ return;
+ }
nsresult rv;
nsCOMPtr <nsIInputListAutoComplete> inputListAutoComplete =
do_GetService("@mozilla.org/satchel/inputlist-autocomplete;1", &rv);
nsCOMPtr<nsIAutoCompleteResult> result;
rv = inputListAutoComplete->AutoCompleteSearch(mLastSearchResult,
mLastSearchString,
@@ -719,57 +783,61 @@ nsFormFillController::HandleEvent(nsIDOM
if (mFocusedInput) {
nsCOMPtr<nsIDOMDocument> inputDoc;
mFocusedInput->GetOwnerDocument(getter_AddRefs(inputDoc));
if (domDoc == inputDoc)
StopControllingInput();
}
- mPwmgrInputs.Enumerate(RemoveForDOMDocumentEnumerator, domDoc);
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+ PwmgrInputsEnumData ed(this, doc);
+ mPwmgrInputs.Enumerate(RemoveForDocumentEnumerator, &ed);
}
return NS_OK;
}
/* static */ PLDHashOperator
-nsFormFillController::RemoveForDOMDocumentEnumerator(nsISupports* aKey,
- PRInt32& aEntry,
+nsFormFillController::RemoveForDocumentEnumerator(const nsINode* aKey,
+ bool& aEntry,
void* aUserData)
{
- nsIDOMDocument* domDoc = static_cast<nsIDOMDocument*>(aUserData);
- nsCOMPtr<nsIDOMHTMLInputElement> element = do_QueryInterface(aKey);
- nsCOMPtr<nsIDOMDocument> elementDoc;
- element->GetOwnerDocument(getter_AddRefs(elementDoc));
- if (elementDoc == domDoc)
+ PwmgrInputsEnumData* ed = static_cast<PwmgrInputsEnumData*>(aUserData);
+ if (aKey && (!ed->mDoc || aKey->OwnerDoc() == ed->mDoc)) {
+ // mFocusedInputNode's observer is tracked separately, don't remove it here.
+ if (aKey != ed->mFFC->mFocusedInputNode) {
+ const_cast<nsINode*>(aKey)->RemoveMutationObserver(ed->mFFC);
+ }
return PL_DHASH_REMOVE;
-
+ }
return PL_DHASH_NEXT;
}
nsresult
nsFormFillController::Focus(nsIDOMEvent* aEvent)
{
nsCOMPtr<nsIDOMEventTarget> target;
aEvent->GetTarget(getter_AddRefs(target));
nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(target);
- if (!input)
+ nsCOMPtr<nsINode> inputNode = do_QueryInterface(input);
+ if (!inputNode)
return NS_OK;
bool isReadOnly = false;
input->GetReadOnly(&isReadOnly);
nsAutoString autocomplete;
input->GetAttribute(NS_LITERAL_STRING("autocomplete"), autocomplete);
- PRInt32 dummy;
+ bool dummy;
bool isPwmgrInput = false;
- if (mPwmgrInputs.Get(input, &dummy))
+ if (mPwmgrInputs.Get(inputNode, &dummy))
isPwmgrInput = true;
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(input);
if (formControl && formControl->IsSingleLineTextControl(true) &&
!isReadOnly || isPwmgrInput) {
StartControllingInput(input);
}
@@ -961,17 +1029,19 @@ nsFormFillController::RemoveWindowListen
{
if (!aWindow)
return;
StopControllingInput();
nsCOMPtr<nsIDOMDocument> domDoc;
aWindow->GetDocument(getter_AddRefs(domDoc));
- mPwmgrInputs.Enumerate(RemoveForDOMDocumentEnumerator, domDoc);
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+ PwmgrInputsEnumData ed(this, doc);
+ mPwmgrInputs.Enumerate(RemoveForDocumentEnumerator, &ed);
nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(aWindow));
nsIDOMEventTarget* target = nsnull;
if (privateDOMWindow)
target = privateDOMWindow->GetChromeEventHandler();
if (!target)
return;
@@ -1020,47 +1090,62 @@ nsFormFillController::StartControllingIn
nsCOMPtr<nsIDocShell> docShell = GetDocShellForInput(aInput);
PRInt32 index = GetIndexOfDocShell(docShell);
if (index < 0)
return;
// Cache the popup for the focused docShell
mPopups->GetElementAt(index, getter_AddRefs(mFocusedPopup));
+ nsCOMPtr<nsINode> node = do_QueryInterface(aInput);
+ if (!node) {
+ return;
+ }
+
AddKeyListener(aInput);
+
+ node->AddMutationObserverUnlessExists(this);
+ mFocusedInputNode = node;
mFocusedInput = aInput;
+ nsCOMPtr<nsIDOMHTMLElement> list;
+ mFocusedInput->GetList(getter_AddRefs(list));
+ nsCOMPtr<nsINode> listNode = do_QueryInterface(list);
+ if (listNode) {
+ listNode->AddMutationObserverUnlessExists(this);
+ mListNode = listNode;
+ }
+
// Now we are the autocomplete controller's bitch
mController->SetInput(this);
}
void
nsFormFillController::StopControllingInput()
{
RemoveKeyListener();
- if(mFocusedInput) {
- nsCOMPtr<nsIDOMHTMLElement> list;
- mFocusedInput->GetList(getter_AddRefs(list));
-
- nsCOMPtr<nsINode> node = do_QueryInterface(list);
- if (node) {
- node->RemoveMutationObserver(this);
- }
+ if (mListNode) {
+ mListNode->RemoveMutationObserver(this);
+ mListNode = nsnull;
}
// Reset the controller's input, but not if it has been switched
// to another input already, which might happen if the user switches
// focus by clicking another autocomplete textbox
nsCOMPtr<nsIAutoCompleteInput> input;
mController->GetInput(getter_AddRefs(input));
if (input == this)
mController->SetInput(nsnull);
- mFocusedInput = nsnull;
+ if (mFocusedInputNode) {
+ MaybeRemoveMutationObserver(mFocusedInputNode);
+ mFocusedInputNode = nsnull;
+ mFocusedInput = nsnull;
+ }
mFocusedPopup = nsnull;
}
nsIDocShell *
nsFormFillController::GetDocShellForInput(nsIDOMHTMLInputElement *aInput)
{
nsCOMPtr<nsIDOMDocument> domDoc;
aInput->GetOwnerDocument(getter_AddRefs(domDoc));
--- a/toolkit/components/satchel/nsFormFillController.h
+++ b/toolkit/components/satchel/nsFormFillController.h
@@ -56,16 +56,17 @@
#include "nsIMutationObserver.h"
// X.h defines KeyPress
#ifdef KeyPress
#undef KeyPress
#endif
class nsFormHistory;
+class nsINode;
class nsFormFillController : public nsIFormFillController,
public nsIAutoCompleteInput,
public nsIAutoCompleteSearch,
public nsIDOMEventListener,
public nsIMutationObserver
{
public:
@@ -95,37 +96,41 @@ protected:
void RevalidateDataList();
bool RowMatch(nsFormHistory *aHistory, PRUint32 aIndex, const nsAString &aInputName, const nsAString &aInputValue);
inline nsIDocShell *GetDocShellForInput(nsIDOMHTMLInputElement *aInput);
inline nsIDOMWindow *GetWindowForDocShell(nsIDocShell *aDocShell);
inline PRInt32 GetIndexOfDocShell(nsIDocShell *aDocShell);
- static PLDHashOperator RemoveForDOMDocumentEnumerator(nsISupports* aKey,
- PRInt32& aEntry,
- void* aUserData);
+ void MaybeRemoveMutationObserver(nsINode* aNode);
+
+ static PLDHashOperator RemoveForDocumentEnumerator(const nsINode* aKey,
+ bool& aEntry,
+ void* aUserData);
bool IsEventTrusted(nsIDOMEvent *aEvent);
bool IsInputAutoCompleteOff();
// members //////////////////////////////////////////
nsCOMPtr<nsIAutoCompleteController> mController;
nsCOMPtr<nsILoginManager> mLoginManager;
- nsCOMPtr<nsIDOMHTMLInputElement> mFocusedInput;
+ nsIDOMHTMLInputElement* mFocusedInput;
+ nsINode* mFocusedInputNode;
+ nsINode* mListNode;
nsCOMPtr<nsIAutoCompletePopup> mFocusedPopup;
nsCOMPtr<nsISupportsArray> mDocShells;
nsCOMPtr<nsISupportsArray> mPopups;
//these are used to dynamically update the autocomplete
nsCOMPtr<nsIAutoCompleteResult> mLastSearchResult;
nsCOMPtr<nsIAutoCompleteObserver> mLastListener;
nsString mLastSearchString;
- nsDataHashtable<nsISupportsHashKey,PRInt32> mPwmgrInputs;
+ nsDataHashtable<nsPtrHashKey<const nsINode>, bool> mPwmgrInputs;
PRUint32 mTimeout;
PRUint32 mMinResultsForPopup;
PRUint32 mMaxRows;
bool mDisableAutoComplete;
bool mCompleteDefaultIndex;
bool mCompleteSelectedIndex;
bool mForceComplete;