Bug 753986 - Change presenter and utterance generator to be subtree-based. r=davidb
authorEitan Isaacson <eitan@monotonous.org>
Tue, 29 May 2012 13:46:08 -0700
changeset 95203 1b96b75f831a52f6609c20b9c854311ada676913
parent 95202 1ff8624857594461da0029498af4ff103f309c7e
child 95204 b18797e3ee6cd36eb4cca305a86aa43512cda9a3
push id22791
push useremorley@mozilla.com
push dateWed, 30 May 2012 11:23:53 +0000
treeherdermozilla-central@b5af439f1717 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdavidb
bugs753986
milestone15.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 753986 - Change presenter and utterance generator to be subtree-based. r=davidb
accessible/src/jsat/Presenters.jsm
accessible/src/jsat/UtteranceGenerator.jsm
--- a/accessible/src/jsat/Presenters.jsm
+++ b/accessible/src/jsat/Presenters.jsm
@@ -233,23 +233,30 @@ AndroidPresenter.prototype = {
   ANDROID_VIEW_TEXT_CHANGED: 0x10,
   ANDROID_WINDOW_STATE_CHANGED: 0x20,
 
   pivotChanged: function AndroidPresenter_pivotChanged(aContext) {
     if (!aContext.accessible)
       return;
 
     let output = [];
-    for (let i in aContext.newAncestry)
-      output.push.apply(
-        output, UtteranceGenerator.genForObject(aContext.newAncestry[i]));
+    aContext.newAncestry.forEach(
+      function (acc) {
+        output.push.apply(output, UtteranceGenerator.genForObject(acc));
+      }
+    );
 
     output.push.apply(output,
-                      UtteranceGenerator.genForObject(aContext.accessible,
-                                                      true));
+                      UtteranceGenerator.genForObject(aContext.accessible));
+
+    aContext.subtreePreorder.forEach(
+      function (acc) {
+        output.push.apply(output, UtteranceGenerator.genForObject(acc));
+      }
+    );
 
     this.sendMessageToJava({
       gecko: {
         type: 'Accessibility:Event',
         eventType: this.ANDROID_VIEW_FOCUSED,
         text: output
       }
     });
@@ -359,45 +366,69 @@ PresenterContext.prototype = {
    * user context as to where they are in the heirarchy.
    */
   get newAncestry() {
     if (!this._newAncestry) {
       let newLineage = [];
       let oldLineage = [];
 
       let parent = this._accessible;
-      while ((parent = parent.parent))
+      while (parent && (parent = parent.parent))
         newLineage.push(parent);
 
-      if (this._oldAccessible) {
-        parent = this._oldAccessible;
-        while ((parent = parent.parent))
-          oldLineage.push(parent);
-      }
+      parent = this._oldAccessible;
+      while (parent && (parent = parent.parent))
+        oldLineage.push(parent);
 
-      let i = 0;
       this._newAncestry = [];
 
       while (true) {
         let newAncestor = newLineage.pop();
         let oldAncestor = oldLineage.pop();
 
         if (newAncestor == undefined)
           break;
 
         if (newAncestor != oldAncestor)
           this._newAncestry.push(newAncestor);
-        i++;
       }
 
     }
 
     return this._newAncestry;
   },
 
+  /*
+   * This is a flattened list of the accessible's subtree in preorder.
+   * It only includes the accessible's visible chidren.
+   */
+  get subtreePreorder() {
+    function traversePreorder(aAccessible) {
+      let list = [];
+      let child = aAccessible.firstChild;
+      while (child) {
+        let state = {};
+        child.getState(state, {});
+
+        if (!(state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE)) {
+          list.push(child);
+          list.push.apply(list, traversePreorder(child));
+        }
+
+        child = child.nextSibling;
+      }
+      return list;
+    }
+
+    if (!this._subtreePreOrder)
+      this._subtreePreOrder = traversePreorder(this._accessible);
+
+    return this._subtreePreOrder;
+  },
+
   _isDefunct: function _isDefunct(aAccessible) {
     try {
       let extstate = {};
       aAccessible.getState({}, extstate);
       return !!(aAccessible.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT);
     } catch (x) {
       return true;
     }
--- a/accessible/src/jsat/UtteranceGenerator.jsm
+++ b/accessible/src/jsat/UtteranceGenerator.jsm
@@ -55,33 +55,30 @@ var UtteranceGenerator = {
     cycle: 'cycleAction'
   },
 
 
   /**
    * Generates an utterance for an object.
    * @param {nsIAccessible} aAccessible accessible object to generate utterance
    *    for.
-   * @param {boolean} aForceName include the object's name in the utterance
-   *    even if this object type does not usually have it's name uttered.
    * @return {Array} Two string array. The first string describes the object
-   *    and its states. The second string is the object's name. Some object
-   *    types may have the description or name omitted, instead an empty string
-   *    is returned as a placeholder. Whether the object's description or it's
-   *    role is included is determined by {@link verbosityRoleMap}.
+   *    and its states. The second string is the object's name. Whether the
+   *    object's description or it's role is included is determined by
+   *    {@link verbosityRoleMap}.
    */
-  genForObject: function genForObject(aAccessible, aForceName) {
+  genForObject: function genForObject(aAccessible) {
     let roleString = gAccRetrieval.getStringRole(aAccessible.role);
 
     let func = this.objectUtteranceFunctions[roleString] ||
       this.objectUtteranceFunctions.defaultFunc;
 
     let flags = this.verbosityRoleMap[roleString] || 0;
 
-    if (aForceName)
+    if (aAccessible.childCount == 0)
       flags |= INCLUDE_NAME;
 
     let state = {};
     let extState = {};
     aAccessible.getState(state, extState);
     let states = {base: state.value, ext: extState.value};
 
     return func.apply(this, [aAccessible, roleString, states, flags]);
@@ -125,35 +122,35 @@ var UtteranceGenerator = {
         return [];
     }
   },
 
   verbosityRoleMap: {
     'menubar': INCLUDE_DESC,
     'scrollbar': INCLUDE_DESC,
     'grip': INCLUDE_DESC,
-    'alert': INCLUDE_DESC,
+    'alert': INCLUDE_DESC | INCLUDE_NAME,
     'menupopup': INCLUDE_DESC,
     'menuitem': INCLUDE_DESC,
     'tooltip': INCLUDE_DESC,
     'application': INCLUDE_NAME,
     'document': INCLUDE_NAME,
+    'grouping': INCLUDE_DESC | INCLUDE_NAME,
     'toolbar': INCLUDE_DESC,
+    'table': INCLUDE_DESC | INCLUDE_NAME,
     'link': INCLUDE_DESC,
     'list': INCLUDE_DESC,
     'listitem': INCLUDE_DESC,
     'outline': INCLUDE_DESC,
     'outlineitem': INCLUDE_DESC,
     'pagetab': INCLUDE_DESC,
-    'graphic': INCLUDE_DESC | INCLUDE_NAME,
-    'statictext': INCLUDE_NAME,
-    'text leaf': INCLUDE_NAME,
+    'graphic': INCLUDE_DESC,
     'pushbutton': INCLUDE_DESC,
-    'checkbutton': INCLUDE_DESC | INCLUDE_NAME,
-    'radiobutton': INCLUDE_DESC | INCLUDE_NAME,
+    'checkbutton': INCLUDE_DESC,
+    'radiobutton': INCLUDE_DESC,
     'combobox': INCLUDE_DESC,
     'droplist': INCLUDE_DESC,
     'progressbar': INCLUDE_DESC,
     'slider': INCLUDE_DESC,
     'spinbutton': INCLUDE_DESC,
     'diagram': INCLUDE_DESC,
     'animation': INCLUDE_DESC,
     'equation': INCLUDE_DESC,
@@ -163,21 +160,21 @@ var UtteranceGenerator = {
     'check menu item': INCLUDE_DESC,
     'label': INCLUDE_DESC,
     'password text': INCLUDE_DESC,
     'popup menu': INCLUDE_DESC,
     'radio menu item': INCLUDE_DESC,
     'toggle button': INCLUDE_DESC,
     'header': INCLUDE_DESC,
     'footer': INCLUDE_DESC,
-    'entry': INCLUDE_DESC,
+    'entry': INCLUDE_DESC | INCLUDE_NAME,
     'caption': INCLUDE_DESC,
     'document frame': INCLUDE_DESC,
     'heading': INCLUDE_DESC,
-    'calendar': INCLUDE_DESC,
+    'calendar': INCLUDE_DESC | INCLUDE_NAME,
     'combobox list': INCLUDE_DESC,
     'combobox option': INCLUDE_DESC,
     'image map': INCLUDE_DESC,
     'option': INCLUDE_DESC,
     'listbox': INCLUDE_DESC},
 
   objectUtteranceFunctions: {
     defaultFunc: function defaultFunc(aAccessible, aRoleStr, aStates, aFlags) {