Bug 1110557. r=mak, r=gavin, a=abillings
authorMats Palmgren <mats@mozilla.com>
Sat, 24 Jan 2015 12:37:47 -0500
changeset 243038 3f71dcaa9396
parent 243037 12972395700a
child 243039 a532a2852b2f
push id4370
push userryanvm@gmail.com
push date2015-01-26 15:57 +0000
treeherdermozilla-beta@bf8644a5c52a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak, gavin, abillings
bugs1110557
milestone36.0
Bug 1110557. r=mak, r=gavin, a=abillings
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);