Bug 1577759 - add a keyboard check for non-focusable non-semantic elements that are interactive with mouse only. r=nchevobbe
authorYura Zenevich <yura.zenevich@gmail.com>
Mon, 02 Sep 2019 06:59:33 +0000
changeset 491200 db44fcfe3ee3279c45b290d7a4441eba277329bc
parent 491199 60950c861a6a18fdcb090d04fd5e53ef65411bbf
child 491201 bb93809181cf38ac5bb295ee4bfe148c72106689
push id94252
push useryura.zenevich@gmail.com
push dateMon, 02 Sep 2019 12:58:39 +0000
treeherderautoland@de24dca77618 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnchevobbe
bugs1577759
milestone70.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 1577759 - add a keyboard check for non-focusable non-semantic elements that are interactive with mouse only. r=nchevobbe Differential Revision: https://phabricator.services.mozilla.com/D44231
devtools/server/actors/accessibility/audit/keyboard.js
devtools/server/tests/browser/browser_accessibility_keyboard_audit.js
devtools/server/tests/browser/doc_accessibility_keyboard_audit.html
devtools/shared/constants.js
--- a/devtools/server/actors/accessibility/audit/keyboard.js
+++ b/devtools/server/actors/accessibility/audit/keyboard.js
@@ -28,16 +28,17 @@ const {
   accessibility: {
     AUDIT_TYPE: { KEYBOARD },
     ISSUE_TYPE: {
       [KEYBOARD]: {
         FOCUSABLE_NO_SEMANTICS,
         FOCUSABLE_POSITIVE_TABINDEX,
         INTERACTIVE_NO_ACTION,
         INTERACTIVE_NOT_FOCUSABLE,
+        MOUSE_INTERACTIVE_ONLY,
         NO_FOCUS_VISIBLE,
       },
     },
     SCORES: { FAIL, WARNING },
   },
 } = require("devtools/shared/constants");
 
 // Specified by the author CSS rule type.
@@ -331,16 +332,20 @@ function semanticsRule(accessible) {
   }
 
   const state = {};
   accessible.getState(state, {});
   if (state.value & Ci.nsIAccessibleStates.STATE_FOCUSABLE) {
     return { score: WARNING, issue: FOCUSABLE_NO_SEMANTICS };
   }
 
+  if (accessible.actionCount > 0) {
+    return { score: FAIL, issue: MOUSE_INTERACTIVE_ONLY };
+  }
+
   return null;
 }
 
 /**
  * A rule that determines if an element associated with a focusable accessible
  * has a positive tabindex.
  *
  * @param  {nsIAccessible} accessible
--- a/devtools/server/tests/browser/browser_accessibility_keyboard_audit.js
+++ b/devtools/server/tests/browser/browser_accessibility_keyboard_audit.js
@@ -13,16 +13,17 @@ const {
     AUDIT_TYPE: { KEYBOARD },
     SCORES: { FAIL, WARNING },
     ISSUE_TYPE: {
       [KEYBOARD]: {
         FOCUSABLE_NO_SEMANTICS,
         FOCUSABLE_POSITIVE_TABINDEX,
         INTERACTIVE_NO_ACTION,
         INTERACTIVE_NOT_FOCUSABLE,
+        MOUSE_INTERACTIVE_ONLY,
         NO_FOCUS_VISIBLE,
       },
     },
   },
 } = require("devtools/shared/constants");
 
 add_task(async function() {
   const { target, walker, accessibility } = await initAccessibilityFrontForUrl(
@@ -85,16 +86,32 @@ add_task(async function() {
     ],
     [
       "Focusable ARIA button with no focus styling.",
       "#focusable-1",
       { score: WARNING, issue: NO_FOCUS_VISIBLE },
     ],
     ["Focusable ARIA button with focus styling.", "#focusable-2", null],
     ["Focusable ARIA button with browser styling.", "#focusable-3", null],
+    [
+      "Not focusable, non-semantic element that has a click handler.",
+      "#mouse-only-1",
+      { score: FAIL, issue: MOUSE_INTERACTIVE_ONLY },
+    ],
+    [
+      "Focusable, non-semantic element that has a click handler.",
+      "#focusable-4",
+      { score: WARNING, issue: FOCUSABLE_NO_SEMANTICS },
+    ],
+    [
+      "Not focusable, ARIA button that has a click handler.",
+      "#button-7",
+      { score: FAIL, issue: INTERACTIVE_NOT_FOCUSABLE },
+    ],
+    ["Focusable, ARIA button with a click handler.", "#button-8", null],
   ];
 
   for (const [description, selector, expected] of tests) {
     info(description);
     const node = await walker.querySelector(walker.rootNode, selector);
     const front = await a11yWalker.getAccessibleFor(node);
     const audit = await front.audit({ types: [KEYBOARD] });
     Assert.deepEqual(
--- a/devtools/server/tests/browser/doc_accessibility_keyboard_audit.html
+++ b/devtools/server/tests/browser/doc_accessibility_keyboard_audit.html
@@ -24,10 +24,14 @@
   <a id="link-2" href="example.com">I'm a proper link.</a>
   <button id="button-3">Button with no tabindex</button>
   <button id="button-4" tabindex="-1">Button with -1 tabindex</button>
   <button id="button-5" tabindex="0">Button with 0 tabindex</button>
   <button id="button-6" tabindex="1">Button with 1 tabindex</button>
   <div id="focusable-1" role="button" tabindex="0">Focusable with no focus style.</div>
   <div id="focusable-2" role="button" tabindex="0">Focusable with focus style.</div>
   <div id="focusable-3" role="button" tabindex="0">Focusable with focus style.</div>
+  <div id="mouse-only-1" onclick="console.log('foo');">Button for mouse only</div>
+  <div id="focusable-4" onclick="console.log('foo');" tabindex="0">Button no semantics</div>
+  <div id="button-7" onclick="console.log('foo');" role="button">Semantic button not focusable</div>
+  <div id="button-8" onclick="console.log('foo');" role="button" tabindex="0">Button</div>
 </body>
 </html>
--- a/devtools/shared/constants.js
+++ b/devtools/shared/constants.js
@@ -23,16 +23,18 @@ const ISSUE_TYPE = {
     // Focusable accessible objects have no semantics.
     FOCUSABLE_NO_SEMANTICS: "FOCUSABLE_NO_SEMANTICS",
     // Tab index greater than 0 is provided.
     FOCUSABLE_POSITIVE_TABINDEX: "FOCUSABLE_POSITIVE_TABINDEX",
     // Interactive accesible objects do not have an associated action.
     INTERACTIVE_NO_ACTION: "INTERACTIVE_NO_ACTION",
     // Interative accessible objcets are not focusable.
     INTERACTIVE_NOT_FOCUSABLE: "INTERACTIVE_NOT_FOCUSABLE",
+    // Accessible objects can only be interacted with a mouse.
+    MOUSE_INTERACTIVE_ONLY: "MOUSE_INTERACTIVE_ONLY",
     // Focusable accessible objects have no focus styling.
     NO_FOCUS_VISIBLE: "NO_FOCUS_VISIBLE",
   },
   [AUDIT_TYPE.TEXT_LABEL]: {
     // <AREA> name is provided via "alt" attribute.
     AREA_NO_NAME_FROM_ALT: "AREA_NO_NAME_FROM_ALT",
     // Dialog name is not provided.
     DIALOG_NO_NAME: "DIALOG_NO_NAME",