Bug 1008502 - Activate first operable descendant when landing on containers. r=MarcoZ
authorEitan Isaacson <eitan@monotonous.org>
Wed, 14 May 2014 16:47:53 -0500
changeset 195767 537eb199f2b7e7ebb602b532dc5d7394e1087888
parent 195766 e0fa93595164f3b340f43a82d4d5e401b58ffd6c
child 195768 28e12f7f68bed7840c93e43dac22744f82102d39
push id5990
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:40:24 +0000
treeherdermozilla-aurora@0796197efbc9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMarcoZ
bugs1008502
milestone32.0a1
Bug 1008502 - Activate first operable descendant when landing on containers. r=MarcoZ .. and don't land on containers with a mix of operable and not operable children.
accessible/src/jsat/ContentControl.jsm
accessible/src/jsat/TraversalRules.jsm
accessible/tests/mochitest/jsat/doc_content_integration.html
accessible/tests/mochitest/jsat/doc_traversal.html
accessible/tests/mochitest/jsat/test_content_integration.html
accessible/tests/mochitest/jsat/test_traversal.html
--- a/accessible/src/jsat/ContentControl.jsm
+++ b/accessible/src/jsat/ContentControl.jsm
@@ -216,19 +216,36 @@ this.ContentControl.prototype = {
       if (newOffset >= 0 && newOffset <= accText.characterCount) {
         accText.caretOffset = newOffset;
       }
 
       this.presentCaretChange(text, oldOffset, accText.caretOffset);
       return;
     }
 
+    // recursively find a descendant that is activatable.
+    let getActivatableDescendant = (aAccessible) => {
+      if (aAccessible.actionCount > 0) {
+        return aAccessible;
+      }
+
+      for (let acc = aAccessible.firstChild; acc; acc = acc.nextSibling) {
+        let activatable = getActivatableDescendant(acc);
+        if (activatable) {
+          return activatable;
+        }
+      }
+
+      return null;
+    };
+
     let vc = this.vc;
     if (!this.sendToChild(vc, aMessage)) {
-      activateAccessible(vc.position);
+      let position = vc.position;
+      activateAccessible(getActivatableDescendant(position) || position);
     }
   },
 
   handleMoveByGranularity: function cc_handleMoveByGranularity(aMessage) {
     // XXX: Add sendToChild. Right now this is only used in Android, so no need.
     let direction = aMessage.json.direction;
     let granularity;
 
--- a/accessible/src/jsat/TraversalRules.jsm
+++ b/accessible/src/jsat/TraversalRules.jsm
@@ -108,17 +108,17 @@ var gSimpleMatchFunc = function gSimpleM
         return false;
       }
     }
     return true;
   }
 
   function isFlatSubtree(acc) {
     for (let child = acc.firstChild; child; child = child.nextSibling) {
-      if (child.childCount > 0) {
+      if (child.childCount > 0 || child.actionCount > 0) {
         return false;
       }
     }
     return true;
   }
 
   switch (aAccessible.role) {
   case Roles.COMBOBOX:
--- a/accessible/tests/mochitest/jsat/doc_content_integration.html
+++ b/accessible/tests/mochitest/jsat/doc_content_integration.html
@@ -3,17 +3,19 @@
 <head>
   <title>Traversal Rule test document</title>
   <meta charset="utf-8" />
   <script>
     var frameContents = '<html>' +
       '<head><title>such app</title></head>' +
       '<body>' +
       '<h1>wow</h1>' +
-      '<label><input type="checkbox">many option</label><br>' +
+      '<ul>' +
+      '<li><label><input type="checkbox">many option</label></li>' +
+      '</ul>' +
       '<label for="r">much range</label>' +
       '<input min="0" max="10" value="5" type="range" id="r">' +
       '</body>' +
       '</html>';
 
     function showAlert() {
       document.getElementById('alert').hidden = false;
     }
--- a/accessible/tests/mochitest/jsat/doc_traversal.html
+++ b/accessible/tests/mochitest/jsat/doc_traversal.html
@@ -32,17 +32,17 @@
       <option>Value 2</option>
       <option>Value 3</option>
     </select>
     <label for="checkbox-1-2">Check me too: </label>
     <input id="checkbox-1-2" type="checkbox">
     <label for="checkbox-1-3">But not me: </label>
     <input id="checkbox-1-3" type="checkbox" aria-hidden="true">
     <label for="checkbox-1-4">Or me! </label>
-    <input id="checkbox-1-3" type="checkbox" style="visibility:hidden">
+    <input id="checkbox-1-4" type="checkbox" style="visibility:hidden">
     <select id="select-1-3" size="3">
       <option>Value 1</option>
       <option>Value 2</option>
       <option>Value 3</option>
     </select>
     <label for="input-1-5">Electronic mailing address:</label>
     <input id="input-1-5" type="email">
     <input id="button-1-3" type="submit" value="Submit">
@@ -62,16 +62,19 @@
   </dl>
   <ul id="list-2">
     <li id="listitem-2-1">Lists of Programming Languages</li>
     <li id="listitem-2-2">Lisp
       <ol id="list-3">
         <li id="listitem-3-1">Scheme</li>
         <li id="listitem-3-2">Racket</li>
         <li id="listitem-3-3">Clojure</li>
+        <li id="listitem-3-4"><strong>Standard</strong> Lisp</li>
+        <li id="listitem-3-5"><a id="link-0" href="#">Common</a> Lisp</li>
+        <li id="listitem-3-6"><input id="checkbox-1-5" type="checkbox"> LeLisp</li>
       </ol>
     </li>
     <li id="listitem-2-3">JavaScript</li>
   </ul>
   <h6 id="heading-5">The last (visible) one!</h6>
   <img id="image-1" src="http://example.com" alt="">
   <img id="image-2" src="../moz.png" alt="stuff">
   <div id="image-3" tabindex="0" role="img">Not actually an image</div>
--- a/accessible/tests/mochitest/jsat/test_content_integration.html
+++ b/accessible/tests/mochitest/jsat/test_content_integration.html
@@ -31,17 +31,17 @@
           // Simple traversal forward
           [ContentMessages.simpleMoveNext, {
             speak: 'Phone status bar Traversal Rule test document'
           }],
           [ContentMessages.simpleMoveNext, {
             speak: 'wow heading level 1 such app'
           }],
           [ContentMessages.simpleMoveNext, {
-            speak: 'many option not checked check button'
+            speak: 'many option not checked check button First item list 1 item'
           }],
           // check checkbox
           [ContentMessages.activateCurrent(), {
             speak: 'checked'
           }],
           [ContentMessages.simpleMoveNext, {
             speak: 'much range label'
           }],
@@ -59,17 +59,17 @@
             speak: 'much range 6 slider such app'
           }],
           [ContentMessages.adjustRangeDown,
            { speak: '5'}],
           [ContentMessages.simpleMovePrevious, {
             speak: 'much range label'
           }],
           [ContentMessages.simpleMovePrevious, {
-            speak: 'many option checked check button'
+            speak: 'many option checked check button First item list 1 item'
           }],
           // uncheck checkbox
           [ContentMessages.activateCurrent(), {
             speak: 'unchecked'
           }],
           [ContentMessages.simpleMovePrevious, {
             speak: 'wow heading level 1'
           }],
