Bug 376668 - Make login fields show autocomplete on focus. r=mattn
authorDale Harvey <dale@arandomurl.com>
Mon, 21 Nov 2016 15:20:44 +0000
changeset 323708 ace6ae91b3760d36e66e9d831f30b4fba73c0c7a
parent 323707 c672332a484db01b65449af076039221c777c409
child 323709 bd8fc8fe4a9de47628c22ae1ed87cf9c9029acf8
push id21
push usermaklebus@msu.edu
push dateThu, 01 Dec 2016 06:22:08 +0000
reviewersmattn
bugs376668
milestone53.0a1
Bug 376668 - Make login fields show autocomplete on focus. r=mattn
toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
toolkit/components/satchel/nsFormFillController.cpp
toolkit/components/satchel/nsFormFillController.h
--- a/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
@@ -821,12 +821,36 @@ add_task(function* test_form12_formless(
   // Trigger autocomplete
   doKey("down");
   checkACForm("", ""); // value shouldn't update
   let processedPromise = promiseFormsProcessed();
   doKey("return"); // not "enter"!
   yield processedPromise;
   checkACForm("testuser", "testpass");
 });
+
+add_task(function* test_form12_open_on_trusted_focus() {
+  uname = $_(12, "uname");
+  pword = $_(12, "pword");
+  uname.value = "";
+  pword.value = "";
+
+  checkACForm("", "");
+  const firePrivEventPromise = new Promise((resolve) => {
+    uname.addEventListener("click", (e) => {
+      ok(e.isTrusted, "Ensure event is trusted");
+      resolve();
+    });
+  });
+  const shownPromise = promiseACShown();
+  synthesizeMouseAtCenter(uname, {});
+  yield firePrivEventPromise;
+  yield shownPromise;
+  doKey("down");
+  const processedPromise = promiseFormsProcessed();
+  doKey("return"); // not "enter"!
+  yield processedPromise;
+  checkACForm("testuser", "testpass");
+});
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -64,16 +64,17 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFormF
 
 nsFormFillController::nsFormFillController() :
   mFocusedInput(nullptr),
   mFocusedInputNode(nullptr),
   mListNode(nullptr),
   mTimeout(50),
   mMinResultsForPopup(1),
   mMaxRows(0),
+  mContextMenuFiredBeforeFocus(false),
   mDisableAutoComplete(false),
   mCompleteDefaultIndex(false),
   mCompleteSelectedIndex(false),
   mForceComplete(false),
   mSuppressOnInput(false)
 {
   mController = do_GetService("@mozilla.org/autocomplete/controller;1");
   MOZ_ASSERT(mController);
@@ -852,16 +853,17 @@ nsFormFillController::HandleEvent(nsIDOM
   }
   if (type.EqualsLiteral("compositionend")) {
     NS_ASSERTION(mController, "should have a controller!");
     if (mController && mFocusedInput)
       mController->HandleEndComposition();
     return NS_OK;
   }
   if (type.EqualsLiteral("contextmenu")) {
+    mContextMenuFiredBeforeFocus = true;
     if (mFocusedPopup)
       mFocusedPopup->ClosePopup();
     return NS_OK;
   }
   if (type.EqualsLiteral("pagehide")) {
 
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(
       aEvent->InternalDOMEvent()->GetTarget());
@@ -927,16 +929,24 @@ nsFormFillController::MaybeStartControll
 }
 
 nsresult
 nsFormFillController::Focus(nsIDOMEvent* aEvent)
 {
   nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(
     aEvent->InternalDOMEvent()->GetTarget());
   MaybeStartControllingInput(input);
+
+  // If this focus doesn't immediately follow a contextmenu event then show
+  // the autocomplete popup
+  if (!mContextMenuFiredBeforeFocus && mPwmgrInputs.Get(mFocusedInputNode)) {
+    ShowPopup();
+  }
+
+  mContextMenuFiredBeforeFocus = false;
   return NS_OK;
 }
 
 nsresult
 nsFormFillController::KeyPress(nsIDOMEvent* aEvent)
 {
   NS_ASSERTION(mController, "should have a controller!");
   if (!mFocusedInput || !mController)
@@ -1059,16 +1069,22 @@ nsFormFillController::MouseDown(nsIDOMEv
   if (!targetInput)
     return NS_OK;
 
   int16_t button;
   mouseEvent->GetButton(&button);
   if (button != 0)
     return NS_OK;
 
+  return ShowPopup();
+}
+
+nsresult
+nsFormFillController::ShowPopup()
+{
   bool isOpen = false;
   GetPopupOpen(&isOpen);
   if (isOpen) {
     return SetPopupOpen(false);
   }
 
   nsCOMPtr<nsIAutoCompleteInput> input;
   mController->GetInput(getter_AddRefs(input));
@@ -1222,16 +1238,20 @@ nsFormFillController::StopControllingInp
       do_GetService("@mozilla.org/satchel/form-autocomplete;1", &rv);
     if (formAutoComplete) {
       formAutoComplete->StopControllingInput(mFocusedInput);
     }
 
     mFocusedInputNode = nullptr;
     mFocusedInput = nullptr;
   }
+
+  if (mFocusedPopup) {
+    mFocusedPopup->ClosePopup();
+  }
   mFocusedPopup = nullptr;
 }
 
 nsIDocShell *
 nsFormFillController::GetDocShellForInput(nsIDOMHTMLInputElement *aInput)
 {
   nsCOMPtr<nsINode> node = do_QueryInterface(aInput);
   NS_ENSURE_TRUE(node, nullptr);
--- a/toolkit/components/satchel/nsFormFillController.h
+++ b/toolkit/components/satchel/nsFormFillController.h
@@ -72,16 +72,17 @@ protected:
    */
   void MaybeStartControllingInput(nsIDOMHTMLInputElement* aElement);
 
   nsresult PerformInputListAutoComplete(const nsAString& aSearch,
                                         nsIAutoCompleteResult** aResult);
 
   void RevalidateDataList();
   bool RowMatch(nsFormHistory *aHistory, uint32_t aIndex, const nsAString &aInputName, const nsAString &aInputValue);
+  nsresult ShowPopup();
 
   inline nsIDocShell *GetDocShellForInput(nsIDOMHTMLInputElement *aInput);
   inline nsPIDOMWindowOuter *GetWindowForDocShell(nsIDocShell *aDocShell);
   inline int32_t GetIndexOfDocShell(nsIDocShell *aDocShell);
 
   void MaybeRemoveMutationObserver(nsINode* aNode);
 
   void RemoveForDocument(nsIDocument* aDoc);
@@ -109,16 +110,17 @@ protected:
   nsCOMPtr<nsIFormAutoComplete> mLastFormAutoComplete;
   nsString mLastSearchString;
 
   nsDataHashtable<nsPtrHashKey<const nsINode>, bool> mPwmgrInputs;
 
   uint32_t mTimeout;
   uint32_t mMinResultsForPopup;
   uint32_t mMaxRows;
+  bool mContextMenuFiredBeforeFocus;
   bool mDisableAutoComplete;
   bool mCompleteDefaultIndex;
   bool mCompleteSelectedIndex;
   bool mForceComplete;
   bool mSuppressOnInput;
 };
 
 #endif // __nsFormFillController__