Bug 1278473 - write shim Services.focus; r=gregtatum
authorTom Tromey <tom@tromey.com>
Tue, 26 Jul 2016 08:03:24 -0600
changeset 347073 cd08b203bbae84b4bc042b4e27fe1f3bc4b2b354
parent 347072 2e786301b6e86a991a95707e9f64d9f4e7e1dc1a
child 347074 54eb32559af3c72a14593fdcea471ecae7001da2
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgregtatum
bugs1278473
milestone50.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 1278473 - write shim Services.focus; r=gregtatum MozReview-Commit-ID: 1D2EjcoiEz6
devtools/client/shared/shim/Services.js
devtools/client/shared/shim/test/mochitest.ini
devtools/client/shared/shim/test/test_service_focus.html
--- a/devtools/client/shared/shim/Services.js
+++ b/devtools/client/shared/shim/Services.js
@@ -1,17 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-/* globals localStorage, window */
+/* globals localStorage, window, document, NodeFilter */
 
 // Some constants from nsIPrefBranch.idl.
 const PREF_INVALID = 0;
 const PREF_STRING = 32;
 const PREF_INT = 64;
 const PREF_BOOL = 128;
 const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
 
@@ -514,16 +514,73 @@ const Services = {
     },
 
     getKeyedHistogramById: function (name) {
       return {
         add: () => {}
       };
     },
   },
+
+  /**
+   * An implementation of Services.focus that holds just the
+   * properties and methods needed by devtools.
+   * @see nsIFocusManager.idl for details.
+   */
+  focus: {
+    // These values match nsIFocusManager in order to make testing a
+    // bit simpler.
+    MOVEFOCUS_FORWARD: 1,
+    MOVEFOCUS_BACKWARD: 2,
+
+    get focusedElement() {
+      if (!document.hasFocus()) {
+        return null;
+      }
+      return document.activeElement;
+    },
+
+    moveFocus: function (window, startElement, type, flags) {
+      if (flags !== 0) {
+        throw new Error("shim Services.focus.moveFocus only accepts flags===0");
+      }
+      if (type !== Services.focus.MOVEFOCUS_FORWARD
+          && type !== Services.focus.MOVEFOCUS_BACKWARD) {
+        throw new Error("shim Services.focus.moveFocus only supports " +
+                        " MOVEFOCUS_FORWARD and MOVEFOCUS_BACKWARD");
+      }
+
+      if (!startElement) {
+        startElement = document.activeElement || document;
+      }
+
+      let iter = document.createTreeWalker(document, NodeFilter.SHOW_ELEMENT, {
+        acceptNode: function (node) {
+          let tabIndex = node.getAttribute("tabindex");
+          if (tabIndex === "-1") {
+            return NodeFilter.FILTER_SKIP;
+          }
+          node.focus();
+          if (document.activeElement == node) {
+            return NodeFilter.FILTER_ACCEPT;
+          }
+          return NodeFilter.FILTER_SKIP;
+        }
+      });
+
+      iter.currentNode = startElement;
+
+      // Sets the focus via side effect in the filter.
+      if (type === Services.focus.MOVEFOCUS_FORWARD) {
+        iter.nextNode();
+      } else {
+        iter.previousNode();
+      }
+    },
+  },
 };
 
 /**
  * Create a new preference.  This is used during startup (see
  * devtools/client/preferences/devtools.js) to install the
  * default preferences.
  *
  * @param {String} name the name of the preference
--- a/devtools/client/shared/shim/test/mochitest.ini
+++ b/devtools/client/shared/shim/test/mochitest.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 support-files =
   prefs-wrapper.js
 
 [test_service_appinfo.html]
+[test_service_focus.html]
 [test_service_prefs.html]
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/shim/test/test_service_focus.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1278473
+-->
+<head>
+  <title>Test for Bug 1278473 - replace Services.focus</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css">
+
+  <script type="application/javascript;version=1.8">
+  "use strict";
+  var exports = {};
+  </script>
+
+  <script type="application/javascript;version=1.8"
+	  src="resource://devtools/client/shared/shim/Services.js"></script>
+</head>
+<body>
+  <span>
+    <span id="start" testvalue="0" tabindex="0"> </span>
+    <label>
+      <input testvalue="1" type="radio">Hi</input>
+    </label>
+    <label>
+      <input type="radio" tabindex="-1">Bye</input>
+    </label>
+    <label style="display: none">
+      <input id="button3" type="radio" tabindex="-1">Invisible</input>
+    </label>
+    <input id="button4" type="radio" disabled="true">Disabled</input>
+    <span testvalue="2" tabindex="0"> </span>
+  </span>
+
+<script type="application/javascript;version=1.8">
+  "use strict";
+
+  // The test assumes these are identical, so assert it here.
+  is(Services.focus.MOVEFOCUS_BACKWARD, SpecialPowers.Services.focus.MOVEFOCUS_BACKWARD,
+     "check MOVEFOCUS_BACKWARD");
+  is(Services.focus.MOVEFOCUS_FORWARD, SpecialPowers.Services.focus.MOVEFOCUS_FORWARD,
+     "check MOVEFOCUS_FORWARD");
+
+  function moveFocus(element, type, expect) {
+    let current = document.activeElement;
+    const suffix = "(type=" + type + ", to=" + expect + ")";
+
+    // First try with the platform implementation.
+    SpecialPowers.Services.focus.moveFocus(window, element, type, 0);
+    is(document.activeElement.getAttribute("testvalue"), expect,
+       "platform moveFocus " + suffix);
+
+    // Reset the focus and try again with the shim.
+    current.focus();
+    is(document.activeElement, current, "reset " + suffix);
+
+    Services.focus.moveFocus(window, element, type, 0);
+    is(document.activeElement.getAttribute("testvalue"), expect,
+       "shim moveFocus " + suffix);
+  }
+
+  let start = document.querySelector("#start");
+  start.focus();
+  is(document.activeElement.getAttribute("testvalue"), "0", "initial focus");
+
+  moveFocus(null, Services.focus.MOVEFOCUS_FORWARD, "1");
+  moveFocus(null, Services.focus.MOVEFOCUS_FORWARD, "2");
+  let end = document.activeElement;
+  moveFocus(null, Services.focus.MOVEFOCUS_BACKWARD, "1");
+  moveFocus(null, Services.focus.MOVEFOCUS_BACKWARD, "0");
+
+  moveFocus(start, Services.focus.MOVEFOCUS_FORWARD, "1");
+  moveFocus(end, Services.focus.MOVEFOCUS_BACKWARD, "1");
+</script>
+</body>