Bug 1534896 - Don't close the login autocomplete popup when the search string becomes empty. r=jaws
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Fri, 15 Mar 2019 20:57:02 +0000
changeset 464446 54ae8fe6502b06eb1523b6d249299367e3834deb
parent 464445 e58902a844dde0c468c89e02704bbb8ca69e482c
child 464447 a645622faf21365beb94094ed3f4274f0b00e88b
push id35716
push useraciure@mozilla.com
push dateSun, 17 Mar 2019 09:42:17 +0000
treeherdermozilla-central@8ee97c045359 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws
bugs1534896
milestone67.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 1534896 - Don't close the login autocomplete popup when the search string becomes empty. r=jaws Differential Revision: https://phabricator.services.mozilla.com/D23622
toolkit/components/autocomplete/nsAutoCompleteController.cpp
toolkit/components/autocomplete/nsIAutoCompleteInput.idl
toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_autocomplete.html
toolkit/components/satchel/nsFormFillController.cpp
toolkit/content/widgets/autocomplete.xml
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -259,18 +259,22 @@ nsAutoCompleteController::HandleText(boo
     mProhibitAutoFill = true;
     mPlaceholderCompletionString.Truncate();
   } else {
     mProhibitAutoFill = false;
   }
 
   SetSearchStringInternal(newValue);
 
