Bug 1110557. r=mak, r=gavin
authorMats Palmgren <mats@mozilla.com>
Sat, 24 Jan 2015 12:37:47 -0500
changeset 225561 7f0738c201ebb013e201a14173edc21aad0bbee4
parent 225560 73ac9581083aeec24ba2508820e339dc452ba5a7
child 225562 1e2e5169edf8202110a15c2c6e88edc0fcc6ef01
push id28167
push userryanvm@gmail.com
push dateSun, 25 Jan 2015 00:24:46 +0000
treeherdermozilla-central@c18776175a69 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak, gavin
bugs1110557
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1110557. r=mak, r=gavin
toolkit/components/satchel/nsFormFillController.cpp
toolkit/components/satchel/nsFormFillController.h
--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -69,16 +69,17 @@ nsFormFillController::nsFormFillControll
   mMaxRows(0),
   mDisableAutoComplete(false),
   mCompleteDefaultIndex(false),
   mCompleteSelectedIndex(false),
   mForceComplete(false),
   mSuppressOnInput(false)
 {
   mController = do_GetService("@mozilla.org/autocomplete/controller;1");
+  MOZ_ASSERT(mController);
 }
 
 struct PwmgrInputsEnumData
 {
   PwmgrInputsEnumData(nsFormFillController* aFFC, nsIDocument* aDoc)
   : mFFC(aFFC), mDoc(aDoc) {}
 
   nsFormFillController* mFFC;
@@ -112,16 +113,31 @@ nsFormFillController::~nsFormFillControl
 //
 
 void
 nsFormFillController::AttributeChanged(nsIDocument* aDocument,
                                        mozilla::dom::Element* aElement,
                                        int32_t aNameSpaceID,
                                        nsIAtom* aAttribute, int32_t aModType)
 {
+  if ((aAttribute == nsGkAtoms::type || aAttribute == nsGkAtoms::readonly ||
+       aAttribute == nsGkAtoms::autocomplete) &&
+      aNameSpaceID == kNameSpaceID_None) {
+    nsCOMPtr<nsIDOMHTMLInputElement> focusedInput(mFocusedInput);
+    // Reset the current state of the controller, unconditionally.
+    StopControllingInput();
+    // Then restart based on the new values.  We have to delay this
+    // to avoid ending up in an endless loop due to re-registering our
+    // mutation observer (which would notify us again for *this* event).
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethodWithArg<nsCOMPtr<nsIDOMHTMLInputElement>>
+      (this, &nsFormFillController::MaybeStartControllingInput, focusedInput);
+    NS_DispatchToCurrentThread(event);
+  }
+
   if (mListNode && mListNode->Contains(aElement)) {
     RevalidateDataList();
   }
 }
 
 void
 nsFormFillController::ContentAppended(nsIDocument* aDocument,
                                       nsIContent* aContainer,
@@ -856,49 +872,54 @@ nsFormFillController::RemoveForDocumentE
     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)
+void
+nsFormFillController::MaybeStartControllingInput(nsIDOMHTMLInputElement* aInput)
 {
-  nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(
-    aEvent->InternalDOMEvent()->GetTarget());
-  nsCOMPtr<nsINode> inputNode = do_QueryInterface(input);
+  nsCOMPtr<nsINode> inputNode = do_QueryInterface(aInput);
   if (!inputNode)
-    return NS_OK;
+    return;
 
-  nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(input);
+  nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(aInput);
   if (!formControl || !formControl->IsSingleLineTextControl(true))
-    return NS_OK;
+    return;
 
   bool isReadOnly = false;
-  input->GetReadOnly(&isReadOnly);
+  aInput->GetReadOnly(&isReadOnly);
   if (isReadOnly)
-    return NS_OK;
+    return;
 
-  bool autocomplete = nsContentUtils::IsAutocompleteEnabled(input);
+  bool autocomplete = nsContentUtils::IsAutocompleteEnabled(aInput);
 
   nsCOMPtr<nsIDOMHTMLElement> datalist;
-  input->GetList(getter_AddRefs(datalist));
+  aInput->GetList(getter_AddRefs(datalist));
   bool hasList = datalist != nullptr;
 
   bool dummy;
   bool isPwmgrInput = false;
   if (mPwmgrInputs.Get(inputNode, &dummy))
       isPwmgrInput = true;
 
   if (isPwmgrInput || hasList || autocomplete) {
-    StartControllingInput(input);
+    StartControllingInput(aInput);
   }
+}
 
+nsresult
+nsFormFillController::Focus(nsIDOMEvent* aEvent)
+{
+  nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(
+    aEvent->InternalDOMEvent()->GetTarget());
+  MaybeStartControllingInput(input);
   return NS_OK;
 }
 
 nsresult
 nsFormFillController::KeyPress(nsIDOMEvent* aEvent)
 {
   NS_ASSERTION(mController, "should have a controller!");
   if (!mFocusedInput || !mController)
@@ -1132,16 +1153,20 @@ nsFormFillController::RemoveKeyListener(
 }
 
 void
 nsFormFillController::StartControllingInput(nsIDOMHTMLInputElement *aInput)
 {
   // Make sure we're not still attached to an input
   StopControllingInput();
 
+  if (!mController) {
+    return;
+  }
+
   // Find the currently focused docShell
   nsCOMPtr<nsIDocShell> docShell = GetDocShellForInput(aInput);
   int32_t index = GetIndexOfDocShell(docShell);
   if (index < 0)
     return;
 
   // Cache the popup for the focused docShell
   mFocusedPopup = mPopups.SafeElementAt(index);
@@ -1174,23 +1199,25 @@ nsFormFillController::StopControllingInp
 {
   RemoveKeyListener();
 
   if (mListNode) {
     mListNode->RemoveMutationObserver(this);
     mListNode = nullptr;
   }
 
-  // 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(nullptr);
+  if (mController) {
+    // 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(nullptr);
+  }
 
   if (mFocusedInputNode) {
     MaybeRemoveMutationObserver(mFocusedInputNode);
     mFocusedInputNode = nullptr;
     mFocusedInput = nullptr;
   }
   mFocusedPopup = nullptr;
 }
--- a/toolkit/components/satchel/nsFormFillController.h
+++ b/toolkit/components/satchel/nsFormFillController.h
@@ -61,16 +61,21 @@ protected:
   void AddWindowListeners(nsIDOMWindow *aWindow);
   void RemoveWindowListeners(nsIDOMWindow *aWindow);
 
   void AddKeyListener(nsINode* aInput);
   void RemoveKeyListener();
 
   void StartControllingInput(nsIDOMHTMLInputElement *aInput);
   void StopControllingInput();
+  /**
+   * Checks that aElement is a type of element we want to fill, then calls
+   * StartControllingInput on it.
+   */
+  void MaybeStartControllingInput(nsIDOMHTMLInputElement* aElement);
 
   nsresult PerformInputListAutoComplete(nsIAutoCompleteResult* aPreviousResult);
 
   void RevalidateDataList();
   bool RowMatch(nsFormHistory *aHistory, uint32_t aIndex, const nsAString &aInputName, const nsAString &aInputValue);
 
   inline nsIDocShell *GetDocShellForInput(nsIDOMHTMLInputElement *aInput);
   inline nsIDOMWindow *GetWindowForDocShell(nsIDocShell *aDocShell);