@@ -94,17 +94,17 @@
           // fails. Bug 972035.
           [ContentMessages.simpleMoveNext, {
             speak: 'Phone status bar Traversal Rule test document'
           }],
           [ContentMessages.simpleMoveNext, {
             speak: 'wow heading level 1 such app'
           }],
           [ContentMessages.simpleMoveNext, {
-            speak: 'many option not checked check button'
+            speak: 'many option not checked check button First item list 1 item'
           }],
           [ContentMessages.simpleMoveFirst, {
             speak: 'Phone status bar',
             speak_checkFunc: 'todo_is'
           }],
 
           // Reset cursors
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
--- a/accessible/tests/mochitest/jsat/test_traversal.html
+++ b/accessible/tests/mochitest/jsat/test_traversal.html
@@ -51,45 +51,46 @@
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.FormElement, null,
                              ['input-1-1', 'label-1-2', 'button-1-1',
                               'radio-1-1', 'radio-1-2', 'input-1-3',
                               'input-1-4', 'button-1-2', 'checkbox-1-1',
                               'select-1-1', 'select-1-2', 'checkbox-1-2',
                               'select-1-3', 'input-1-5', 'button-1-3',
                               'button-2-1', 'button-2-2', 'button-2-3',
-                              'button-2-4']);
+                              'button-2-4', 'checkbox-1-5']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Button, null,
                              ['button-1-1', 'button-1-2', 'button-1-3',
                               'button-2-1', 'button-2-2', 'button-2-3',
                               'button-2-4']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.RadioButton, null,
                              ['radio-1-1', 'radio-1-2']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Checkbox, null,
-                             ['checkbox-1-1', 'checkbox-1-2']);
+                             ['checkbox-1-1', 'checkbox-1-2', 'checkbox-1-5']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Combobox, null,
                              ['select-1-1', 'select-1-2', 'select-1-3']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.List, null,
                              ['list-1', 'list-2', 'list-3']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.ListItem, null,
                              ['listitem-1-1', 'listitem-2-1', 'listitem-2-2',
                               'listitem-3-1', 'listitem-3-2', 'listitem-3-3',
+                              'listitem-3-4', 'listitem-3-5', 'listitem-3-6',
                               'listitem-2-3']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Graphic, null,
                              ['image-2', 'image-3']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Link, null,
-                             ['link-1', 'link-2', 'link-3']);
+                             ['link-0', 'link-1', 'link-2', 'link-3']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Anchor, null,
                              ['anchor-1', 'anchor-2']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Separator, null,
                              ['separator-1', 'separator-2']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Table, null,
@@ -107,17 +108,19 @@
                               'Electronic mailing address:', 'input-1-5',
                               'button-1-3', 'heading-2', 'heading-3',
                               'button-2-1', 'button-2-2', 'button-2-3',
                               'button-2-4', 'Programming Language',
                               'A esoteric weapon wielded by only the most ' +
                               'formidable warriors, for its unrelenting strict' +
                               ' power is unfathomable.',
                               '• Lists of Programming Languages', 'Lisp ',
-                              '1. Scheme', '2. Racket', '3. Clojure', '• JavaScript', 'heading-5',
+                              '1. Scheme', '2. Racket', '3. Clojure',
+                              '4. Standard Lisp', 'link-0', ' Lisp', 'checkbox-1-5',
+                              ' LeLisp', '• JavaScript', 'heading-5',
                               'image-2', 'image-3', 'Not actually an image',
                               'link-1', 'anchor-1', 'link-2', 'anchor-2', 'link-3',
                               '3', '1', '4', '1', 'Just an innocuous separator',
                               'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt',
                               'Dirt', 'Messy Stuff']);
 
       gQueue.invoke();
     }