+  bool noRollupOnEmptySearch;
+  nsresult rv = input->GetNoRollupOnEmptySearch(&noRollupOnEmptySearch);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // Don't search if the value is empty
-  if (newValue.Length() == 0) {
+  if (newValue.Length() == 0 && !noRollupOnEmptySearch) {
     // If autocomplete popup was closed by compositionstart event handler,
     // we should reopen it forcibly even if the value is empty.
     if (popupClosedByCompositionStart && handlingCompositionCommit) {
       bool cancel;
       HandleKeyNavigation(dom::KeyboardEvent_Binding::DOM_VK_DOWN, &cancel);
       return NS_OK;
     }
     ClosePopup();
--- a/toolkit/components/autocomplete/nsIAutoCompleteInput.idl
+++ b/toolkit/components/autocomplete/nsIAutoCompleteInput.idl
@@ -161,13 +161,18 @@ interface nsIAutoCompleteInput : nsISupp
    */
   readonly attribute boolean inPrivateContext;
 
   /*
    * Don't rollup the popup when the caret is moved.
    */
   readonly attribute boolean noRollupOnCaretMove;
 
+  /*
+   * Don't rollup the popup when the search string becomes "".
+   */
+  readonly attribute boolean noRollupOnEmptySearch;
+
   /**
    * The userContextId of the current browser.
    */
   readonly attribute unsigned long userContextId;
 };
--- a/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
@@ -171,16 +171,23 @@ var setupScript = runInParent(function s
   </div>
 
   <!-- test for onUsernameInput recipe testing -->
   <form id="form12" action="http://autocomplete7" onsubmit="return false;">
     <input  type="text"   name="1">
     <input  type="text"   name="2">
     <button type="submit">Submit</button>
   </form>
+
+  <!-- test not closing when the search string (.value) becomes empty -->
+  <form id="form13" action="http://autocomplete:8888/formtest.js" onsubmit="return false;">
+    <input  type="text"       name="uname" value="prefilled">
+    <input  type="password"   name="pword" value="prefilled">
+    <button type="submit">Submit</button>
+  </form>
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Login Manager: multiple login autocomplete. **/
 
 var uname = $_(1, "uname");
@@ -852,12 +859,42 @@ add_task(async function test_form12_reci
   checkACForm("", "");
   uname.value = "testuser10";
   checkACForm("testuser10", "");
   synthesizeKey("KEY_Tab");
   await promiseFormsProcessed();
   checkACForm("testuser10", "testpass10");
   await resetRecipes();
 });
+
+add_task(async function test_form13_stays_open_upon_empty_search() {
+  uname = $_(13, "uname");
+  pword = $_(13, "pword");
+  checkACForm("prefilled", "prefilled");
+
+  uname.scrollIntoView();
+  let shownPromise = promiseACShown();
+  synthesizeMouseAtCenter(uname, {});
+  await shownPromise;
+  uname.select();
+  synthesizeKey("KEY_Delete");
+
+  await spinEventLoop();
+  let popupState = await getPopupState();
+  is(popupState.open, true, "Check popup is still open");
+  checkACForm("", "prefilled");
+
+  info("testing password field");
+  shownPromise = promiseACShown();
+  synthesizeMouseAtCenter(pword, {});
+  await shownPromise;
+  pword.select();
+  synthesizeKey("KEY_Delete");
+
+  await spinEventLoop();
+  popupState = await getPopupState();
+  is(popupState.open, true, "Check popup is still open");
+  checkACForm("", "");
+});
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_autocomplete.html
@@ -166,17 +166,24 @@ var setupScript = runInParent(function s
     <button type="submit">Submit</button>
   </form>
 
   <!-- tests <form>-less autocomplete -->
   <div id="form12">
      <input  type="text"       name="uname" id="uname">
      <input  type="password"   name="pword" id="pword">
      <button type="submit">Submit</button>
-   </div>
+  </div>
+
+  <!-- test not closing when the search string (.value) becomes empty -->
+  <form id="form13" action="http://autocomplete:8888/formtest.js" onsubmit="return false;">
+    <input  type="text"       name="uname" value="prefilled">
+    <input  type="password"   name="pword" value="prefilled">
+    <button type="submit">Submit</button>
+  </form>
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Login Manager: multiple login autocomplete. **/
 
 SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal", true]]});
@@ -863,12 +870,42 @@ add_task(async function test_form12_form
   // Trigger autocomplete
   synthesizeKey("KEY_ArrowDown");
   checkACForm("", ""); // value shouldn't update
   let processedPromise = promiseFormsProcessed();
   synthesizeKey("KEY_Enter");
   await processedPromise;
   checkACForm("testuser", "testpass");
 });
+
+add_task(async function test_form13_stays_open_upon_empty_search() {
+  uname = $_(13, "uname");
+  pword = $_(13, "pword");
+  checkACForm("prefilled", "prefilled");
+
+  uname.scrollIntoView();
+  let shownPromise = promiseACShown();
+  synthesizeMouseAtCenter(uname, {});
+  await shownPromise;
+  uname.select();
+  synthesizeKey("KEY_Delete");
+
+  await spinEventLoop();
+  let popupState = await getPopupState();
+  is(popupState.open, true, "Check popup is still open");
+  checkACForm("", "prefilled");
+
+  info("testing password field");
+  shownPromise = promiseACShown();
+  synthesizeMouseAtCenter(pword, {});
+  await shownPromise;
+  pword.select();
+  synthesizeKey("KEY_Delete");
+
+  await spinEventLoop();
+  popupState = await getPopupState();
+  is(popupState.open, true, "Check popup is still open");
+  checkACForm("", "");
+});
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -639,16 +639,29 @@ nsFormFillController::GetInPrivateContex
 
 NS_IMETHODIMP
 nsFormFillController::GetNoRollupOnCaretMove(bool* aNoRollupOnCaretMove) {
   *aNoRollupOnCaretMove = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsFormFillController::GetNoRollupOnEmptySearch(bool* aNoRollupOnEmptySearch) {
+  if (mFocusedInput &&
+      (mPwmgrInputs.Get(mFocusedInput) ||
+       mFocusedInput->ControlType() == NS_FORM_INPUT_PASSWORD)) {
+    // Don't close the login popup when the field is cleared (bug 1534896).
+    *aNoRollupOnEmptySearch = true;
+  } else {
+    *aNoRollupOnEmptySearch = false;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsFormFillController::GetUserContextId(uint32_t* aUserContextId) {
   *aUserContextId = nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID;
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////
 //// nsIAutoCompleteSearch
 
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -25,16 +25,17 @@
 
       <xul:popupset anonid="popupset" class="autocomplete-result-popupset"/>
     </content>
 
     <implementation implements="nsIAutoCompleteInput, nsIDOMXULMenuListElement">
       <field name="mController">null</field>
       <field name="mSearchNames">null</field>
       <field name="mIgnoreInput">false</field>
+      <field name="noRollupOnEmptySearch">false</field>
 
       <field name="_searchBeginHandler">null</field>
       <field name="_searchCompleteHandler">null</field>
       <field name="_textEnteredHandler">null</field>
       <field name="_textRevertedHandler">null</field>
 
       <constructor><![CDATA[
         this.mController = Cc["@mozilla.org/autocomplete/controller;1"].