Bug 376668 - making login fields show autocomplete on focus r?MattN draft
authorJonathan Kingston <jkt@mozilla.com>
Wed, 21 Sep 2016 16:11:47 +0100
changeset 436897 ce77cdcf01726221734c5608a6548199e50c4f1f
parent 435182 f13e90d496cf1bc6dfc4fd398da33e4afe785bde
child 536475 fd98991d2ded24061f1bd8640dc277eb2f2ae3d0
push id35235
push userjkingston@mozilla.com
push dateThu, 10 Nov 2016 01:06:48 +0000
reviewersMattN
bugs376668
milestone52.0a1
Bug 376668 - making login fields show autocomplete on focus r?MattN MozReview-Commit-ID: Avzp5FaAgHQ
toolkit/components/passwordmgr/test/browser/browser_context_menu.js
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/browser/browser_context_menu.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_context_menu.js
@@ -42,16 +42,38 @@ add_task(function* test_context_menu_pop
     checkMenu(popupMenu, 2);
 
     let contextMenu = document.getElementById("contentAreaContextMenu");
     contextMenu.hidePopup();
   });
 });
 
 /**
+ * Check if clicking the edge doesn't show both autocomplete and context menu
+ */
+add_task(function* test_context_menu_doesnt_overlap_with_autocomplete() {
+  Services.prefs.setBoolPref("signon.schemeUpgrades", false);
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: TEST_HOSTNAME + MULTIPLE_FORMS_PAGE_PATH,
+  }, function* (browser) {
+    let passwordInput = browser.contentWindow.document.getElementById("test-password-1");
+
+    // Synthesize a right mouse click over the password input element.
+    let contextMenuShownPromise = BrowserTestUtils.waitForEvent(window, "popupshown");
+    let eventDetails = {type: "contextmenu", button: 2};
+    BrowserTestUtils.synthesizeMouseAtCenter(passwordInput, eventDetails, browser);
+    yield contextMenuShownPromise;
+
+    let contextMenu = document.getElementById("contentAreaContextMenu");
+    contextMenu.hidePopup();
+  });
+});
+
+/**
  * Check if the context menu is populated with the right
  * menuitems for the target password input field.
  */
 add_task(function* test_context_menu_populate_password_schemeUpgrades() {
   Services.prefs.setBoolPref("signon.schemeUpgrades", true);
   yield BrowserTestUtils.withNewTab({
     gBrowser,
     url: TEST_HOSTNAME + MULTIPLE_FORMS_PAGE_PATH,
--- a/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
@@ -797,12 +797,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
@@ -849,16 +849,18 @@ 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")) {
+    if (mFocusedInput)
+      StopControllingInput();
     if (mFocusedPopup)
       mFocusedPopup->ClosePopup();
     return NS_OK;
   }
   if (type.EqualsLiteral("pagehide")) {
 
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(
       aEvent->InternalDOMEvent()->GetTarget());
@@ -924,16 +926,22 @@ nsFormFillController::MaybeStartControll
 }
 
 nsresult
 nsFormFillController::Focus(nsIDOMEvent* aEvent)
 {
   nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(
     aEvent->InternalDOMEvent()->GetTarget());
   MaybeStartControllingInput(input);
+
+  // if we have a password manager field auto show the popup
+  if (mPwmgrInputs.Get(mFocusedInputNode)) {
+    ShowPopup();
+  }
+
   return NS_OK;
 }
 
 nsresult
 nsFormFillController::KeyPress(nsIDOMEvent* aEvent)
 {
   NS_ASSERTION(mController, "should have a controller!");
   if (!mFocusedInput || !mController)
@@ -1056,16 +1064,21 @@ 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));
--- 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);