Bug 1311301 - Ensure login managed inputs focus on load. r=mattn
authorDale Harvey <dale@arandomurl.com>
Thu, 01 Dec 2016 00:01:52 +0000
changeset 324952 72cef09ee478a13d87b78a61b93fbbdf7f616a79
parent 324951 f97b53fd3b7e0728c555efd145765879bda1b950
child 324953 35fb04df942944c17756b4ed86fc61879da01ab6
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersmattn
bugs1311301
milestone53.0a1
Bug 1311301 - Ensure login managed inputs focus on load. r=mattn
toolkit/components/passwordmgr/test/browser/form_autofocus_js.html
toolkit/components/passwordmgr/test/mochitest/mochitest.ini
toolkit/components/passwordmgr/test/mochitest/test_autofocus_js.html
toolkit/components/satchel/nsFormFillController.cpp
toolkit/components/satchel/test/parent_utils.js
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/test/browser/form_autofocus_js.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html><html><head><meta charset="utf-8"></head>
+<body onload="document.getElementById('form-basic-username').focus();">
+<!-- Username field is focused by js onload -->
+<form id="form-basic">
+  <input id="form-basic-username" name="username">
+  <input id="form-basic-password" name="password" type="password">
+  <input id="form-basic-submit" type="submit">
+</form>
+
+</body></html>
--- a/toolkit/components/passwordmgr/test/mochitest/mochitest.ini
+++ b/toolkit/components/passwordmgr/test/mochitest/mochitest.ini
@@ -1,26 +1,28 @@
 [DEFAULT]
 support-files =
   ../../../prompts/test/chromeScript.js
   ../../../prompts/test/prompt_common.js
   ../../../satchel/test/parent_utils.js
   ../../../satchel/test/satchel_common.js
   ../authenticate.sjs
   ../blank.html
+  ../browser/form_autofocus_js.html
   ../browser/form_basic.html
   ../browser/form_cross_origin_secure_action.html
   ../pwmgr_common.js
   auth2/authenticate.sjs
 
 [test_autocomplete_https_upgrade.html]
 skip-if = toolkit == 'android' # autocomplete
 [test_autofill_https_upgrade.html]
 skip-if = toolkit == 'android' # Bug 1259768
 [test_autofill_password-only.html]
+[test_autofocus_js.html]
 [test_basic_form.html]
 [test_basic_form_0pw.html]
 [test_basic_form_1pw.html]
 [test_basic_form_1pw_2.html]
 [test_basic_form_2pw_1.html]
 [test_basic_form_2pw_2.html]
 [test_basic_form_3pw_1.html]
 [test_basic_form_autocomplete.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/test/mochitest/test_autofocus_js.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test login autocomplete is activated when focused by js on load</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="satchel_common.js"></script>
+  <script type="text/javascript" src="pwmgr_common.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+const chromeScript = runChecksAfterCommonInit(false);
+
+runInParent(function addLogins() {
+  const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+  Cu.import("resource://gre/modules/Services.jsm");
+
+  // Create some logins just for this form, since we'll be deleting them.
+  let nsLoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
+                                           Ci.nsILoginInfo, "init");
+
+  let login0 = new nsLoginInfo("https://example.org", "https://example.org", null,
+                               "name", "pass", "uname", "pword");
+
+  let login1 = new nsLoginInfo("https://example.org", "https://example.org", null,
+                               "name1", "pass1", "uname", "pword");
+
+  try {
+    Services.logins.addLogin(login0);
+    Services.logins.addLogin(login1);
+  } catch (e) {
+    assert.ok(false, "addLogin threw: " + e);
+  }
+});
+</script>
+<p id="display"></p>
+
+<div id="content">
+  <iframe src="https://example.org/tests/toolkit/components/passwordmgr/test/mochitest/form_autofocus_js.html"></iframe>
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+let iframe = SpecialPowers.wrap(document.getElementsByTagName("iframe")[0]);
+let iframeDoc;
+
+add_task(function* setup() {
+  yield new Promise(resolve => {
+    iframe.addEventListener("load", function onLoad() {
+      iframe.removeEventListener("load", onLoad);
+      resolve();
+    });
+  });
+
+  iframeDoc = iframe.contentDocument;
+});
+
+add_task(function* test_initial_focus() {
+  let results = yield notifyMenuChanged(2, "name");
+  checkArrayValues(results, ["name", "name1"], "Two results");
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -35,16 +35,17 @@
 #include "mozilla/ModuleUtils.h"
 #include "nsToolkitCompsCID.h"
 #include "nsEmbedCID.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsContentUtils.h"
 #include "nsILoadContext.h"
 #include "nsIFrame.h"
 #include "nsIScriptSecurityManager.h"
+#include "nsFocusManager.h"
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION(nsFormFillController,
                          mController, mLoginManager, mFocusedPopup, mDocShells,
                          mPopups, mLastListener, mLastFormAutoComplete)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFormFillController)
@@ -270,16 +271,28 @@ nsFormFillController::MarkAsLoginManager
    * autocomplete. The form manager also checks for this tag when saving
    * form history (so it doesn't save usernames).
    */
   nsCOMPtr<nsINode> node = do_QueryInterface(aInput);
   NS_ENSURE_STATE(node);
   mPwmgrInputs.Put(node, true);
   node->AddMutationObserverUnlessExists(this);
 
+  nsFocusManager *fm = nsFocusManager::GetFocusManager();
+  if (fm) {
+    nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedContent();
+    if (SameCOMIdentity(focusedContent, node)) {
+      nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(node);
+      if (!mFocusedInput) {
+        MaybeStartControllingInput(input);
+      }
+      ShowPopup();
+    }
+  }
+
   if (!mLoginManager)
     mLoginManager = do_GetService("@mozilla.org/login-manager;1");
 
   return NS_OK;
 }
 
 
 ////////////////////////////////////////////////////////////////////////
--- a/toolkit/components/satchel/test/parent_utils.js
+++ b/toolkit/components/satchel/test/parent_utils.js
@@ -63,20 +63,26 @@ var ParentUtils = {
       }
     };
 
     FormHistory.count(obj, listener);
   },
 
   checkRowCount(expectedCount, expectedFirstValue = null) {
     ContentTaskUtils.waitForCondition(() => {
-      return gAutocompletePopup.view.matchCount === expectedCount &&
-        (!expectedFirstValue ||
-          expectedCount <= 1 ||
-          gAutocompletePopup.view.getValueAt(0) === expectedFirstValue);
+      // This may be called before gAutocompletePopup has initialised
+      // which causes it to throw
+      try {
+        return gAutocompletePopup.view.matchCount === expectedCount &&
+          (!expectedFirstValue ||
+           expectedCount <= 1 ||
+           gAutocompletePopup.view.getValueAt(0) === expectedFirstValue);
+      } catch (e) {
+        return false;
+      }
     }, "Waiting for row count change: " + expectedCount + " First value: " + expectedFirstValue).then(() => {
       let results = this.getMenuEntries();
       sendAsyncMessage("gotMenuChange", { results });
     });
   },
 
   checkSelectedIndex(expectedIndex) {
     ContentTaskUtils.waitForCondition(() => {