[orca] Fix multiple issues related to presentation of widgets and web content



commit ee27e7015ad536bd9d8218b3af3494b0163d39f2
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Mon Mar 2 09:25:52 2015 -0500

    Fix multiple issues related to presentation of widgets and web content
    
    * Improve and correct several aspects of combo box presentation
    * Stop presenting useless panels that show up in Gecko content
    * Tweak the Gecko zombie replicant detection now that we're not using
      pyatspi's cached names
    * Start presenting data tables as ancestors in Gecko content
    * Eliminate some instances of embedded object characters in Gecko content

 src/orca/formatting.py                             |   12 +-
 src/orca/generator.py                              |   22 ++-
 src/orca/script_utilities.py                       |  247 ++++++++++----------
 src/orca/scripts/default.py                        |    4 +-
 .../scripts/toolkits/Gecko/braille_generator.py    |   35 +---
 src/orca/scripts/toolkits/Gecko/formatting.py      |   15 --
 src/orca/scripts/toolkits/Gecko/script.py          |    2 +-
 .../scripts/toolkits/Gecko/script_utilities.py     |   20 --
 .../scripts/toolkits/Gecko/speech_generator.py     |   99 +++------
 src/orca/speech_generator.py                       |   88 ++------
 test/keystrokes/firefox/aria_button_dojo.py        |   11 +-
 test/keystrokes/firefox/aria_combobox_dojo.py      |   13 +-
 .../firefox/aria_editor_navigation_dojo.py         |    7 +-
 test/keystrokes/firefox/aria_grid_uiuc.py          |    1 +
 test/keystrokes/firefox/aria_tree_dojo.py          |   12 +-
 test/keystrokes/firefox/aria_treegrid.py           |    4 +-
 test/keystrokes/firefox/html_role_combo_box.py     |    2 +-
 test/keystrokes/firefox/html_struct_nav_links.py   |    8 +-
 .../firefox/line_nav_follow_same_page_link.py      |    4 +-
 test/keystrokes/firefox/say_all_bugzilla_search.py |   10 -
 test/keystrokes/firefox/say_all_enter_bug.py       |    6 -
 test/keystrokes/firefox/say_all_entries.py         |   37 ---
 test/keystrokes/firefox/ui_role_combo_box.py       |   18 +-
 test/keystrokes/firefox/ui_role_menu_bar.py        |    2 +-
 test/keystrokes/gtk-demo/role_combo_box.py         |   38 ++--
 test/keystrokes/gtk-demo/role_combo_box2.py        |   11 +-
 test/keystrokes/gtk3-demo/role_combo_box.py        |   31 ++--
 test/keystrokes/gtk3-demo/role_combo_box2.py       |   11 +-
 28 files changed, 279 insertions(+), 491 deletions(-)
---
diff --git a/src/orca/formatting.py b/src/orca/formatting.py
index d7c7a31..8d1e701 100644
--- a/src/orca/formatting.py
+++ b/src/orca/formatting.py
@@ -107,7 +107,7 @@ formatting = {
             'detailedWhereAmI' : '[]'
             },
         pyatspi.ROLE_ALERT: {
-            'unfocused': 'labelOrName + unrelatedLabels'
+            'unfocused': 'expandedEOCs or (labelOrName + unrelatedLabels)'
             },
         pyatspi.ROLE_ANIMATION: {
             'unfocused': 'labelAndName'
@@ -145,7 +145,7 @@ formatting = {
             },
         pyatspi.ROLE_DIALOG: {
             'focused': 'labelOrName',
-            'unfocused': 'labelOrName + unrelatedLabels'
+            'unfocused': 'expandedEOCs or (labelOrName + unrelatedLabels)'
             },
         pyatspi.ROLE_DOCUMENT_FRAME: {
             'unfocused': 'label + readOnly + textRole + textContent + anyTextSelection + ' + MNEMONIC,
@@ -220,7 +220,7 @@ formatting = {
         pyatspi.ROLE_MENU_ITEM: {
             'focused': 'expandableState',
             'unfocused': 'labelOrName + menuItemCheckedState + expandableState + availability + ' + MNEMONIC 
+ ' + accelerator + positionInList',
-            'basicWhereAmI': 'ancestors + labelOrName + accelerator + positionInList + ' + MNEMONIC
+            'basicWhereAmI': 'ancestors + parentRoleName + labelOrName + accelerator + positionInList + ' + 
MNEMONIC
             },
         pyatspi.ROLE_NOTIFICATION: {
             'unfocused': 'roleName + unrelatedLabels'
@@ -425,9 +425,9 @@ formatting = {
             # We could then use the cursorOffset field to indicate where the
             # combobox starts.]]]
             #
-            'unfocused': '((comboBoxTextObj and [Text(comboBoxTextObj[0], asString(label), asString(eol))])\
-                           or [Component(obj, asString(label + name), label and (len(asString(label)) + 1) 
or 0)])\
-                          + [Region(" " + asString(roleName))]'
+            'unfocused': '((comboBoxTextObj and ([Text(comboBoxTextObj[0], asString(label), asString(eol))] \
+                                               + [Region(" " + asString(roleName))])) \
+                           or [Component(obj, asString(label + name + roleName), label and 
(len(asString(label)) + 1) or 0)])'
             },
         #pyatspi.ROLE_DESKTOP_ICON: 'default'
         pyatspi.ROLE_DIAL: {
diff --git a/src/orca/generator.py b/src/orca/generator.py
index 26d903d..bc89d29 100644
--- a/src/orca/generator.py
+++ b/src/orca/generator.py
@@ -301,6 +301,17 @@ class Generator:
         """
         result = []
         name = self._script.utilities.displayedText(obj)
+        if obj.getRole() == pyatspi.ROLE_COMBO_BOX:
+            children = self._script.utilities.selectedChildren(obj)
+            if not children and obj.childCount:
+                children = self._script.utilities.selectedChildren(obj[0])
+            children = children or [child for child in obj]
+            names = map(self._script.utilities.displayedText, children)
+            names = list(filter(lambda x: x, names))
+            if len(names) == 1:
+                name = names[0].strip()
+            elif len(children) == 1 and children[0].name:
+                name = children[0].name.strip()
         if name:
             result.append(name)
         else:
@@ -825,6 +836,10 @@ class Generator:
     #                                                                   #
     #####################################################################
 
+    def _generateExpandedEOCs(self, obj, **args):
+        """Returns the expanded embedded object characters for an object."""
+        return []
+
     def _generateSubstring(self, obj, **args):
         start = args.get('startOffset')
         end = args.get('endOffset')
@@ -833,7 +848,7 @@ class Generator:
 
         substring = self._script.utilities.substring(obj, start, end)
         if substring and substring.strip() != obj.name \
-           and substring.find(self._script.EMBEDDED_OBJECT_CHARACTER) == -1:
+           and not self._script.EMBEDDED_OBJECT_CHARACTER in substring:
             return [substring]
 
         return []
@@ -855,7 +870,10 @@ class Generator:
             return result
 
         [text, caretOffset, startOffset] = self._script.getTextLineAtCaret(obj)
-        return [text]
+        if text and not self._script.EMBEDDED_OBJECT_CHARACTER in text:
+            return [text]
+
+        return []
 
     def _generateDisplayedText(self, obj, **args ):
         """Returns an array of strings for use by speech and braille that
diff --git a/src/orca/script_utilities.py b/src/orca/script_utilities.py
index d7a0da2..1185916 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -477,73 +477,6 @@ class Utilities:
         self._script.generatorCache[self.DISPLAYED_LABEL][obj] = labelString
         return self._script.generatorCache[self.DISPLAYED_LABEL][obj]
 
-    def _displayedTextInComboBox(self, combo):
-
-        """Returns the text being displayed in a combo box.  If nothing is
-        displayed, then None is returned.
-
-        Arguments:
-        - combo: the combo box
-
-        Returns the text in the combo box or an empty string if nothing is
-        displayed.
-        """
-
-        displayedText = None
-
-        # Find the text displayed in the combo box.  This is either:
-        #
-        # 1) The last text object that's a child of the combo box
-        # 2) The selected child of the combo box.
-        # 3) The contents of the text of the combo box itself when
-        #    treated as a text object.
-        #
-        # Preference is given to #1, if it exists.
-        #
-        # If the label of the combo box is the same as the utterance for
-        # the child object, then this utterance is only displayed once.
-        #
-        # [[[TODO: WDW - Combo boxes are complex beasts.  This algorithm
-        # needs serious work.  Logged as bugzilla bug 319745.]]]
-        #
-        textObj = None
-        for child in combo:
-            if child and child.getRole() == pyatspi.ROLE_TEXT:
-                textObj = child
-
-        if textObj:
-            [displayedText, caretOffset, startOffset] = \
-                self._script.getTextLineAtCaret(textObj)
-            #print "TEXTOBJ", displayedText
-        else:
-            try:
-                comboSelection = combo.querySelection()
-                selectedItem = comboSelection.getSelectedChild(0)
-            except:
-                selectedItem = None
-
-            if selectedItem:
-                displayedText = self.displayedText(selectedItem)
-                #print "SELECTEDITEM", displayedText
-                if displayedText:
-                    return displayedText
-            if combo.name and len(combo.name):
-                # We give preference to the name over the text because
-                # the text for combo boxes seems to never change in
-                # some cases.  The main one where we see this is in
-                # the gaim "Join Chat" window.
-                #
-                displayedText = combo.name
-                #print "NAME", displayedText
-            else:
-                [displayedText, caretOffset, startOffset] = \
-                    self._script.getTextLineAtCaret(combo)
-                # Set to None instead of empty string.
-                displayedText = displayedText or None
-                #print "TEXT", displayedText
-
-        return displayedText
-
     def displayedText(self, obj):
         """Returns the text being displayed for an object.
 
@@ -567,48 +500,14 @@ class Utilities:
         if role == pyatspi.ROLE_PUSH_BUTTON and obj.name:
             return obj.name
 
-        if role == pyatspi.ROLE_COMBO_BOX:
-            displayedText = self._displayedTextInComboBox(obj)
-            if self.DISPLAYED_TEXT not in self._script.generatorCache:
-                self._script.generatorCache[self.DISPLAYED_TEXT] = {}
-            self._script.generatorCache[self.DISPLAYED_TEXT][obj] = \
-                displayedText
-            return self._script.generatorCache[self.DISPLAYED_TEXT][obj]
-
-        # The accessible text of an object is used to represent what is
-        # drawn on the screen.
-        #
         try:
             text = obj.queryText()
-        except NotImplementedError:
+            displayedText = text.getText(0, text.characterCount)
+        except:
             pass
         else:
-            displayedText = text.getText(0, text.characterCount)
-
-            # [[[WDW - HACK to account for things such as Gecko that want
-            # to use the EMBEDDED_OBJECT_CHARACTER on a label to hold the
-            # object that has the real accessible text for the label.  We
-            # detect this by the specfic case where the text for the
-            # current object is a single EMBEDDED_OBJECT_CHARACTER.  In
-            # this case, we look to the child for the real text.]]]
-            #
-            if len(displayedText) == 1 \
-               and displayedText[0] == self.EMBEDDED_OBJECT_CHARACTER \
-               and obj.childCount > 0:
-                try:
-                    displayedText = self.displayedText(obj[0])
-                except:
-                    debug.printException(debug.LEVEL_WARNING)
-            else:
-                # [[[TODO: HACK - Welll.....we'll just plain ignore any
-                # text with EMBEDDED_OBJECT_CHARACTERs here.  We still need a
-                # general case to handle this stuff and expand objects
-                # with EMBEDDED_OBJECT_CHARACTERs.]]]
-                #
-                for i in range(len(displayedText)):
-                    if displayedText[i] == self.EMBEDDED_OBJECT_CHARACTER:
-                        displayedText = None
-                        break
+            if self.EMBEDDED_OBJECT_CHARACTER in displayedText:
+                displayedText = None
 
         if not displayedText:
             # TODO - JD: This should probably get nuked. But all sorts of
@@ -706,6 +605,10 @@ class Utilities:
         to routing the cursor.
         """
 
+        if obj and obj.getRole() == pyatspi.ROLE_COMBO_BOX \
+           and not self.isSameObject(obj, orca_state.locusOfFocus):
+            return True
+
         return False
 
     def hasMatchingHierarchy(self, obj, rolesList):
@@ -782,29 +685,44 @@ class Utilities:
         """Returns True if the given object is a container which has
         no presentable information (label, name, displayed text, etc.)."""
 
-        if self.isHidden(obj):
-            return True
-
         layoutOnly = False
 
+        if obj and self.isZombie(obj):
+            return True
+
         try:
-            attributes = obj.getAttributes()
+            attrs = dict([attr.split(':', 1) for attr in obj.getAttributes()])
         except:
-            attributes = {}
+            attrs = {}
         try:
             role = obj.getRole()
+            parentRole = obj.parent.getRole()
         except:
             role = None
+            parentRole = None
+
+        topLevelRoles = [pyatspi.ROLE_ALERT,
+                         pyatspi.ROLE_FRAME,
+                         pyatspi.ROLE_DIALOG,
+                         pyatspi.ROLE_WINDOW]
+        ignorePanelParent = [pyatspi.ROLE_MENU,
+                             pyatspi.ROLE_MENU_ITEM,
+                             pyatspi.ROLE_LIST_ITEM,
+                             pyatspi.ROLE_TREE_ITEM]
 
         if role == pyatspi.ROLE_TABLE:
-            for attribute in attributes:
-                if attribute == "layout-guess:true":
+            layoutOnly = attrs.get('layout-guess') == 'true'
+            if not layoutOnly:
+                try:
+                    table = obj.queryTable()
+                except:
                     layoutOnly = True
-                    break
+                else:
+                    layoutOnly = table.nRows <= 1 or table.nColumns <= 1
         elif role == pyatspi.ROLE_TABLE_CELL and obj.childCount:
             if obj[0].getRole() == pyatspi.ROLE_TABLE_CELL:
                 layoutOnly = True
-            elif obj.parent.getRole() == pyatspi.ROLE_TABLE:
+            elif parentRole == pyatspi.ROLE_TABLE:
                 layoutOnly = self.isLayoutOnly(obj.parent)
         elif role == pyatspi.ROLE_SECTION:
             layoutOnly = True
@@ -814,14 +732,25 @@ class Utilities:
             layoutOnly = True
         elif role == pyatspi.ROLE_AUTOCOMPLETE:
             layoutOnly = True
+        elif role in [pyatspi.ROLE_TEAROFF_MENU_ITEM, pyatspi.ROLE_SEPARATOR]:
+            layoutOnly = True
         elif role in [pyatspi.ROLE_LIST_BOX, pyatspi.ROLE_TREE_TABLE]:
             layoutOnly = False
-        elif role in [pyatspi.ROLE_DIALOG, pyatspi.ROLE_WINDOW]:
+        elif role in topLevelRoles:
             layoutOnly = False
+        elif role == pyatspi.ROLE_MENU and parentRole == pyatspi.ROLE_COMBO_BOX:
+            layoutOnly = True
         elif role == pyatspi.ROLE_LIST or self.isTableRow(obj):
             state = obj.getState()
             layoutOnly = not (state.contains(pyatspi.STATE_FOCUSABLE) \
                               or state.contains(pyatspi.STATE_SELECTABLE))
+        elif role == pyatspi.ROLE_PANEL and obj.childCount \
+             and (obj.childCount == 1 or obj[0].getRole() in ignorePanelParent):
+            layoutOnly = True
+        elif obj.childCount == 1 and obj.name == obj[0].name:
+            layoutOnly = True
+        elif self.isHidden(obj):
+            layoutOnly = True
         else:
             if not (self.displayedText(obj) or self.displayedLabel(obj)):
                 layoutOnly = True
@@ -901,14 +830,16 @@ class Utilities:
 
         return path1[0:index] == path2[0:index]
 
-    def isSameObject(self, obj1, obj2, comparePaths=False):
+    def isSameObject(self, obj1, obj2, comparePaths=False, ignoreNames=False):
         if (obj1 == obj2):
             return True
         elif (not obj1) or (not obj2):
             return False
 
         try:
-            if (obj1.name != obj2.name) or (obj1.getRole() != obj2.getRole()):
+            if obj1.getRole() != obj2.getRole():
+                return False
+            if obj1.name != obj2.name and not ignoreNames:
                 return False
             if comparePaths and self._hasSamePath(obj1, obj2):
                 return True
@@ -2634,6 +2565,18 @@ class Utilities:
             if not self.isZombie(child):
                 children.append(child)
 
+        role = obj.getRole()
+        if role == pyatspi.ROLE_MENU and not children:
+            pred = lambda x: x and x.getState().contains(pyatspi.STATE_SELECTED)
+            children = pyatspi.findAllDescendants(obj, pred)
+
+        if role == pyatspi.ROLE_COMBO_BOX \
+           and children and children[0].getRole() == pyatspi.ROLE_MENU:
+            children = self.selectedChildren(children[0])
+            if not children and obj.name:
+                pred = lambda x: x and x.name == obj.name
+                children = pyatspi.findAllDescendants(obj, pred)
+
         return children
 
     def focusedChild(self, obj):
@@ -2734,11 +2677,15 @@ class Utilities:
         try:
             index = obj.getIndexInParent()
             state = obj.getState()
+            role = obj.getRole()
         except:
             debug.println(debug.LEVEL_INFO, "ZOMBIE: %s is null or dead" % obj)
             return True
 
-        if obj.getIndexInParent() == -1:
+        topLevelRoles = [pyatspi.ROLE_APPLICATION,
+                         pyatspi.ROLE_WINDOW,
+                         pyatspi.ROLE_FRAME]
+        if obj.getIndexInParent() == -1 and role not in topLevelRoles:
             debug.println(debug.LEVEL_INFO, "ZOMBIE: %s's index is -1" % obj)
             return True
         if state.contains(pyatspi.STATE_DEFUNCT):
@@ -2749,3 +2696,67 @@ class Utilities:
             return True
 
         return False
+
+    def findReplicant(self, root, obj):
+        if not (root and obj):
+            return None
+
+        # Given an broken table hierarchy, findDescendant can hang. And the
+        # reason we're here in the first place is to work around the app or
+        # toolkit killing accessibles. There's only so much we can do....
+        if root.getRole() == pyatspi.ROLE_TABLE:
+            return None
+
+        isSame = lambda x: x and self.isSameObject(
+            x, obj, comparePaths=True, ignoreNames=True)
+        if isSame(root):
+            replicant = root
+        else:
+            replicant = pyatspi.findDescendant(root, isSame)
+
+        msg = "HACK: Returning %s as replicant for Zombie %s" % (replicant, obj)
+        debug.println(debug.LEVEL_INFO, msg)
+        return replicant
+
+    def getFunctionalChildren(self, obj):
+        if not obj:
+            return None
+
+        result = []
+        pred = lambda r: r.getRelationType() == pyatspi.RELATION_NODE_PARENT_OF
+        relations = list(filter(pred, obj.getRelationSet()))
+        if relations:
+            result = [r.getTarget(i) for i in range(relations[0].getNTargets())]
+
+        return result or [child for child in obj]
+
+    def getFunctionalParent(self, obj):
+        if not obj:
+            return None
+
+        result = None
+        pred = lambda r: r.getRelationType() == pyatspi.RELATION_NODE_CHILD_OF
+        relations = list(filter(pred, obj.getRelationSet()))
+        if relations:
+            result = relations[0].getTarget(0)
+
+        return result or obj.parent
+
+    def getPositionAndSetSize(self, obj):
+        if not obj:
+            return -1, -1
+
+        if obj.getRole() == pyatspi.ROLE_COMBO_BOX:
+            selected = self.selectedChildren(obj)
+            if selected:
+                obj = selected[0]
+
+        parent = self.getFunctionalParent(obj)
+        siblings = self.getFunctionalChildren(parent)
+        siblings = list(filter(lambda x: not self.isLayoutOnly(x), siblings))
+        if not (siblings and obj in siblings):
+            return -1, -1
+
+        position = siblings.index(obj)
+        setSize = len(siblings)
+        return position, setSize
diff --git a/src/orca/scripts/default.py b/src/orca/scripts/default.py
index 5d2b874..1919cec 100644
--- a/src/orca/scripts/default.py
+++ b/src/orca/scripts/default.py
@@ -2236,7 +2236,9 @@ class Script(script.Script):
         """Callback for object:state-changed:expanded accessibility events."""
 
         obj = event.source
-        if not self.utilities.isSameObject(obj, orca_state.locusOfFocus):
+        role = obj.getRole()
+        if not self.utilities.isSameObject(obj, orca_state.locusOfFocus) \
+           and not (role == pyatspi.ROLE_COMBO_BOX and event.detail1):
             return
 
         oldObj, oldState = self.pointOfReference.get('expandedChange', (None, 0))
diff --git a/src/orca/scripts/toolkits/Gecko/braille_generator.py 
b/src/orca/scripts/toolkits/Gecko/braille_generator.py
index 772c7e0..eaeb61d 100644
--- a/src/orca/scripts/toolkits/Gecko/braille_generator.py
+++ b/src/orca/scripts/toolkits/Gecko/braille_generator.py
@@ -112,40 +112,7 @@ class BrailleGenerator(braille_generator.BrailleGenerator):
     def _generateName(self, obj, **args):
         result = []
         role = args.get('role', obj.getRole())
-        if role == pyatspi.ROLE_COMBO_BOX:
-            # With Gecko, a combo box has a menu as a child.  The text being
-            # displayed for the combo box can be obtained via the selected
-            # menu item.
-            #
-            menu = None
-            for child in obj:
-                if child.getRole() == pyatspi.ROLE_MENU:
-                    menu = child
-                    break
-            if menu:
-                child = None
-                try:
-                    # This should work...
-                    #
-                    child = menu.querySelection().getSelectedChild(0)
-                    if not child:
-                        # It's probably a Gtk combo box.
-                        #
-                        result = braille_generator.BrailleGenerator.\
-                            _generateDisplayedText(self, obj, **args)
-                except:
-                    # But just in case, we'll fall back on this.
-                    # [[[TODO - JD: Will we ever have a case where the first
-                    # fails, but this will succeed???]]]
-                    #
-                    for item in menu:
-                        if item.getState().contains(pyatspi.STATE_SELECTED):
-                            child = item
-                            break
-                if child and child.name:
-                    result.append(child.name)
-        else:
-            result.extend(braille_generator.BrailleGenerator._generateName(
+        result.extend(braille_generator.BrailleGenerator._generateName(
                               self, obj, **args))
         if not result and role == pyatspi.ROLE_LIST_ITEM:
             result.append(self._script.utilities.expandEOCs(obj))
diff --git a/src/orca/scripts/toolkits/Gecko/formatting.py b/src/orca/scripts/toolkits/Gecko/formatting.py
index 1af6928..257993f 100644
--- a/src/orca/scripts/toolkits/Gecko/formatting.py
+++ b/src/orca/scripts/toolkits/Gecko/formatting.py
@@ -46,21 +46,6 @@ formatting = {
             'basicWhereAmI': 'labelAndName + roleName',
             'detailedWhereAmI' : 'pageSummary'
             },
-        pyatspi.ROLE_ALERT: {
-            'unfocused': 'expandedEOCs or (labelOrName + unrelatedLabels)'
-            },
-        pyatspi.ROLE_DIALOG: {
-            'unfocused': 'expandedEOCs or (labelOrName + unrelatedLabels)'
-            },
-        # [[[TODO: JD - We should decide if we want to provide
-        # information about the table dimensions, whether or not
-        # this is a layout table versus a data table, etc.  For now,
-        # however, if it's in HTML content let's ignore it so that
-        # SayAll by sentence works. :-) ]]]
-        #
-        pyatspi.ROLE_TABLE: {
-            'unfocused': '[]'
-            },
     },
 }
 
diff --git a/src/orca/scripts/toolkits/Gecko/script.py b/src/orca/scripts/toolkits/Gecko/script.py
index ba90040..5529775 100644
--- a/src/orca/scripts/toolkits/Gecko/script.py
+++ b/src/orca/scripts/toolkits/Gecko/script.py
@@ -1140,7 +1140,7 @@ class Script(default.Script):
         # To make matters worse, the replacement object can be in the ancestry.
         obj, offset = self.getCaretContext()
         if obj and self.utilities.isZombie(obj):
-            replicant = self.utilities.findReplicant(event.any_data, obj)
+            replicant = self.utilities.findReplicant(event.source, obj)
             if replicant:
                 self.setCaretPosition(replicant, offset)
                 return
diff --git a/src/orca/scripts/toolkits/Gecko/script_utilities.py 
b/src/orca/scripts/toolkits/Gecko/script_utilities.py
index fe7403b..22a4538 100644
--- a/src/orca/scripts/toolkits/Gecko/script_utilities.py
+++ b/src/orca/scripts/toolkits/Gecko/script_utilities.py
@@ -1176,23 +1176,3 @@ class Utilities(script_utilities.Utilities):
                 return True
 
         return False
-
-    def findReplicant(self, root, obj):
-        if not (root and obj):
-            return None
-
-        # Given an broken table hierarchy, findDescendant can hang. And the
-        # reason we're here in the first place is to work around Gecko's
-        # killing the focused accessible. There's only so much we can do....
-        if root.getRole() == pyatspi.ROLE_TABLE:
-            return None
-
-        isSame = lambda x: x and self.isSameObject(x, obj, comparePaths=True)
-        if isSame(root):
-            replicant = root
-        else:
-            replicant = pyatspi.utils.findDescendant(root, isSame)
-
-        msg = "HACK: Returning %s as replicant for Zombie %s" % (replicant, obj)
-        debug.println(debug.LEVEL_INFO, msg)
-        return replicant
diff --git a/src/orca/scripts/toolkits/Gecko/speech_generator.py 
b/src/orca/scripts/toolkits/Gecko/speech_generator.py
index 30f237a..8d2e92a 100644
--- a/src/orca/scripts/toolkits/Gecko/speech_generator.py
+++ b/src/orca/scripts/toolkits/Gecko/speech_generator.py
@@ -71,42 +71,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         result = []
         acss = self.voice(speech_generator.DEFAULT)
         role = args.get('role', obj.getRole())
-        if role == pyatspi.ROLE_COMBO_BOX:
-            # With Gecko, a combo box has a menu as a child.  The text being
-            # displayed for the combo box can be obtained via the selected
-            # menu item.
-            #
-            menu = None
-            for child in obj:
-                if child.getRole() == pyatspi.ROLE_MENU:
-                    menu = child
-                    break
-            if menu:
-                child = None
-                try:
-                    # This should work...
-                    #
-                    child = menu.querySelection().getSelectedChild(0)
-                    if not child:
-                        # It's probably a Gtk combo box.
-                        #
-                        result = speech_generator.SpeechGenerator.\
-                            _generateDisplayedText(self, obj, **args)
-                except:
-                    # But just in case, we'll fall back on this.
-                    # [[[TODO - JD: Will we ever have a case where the first
-                    # fails, but this will succeed???]]]
-                    #
-                    for item in menu:
-                        if item.getState().contains(pyatspi.STATE_SELECTED):
-                            child = item
-                            break
-                if child and child.name:
-                    result.append(child.name)
-                    result.extend(acss)
-
-        else:
-            result.extend(speech_generator.SpeechGenerator._generateName(
+        result.extend(speech_generator.SpeechGenerator._generateName(
                               self, obj, **args))
         if not result and role == pyatspi.ROLE_LIST_ITEM:
             result.append(self._script.utilities.expandEOCs(obj))
@@ -210,17 +175,6 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
            and not self._script.utilities.isLink(obj):
             return []
 
-        # Saying "menu item" for a combo box can confuse users. Therefore,
-        # speak the combo box role instead.  Also, only do it if the menu
-        # item is not focused (if the menu item is focused, it means we're
-        # navigating in the combo box)
-        #
-        if not obj.getState().contains(pyatspi.STATE_FOCUSED):
-            comboBox = self._script.utilities.ancestorWithRole(
-                obj, [pyatspi.ROLE_COMBO_BOX], [pyatspi.ROLE_DOCUMENT_FRAME])
-            if comboBox:
-                return self._generateRoleName(comboBox, **args)
-
         if not force:
             doNotSpeak = [pyatspi.ROLE_FORM,
                           pyatspi.ROLE_LABEL,
@@ -420,33 +374,40 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             self, obj, **args)
 
     def _generatePositionInList(self, obj, **args):
+        if _settingsManager.getSetting('onlySpeakDisplayedText') \
+           or not (_settingsManager.getSetting('enablePositionSpeaking') \
+                   or args.get('forceList', False)):
+            return []
+
         if self._script.utilities.isTextBlockElement(obj):
             return []
 
+        menuRoles = [pyatspi.ROLE_MENU_ITEM,
+                     pyatspi.ROLE_CHECK_MENU_ITEM,
+                     pyatspi.ROLE_RADIO_MENU_ITEM,
+                     pyatspi.ROLE_MENU,
+                     pyatspi.ROLE_COMBO_BOX]
+        if obj.getRole() in menuRoles:
+            return super()._generatePositionInList(obj, **args)
+
         position = self.getAttribute(obj, "posinset")
         total = self.getAttribute(obj, "setsize")
-        if position and total and not obj.getRole() in \
-                [pyatspi.ROLE_MENU_ITEM,
-                 pyatspi.ROLE_TEAROFF_MENU_ITEM,
-                 pyatspi.ROLE_CHECK_MENU_ITEM,
-                 pyatspi.ROLE_RADIO_MENU_ITEM,
-                 pyatspi.ROLE_MENU]:
-            position = int(position)
-            total = int(total)
-            result = []
-            if (_settingsManager.getSetting('enablePositionSpeaking') \
-                or args.get('forceList', False)) \
-                and position >= 0:
-                result.append(self._script.formatting.getString(
-                    mode='speech',
-                    stringType='groupindex') \
-                    % {"index" : position,
-                    "total" : total})
-                result.extend(self.voice(speech_generator.SYSTEM))
-            return result
-        else:
-            return speech_generator.SpeechGenerator._generatePositionInList(
-                self, obj, **args)
+        if position is None or total is None:
+            return super()._generatePositionInList(obj, **args)
+
+        position = int(position)
+        total = int(total)
+        if position < 0 or total < 0:
+            return []
+
+        result = []
+        result.append(self._script.formatting.getString(
+            mode='speech',
+            stringType='groupindex') \
+            % {"index" : position,
+               "total" : total})
+        result.extend(self.voice(speech_generator.SYSTEM))
+        return result
 
     def _generateNewRadioButtonGroup(self, obj, **args):
         # TODO - JD: Looking at the default speech generator's method, this
diff --git a/src/orca/speech_generator.py b/src/orca/speech_generator.py
index 0470733..ee16de0 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -304,8 +304,16 @@ class SpeechGenerator(generator.Generator):
                         pyatspi.ROLE_FILLER,
                         pyatspi.ROLE_EXTENDED]
 
+        try:
+            parentRole = obj.parent.getRole()
+        except:
+            parentRole = None
+
+        if role == pyatspi.ROLE_MENU and parentRole == pyatspi.ROLE_COMBO_BOX:
+            return self._generateRoleName(obj.parent)
+
         # egg-list-box, e.g. privacy panel in gnome-control-center
-        if obj.parent and obj.parent.getRole() == pyatspi.ROLE_LIST_BOX:
+        if parentRole == pyatspi.ROLE_LIST_BOX:
             doNotPresent.append(obj.getRole())
 
         if _settingsManager.getSetting('speechVerbosityLevel') \
@@ -1599,7 +1607,7 @@ class SpeechGenerator(generator.Generator):
            and args.get('formatType', None) \
                in ['basicWhereAmI', 'detailedWhereAmI']:
             return [object_properties.ROLE_ICON_PANEL]
-        elif obj.parent.getRole() == pyatspi.ROLE_TABLE_CELL:
+        if obj.parent.getRole() in [pyatspi.ROLE_TABLE_CELL, pyatspi.ROLE_MENU]:
             obj = obj.parent
         return self._generateRoleName(obj.parent)
 
@@ -1660,6 +1668,7 @@ class SpeechGenerator(generator.Generator):
         specifications) that represent the relative position of an
         object in a list.
         """
+
         if _settingsManager.getSetting('onlySpeakDisplayedText') \
            or not (_settingsManager.getSetting('enablePositionSpeaking') \
                    or args.get('forceList', False)):
@@ -1667,80 +1676,17 @@ class SpeechGenerator(generator.Generator):
 
         result = []
         acss = self.voice(SYSTEM)
-        position = -1
-        index = 0
-        total = 0
-        name = self._generateName(obj)
-        # TODO - JD: There might be a better way to do this (e.g. pass
-        # roles in maybe?).
-        #
-        role = args.get('role', obj.getRole())
-        if role == pyatspi.ROLE_COMBO_BOX:
-            obj = obj[0]
-        elif role in [pyatspi.ROLE_PAGE_TAB,
-                      pyatspi.ROLE_MENU,
-                      pyatspi.ROLE_MENU_ITEM,
-                      pyatspi.ROLE_CHECK_MENU_ITEM,
-                      pyatspi.ROLE_RADIO_MENU_ITEM]:
-            obj = obj.parent
-        elif role == pyatspi.ROLE_LIST_ITEM:
-            parent = obj.parent
-            for relation in obj.getRelationSet():
-                if relation.getRelationType() == \
-                        pyatspi.RELATION_NODE_CHILD_OF:
-                    # childNodes assumes that we have an accessible table
-                    # interface to work with. If we don't, it will fail. So
-                    # don't set the parent until verifying the interface we
-                    # expect actually exists.
-                    #
-                    target = relation.getTarget(0)
-                    try:
-                        target.parent.queryTable()
-                    except:
-                        pass
-                    else:
-                        parent = target
-                    break
-            obj = parent
-        else:
-            obj = obj.parent
-
-        # We want to return the position relative to this hierarchical
-        # level and not the entire list.  If the object in question
-        # uses the NODE_CHILD_OF relationship, we need to use it instead
-        # of the childCount.
-        #
-        childNodes = self._script.utilities.childNodes(obj)
-        total = len(childNodes)
-        for i in range(0, total):
-            childName = self._generateName(childNodes[i])
-            if childName == name:
-                position = i+1
-                break
-
-        if not total:
-            for child in obj:
-                nextName = self._generateName(child)
-                state = child.getState()
-                if not nextName or nextName[0] in ["", "Empty", "separator"] \
-                   or not state.contains(pyatspi.STATE_VISIBLE):
-                    continue
-
-                index += 1
-                total += 1
-
-                if nextName == name:
-                    position = index
+        position, total = self._script.utilities.getPositionAndSetSize(obj)
+        if position < 0 or total < 0:
+            return []
 
-        if (_settingsManager.getSetting('enablePositionSpeaking') \
-             or args.get('forceList', False)) \
-           and position >= 0:
-            result.append(self._script.formatting.getString(
+        position += 1
+        result.append(self._script.formatting.getString(
                               mode='speech',
                               stringType='groupindex') \
                           % {"index" : position,
                              "total" : total})
-            result.extend(acss)
+        result.extend(acss)
         return result
 
     def _generateDefaultButton(self, obj, **args):
diff --git a/test/keystrokes/firefox/aria_button_dojo.py b/test/keystrokes/firefox/aria_button_dojo.py
index 40edd45..7eba4a3 100644
--- a/test/keystrokes/firefox/aria_button_dojo.py
+++ b/test/keystrokes/firefox/aria_button_dojo.py
@@ -87,7 +87,6 @@ sequence.append(utils.AssertPresentationAction(
      "     VISIBLE:  'Create blank', cursor=1",
      "BRAILLE LINE:  'Focus mode'",
      "     VISIBLE:  'Focus mode', cursor=0",
-     "SPEECH OUTPUT: 'createMenu panel'",
      "SPEECH OUTPUT: 'Create blank'",
      "SPEECH OUTPUT: 'Focus mode' voice=system"]))
 
@@ -132,7 +131,6 @@ sequence.append(utils.AssertPresentationAction(
      "     VISIBLE:  'Cut', cursor=1",
      "BRAILLE LINE:  'Focus mode'",
      "     VISIBLE:  'Focus mode', cursor=0",
-     "SPEECH OUTPUT: 'Edit! edit title panel'",
      "SPEECH OUTPUT: 'Edit! menu'",
      "SPEECH OUTPUT: 'Cut'",
      "SPEECH OUTPUT: 'Focus mode' voice=system"]))
@@ -169,7 +167,6 @@ sequence.append(utils.AssertPresentationAction(
     "18. Open Submenu",
     ["BRAILLE LINE:  'Submenu Item One'",
      "     VISIBLE:  'Submenu Item One', cursor=1",
-     "SPEECH OUTPUT: 'Submenu panel'",
      "SPEECH OUTPUT: 'Submenu menu'",
      "SPEECH OUTPUT: 'Submenu Item One'"]))
 
@@ -193,11 +190,8 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Escape"))
 sequence.append(utils.AssertPresentationAction(
     "21. Close the Submenu",
-    ["KNOWN ISSUE: Too much speech context",
-     "BRAILLE LINE:  'Submenu Submenu menu'",
-     "     VISIBLE:  'Submenu Submenu menu', cursor=9",
-     "BRAILLE LINE:  'Focus mode'",
-     "     VISIBLE:  'Focus mode', cursor=0",
+    ["BRAILLE LINE:  'Submenu menu'",
+     "     VISIBLE:  'Submenu menu', cursor=1",
      "SPEECH OUTPUT: 'Edit! edit title panel'",
      "SPEECH OUTPUT: 'Edit! menu'",
      "SPEECH OUTPUT: 'Submenu menu'",
@@ -236,7 +230,6 @@ sequence.append(utils.AssertPresentationAction(
      "     VISIBLE:  'white table cell', cursor=1",
      "BRAILLE LINE:  'Focus mode'",
      "     VISIBLE:  'Focus mode', cursor=0",
-     "SPEECH OUTPUT: 'Color panel'",
      "SPEECH OUTPUT: 'white'",
      "SPEECH OUTPUT: 'Focus mode' voice=system"]))
 
diff --git a/test/keystrokes/firefox/aria_combobox_dojo.py b/test/keystrokes/firefox/aria_combobox_dojo.py
index 4c6381e..3d566e8 100644
--- a/test/keystrokes/firefox/aria_combobox_dojo.py
+++ b/test/keystrokes/firefox/aria_combobox_dojo.py
@@ -29,11 +29,13 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(TypeAction("C"))
 sequence.append(utils.AssertPresentationAction(
     "2. Replace existing text with a 'C'",
-    ["KNOWN ISSUE: It would be good to present the appearance of the popup so one knows there's something to 
Down arrow into.",
-     "BRAILLE LINE:  'US State test 1 (200% Courier font): C $l'",
+    ["BRAILLE LINE:  'US State test 1 (200% Courier font): C $l'",
      "     VISIBLE:  '(200% Courier font): C $l', cursor=23",
      "BRAILLE LINE:  'US State test 1 (200% Courier font): C $l'",
-     "     VISIBLE:  '(200% Courier font): C $l', cursor=23"]))
+     "     VISIBLE:  '(200% Courier font): C $l', cursor=23",
+     "BRAILLE LINE:  'US State test 1 (200% Courier font):'",
+     "     VISIBLE:  'US State test 1 (200% Courier fo', cursor=1",
+     "SPEECH OUTPUT: 'expanded'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Down"))
@@ -41,15 +43,14 @@ sequence.append(utils.AssertPresentationAction(
     "3. Down Arrow",
     ["KNOWN ISSUE: Too much braille updating",
      "BRAILLE LINE:  'US State test 1 (200% Courier font): California $l'",
-     "     VISIBLE:  '(200% Courier font): California ', cursor=21",
+     "     VISIBLE:  'ate test 1 (200% Courier font): ', cursor=32",
      "BRAILLE LINE:  'US State test 1 (200% Courier font): California $l'",
-     "     VISIBLE:  '(200% Courier font): California ', cursor=21",
+     "     VISIBLE:  'ate test 1 (200% Courier font): ', cursor=32",
      "BRAILLE LINE:  'California (CA)'",
      "     VISIBLE:  'California (CA)', cursor=1",
      "BRAILLE LINE:  'US State test 1 (200% Courier font): California $l'",
      "     VISIBLE:  'ate test 1 (200% Courier font): ', cursor=32",
      "SPEECH OUTPUT: 'California'",
-     "SPEECH OUTPUT: 'California panel'",
      "SPEECH OUTPUT: 'California List with 3 items'",
      "SPEECH OUTPUT: 'California (CA)'"]))
 
diff --git a/test/keystrokes/firefox/aria_editor_navigation_dojo.py 
b/test/keystrokes/firefox/aria_editor_navigation_dojo.py
index 4512b2a..111144b 100644
--- a/test/keystrokes/firefox/aria_editor_navigation_dojo.py
+++ b/test/keystrokes/firefox/aria_editor_navigation_dojo.py
@@ -12,9 +12,8 @@ sequence.append(PauseAction(3000))
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Up"))
 sequence.append(utils.AssertPresentationAction(
-    "1. Up Arrow in Focus Mode",
-    ["BRAILLE LINE:  ' $l'",
-     "     VISIBLE:  ' $l', cursor=0"]))
+    "1. Up Arrow in Focus Mode - which should do nothing",
+    [""]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyPressAction(0, None, "KP_Insert"))
@@ -31,7 +30,7 @@ sequence.append(KeyComboAction("Up"))
 sequence.append(utils.AssertPresentationAction(
     "3. Up Arrow in Browse Mode",
     ["BRAILLE LINE:  ' $l'",
-     "     VISIBLE:  ' $l', cursor=0",
+     "     VISIBLE:  ' $l', cursor=1",
      "BRAILLE LINE:  'editor0 tool bar'",
      "     VISIBLE:  'editor0 tool bar', cursor=1",
      "SPEECH OUTPUT: 'editor0 tool bar'"]))
diff --git a/test/keystrokes/firefox/aria_grid_uiuc.py b/test/keystrokes/firefox/aria_grid_uiuc.py
index fafeb82..cd1211d 100644
--- a/test/keystrokes/firefox/aria_grid_uiuc.py
+++ b/test/keystrokes/firefox/aria_grid_uiuc.py
@@ -17,6 +17,7 @@ sequence.append(utils.AssertPresentationAction(
     "1. Tab to grid",
     ["BRAILLE LINE:  'Selected Sort Sel column push button'",
      "     VISIBLE:  'Selected Sort Sel column push bu', cursor=1",
+     "SPEECH OUTPUT: 'Inbox table with 6 rows 9 columns'",
      "SPEECH OUTPUT: 'Selected Sort Sel column push button'"]))
 
 sequence.append(utils.StartRecordingAction())
diff --git a/test/keystrokes/firefox/aria_tree_dojo.py b/test/keystrokes/firefox/aria_tree_dojo.py
index 5cc7894..ea98ea1 100644
--- a/test/keystrokes/firefox/aria_tree_dojo.py
+++ b/test/keystrokes/firefox/aria_tree_dojo.py
@@ -25,12 +25,10 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Down"))
 sequence.append(utils.AssertPresentationAction(
     "2. Down arrow to Africa",
-    ["KNOWN ISSUE: We shouldn't be speaking the panel here",
-     "BRAILLE LINE:  'Africa collapsed list item'",
+    ["BRAILLE LINE:  'Africa collapsed list item'",
      "     VISIBLE:  'Africa collapsed list item', cursor=1",
      "BRAILLE LINE:  'Africa collapsed list item'",
      "     VISIBLE:  'Africa collapsed list item', cursor=1",
-     "SPEECH OUTPUT: 'Continents panel'",
      "SPEECH OUTPUT: 'Africa collapsed tree level 2'"]))
 
 sequence.append(utils.StartRecordingAction())
@@ -53,12 +51,10 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Down"))
 sequence.append(utils.AssertPresentationAction(
     "5. Down arrow to Egypt",
-    ["KNOWN ISSUE: We shouldn't be speaking the panel here",
-     "BRAILLE LINE:  'Egypt list item'",
+    ["BRAILLE LINE:  'Egypt list item'",
      "     VISIBLE:  'Egypt list item', cursor=1",
      "BRAILLE LINE:  'Egypt list item'",
      "     VISIBLE:  'Egypt list item', cursor=1",
-     "SPEECH OUTPUT: 'Africa panel'",
      "SPEECH OUTPUT: 'Egypt tree level 3'"]))
 
 sequence.append(utils.StartRecordingAction())
@@ -119,12 +115,10 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Down"))
 sequence.append(utils.AssertPresentationAction(
     "12. Down arrow to China",
-    ["KNOWN ISSUE: We shouldn't be speaking the panel here",
-     "BRAILLE LINE:  'China list item'",
+    ["BRAILLE LINE:  'China list item'",
      "     VISIBLE:  'China list item', cursor=1",
      "BRAILLE LINE:  'China list item'",
      "     VISIBLE:  'China list item', cursor=1",
-     "SPEECH OUTPUT: 'Asia panel'",
      "SPEECH OUTPUT: 'China tree level 3'"]))
 
 sequence.append(utils.StartRecordingAction())
diff --git a/test/keystrokes/firefox/aria_treegrid.py b/test/keystrokes/firefox/aria_treegrid.py
index a0f23f6..2558fa0 100644
--- a/test/keystrokes/firefox/aria_treegrid.py
+++ b/test/keystrokes/firefox/aria_treegrid.py
@@ -66,7 +66,7 @@ sequence.append(utils.AssertPresentationAction(
     "6. basic whereAmI",
     ["BRAILLE LINE:  '+A Question of Love table cell'",
      "     VISIBLE:  '+A Question of Love table cell', cursor=1",
-     "SPEECH OUTPUT: 'table row +A Question of Love'"]))
+     "SPEECH OUTPUT: 'table row ISBN +A Question of Love'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(TypeAction(" "))
@@ -84,7 +84,7 @@ sequence.append(utils.AssertPresentationAction(
     "8. basic whereAmI",
     ["BRAILLE LINE:  '-A Question of Love table cell'",
      "     VISIBLE:  '-A Question of Love table cell', cursor=1",
-     "SPEECH OUTPUT: 'table row -A Question of Love'"]))
+     "SPEECH OUTPUT: 'table row ISBN -A Question of Love'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Down"))
diff --git a/test/keystrokes/firefox/html_role_combo_box.py b/test/keystrokes/firefox/html_role_combo_box.py
index 0b1912c..1709cf2 100644
--- a/test/keystrokes/firefox/html_role_combo_box.py
+++ b/test/keystrokes/firefox/html_role_combo_box.py
@@ -28,7 +28,7 @@ sequence.append(utils.AssertPresentationAction(
      "     VISIBLE:  'Severity normal combo box', cursor=10",
      "BRAILLE LINE:  'Severity normal combo box'",
      "     VISIBLE:  'Severity normal combo box', cursor=10",
-     "SPEECH OUTPUT: 'Severity combo box normal'"]))
+     "SPEECH OUTPUT: 'Severity combo box normal 4 of 7'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Tab"))
diff --git a/test/keystrokes/firefox/html_struct_nav_links.py 
b/test/keystrokes/firefox/html_struct_nav_links.py
index 402da08..845e485 100644
--- a/test/keystrokes/firefox/html_struct_nav_links.py
+++ b/test/keystrokes/firefox/html_struct_nav_links.py
@@ -67,14 +67,8 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("u"))
 sequence.append(utils.AssertPresentationAction(
     "6. u to textattributes.html link",
-    ["BRAILLE LINE:  'Finished loading Links to test files.'",
-     "     VISIBLE:  'Finished loading Links to test f', cursor=0",
-     "BRAILLE LINE:  '• tables.html'",
-     "     VISIBLE:  '• tables.html', cursor=3",
-     "BRAILLE LINE:  '• textattributes.html'",
+    ["BRAILLE LINE:  '• textattributes.html'",
      "     VISIBLE:  '• textattributes.html', cursor=3",
-     "SPEECH OUTPUT: 'Finished loading Links to test files.' voice=system",
-     "SPEECH OUTPUT: 'tables.html link'",
      "SPEECH OUTPUT: 'textattributes.html'",
      "SPEECH OUTPUT: 'link'"]))
 
diff --git a/test/keystrokes/firefox/line_nav_follow_same_page_link.py 
b/test/keystrokes/firefox/line_nav_follow_same_page_link.py
index 9dec4b9..a85644a 100644
--- a/test/keystrokes/firefox/line_nav_follow_same_page_link.py
+++ b/test/keystrokes/firefox/line_nav_follow_same_page_link.py
@@ -39,8 +39,8 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Return"))
 sequence.append(utils.AssertPresentationAction(
     "4. Return",
-    ["BRAILLE LINE:  'seas. '",
-     "     VISIBLE:  'seas. ', cursor=7",
+    ["BRAILLE LINE:  ''",
+     "     VISIBLE:  '', cursor=1",
      "SPEECH OUTPUT: 'link'"]))
 
 sequence.append(utils.StartRecordingAction())
diff --git a/test/keystrokes/firefox/say_all_bugzilla_search.py 
b/test/keystrokes/firefox/say_all_bugzilla_search.py
index 558495d..5568b63 100644
--- a/test/keystrokes/firefox/say_all_bugzilla_search.py
+++ b/test/keystrokes/firefox/say_all_bugzilla_search.py
@@ -55,7 +55,6 @@ sequence.append(utils.AssertPresentationAction(
      "SPEECH OUTPUT: 'contains all of the words/strings'",
      "SPEECH OUTPUT: 'combo box'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Search'",
      "SPEECH OUTPUT: 'push button'",
      "SPEECH OUTPUT: 'Classification:'",
@@ -96,14 +95,12 @@ sequence.append(utils.AssertPresentationAction(
      "SPEECH OUTPUT: 'contains the string'",
      "SPEECH OUTPUT: 'combo box'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Whiteboard:'",
      "SPEECH OUTPUT: 'row header'",
      "SPEECH OUTPUT: 'Whiteboard:'",
      "SPEECH OUTPUT: 'contains all of the words/strings'",
      "SPEECH OUTPUT: 'combo box'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Keywords'",
      "SPEECH OUTPUT: 'link'",
      "SPEECH OUTPUT: 'Keywords :'",
@@ -112,7 +109,6 @@ sequence.append(utils.AssertPresentationAction(
      "SPEECH OUTPUT: 'contains all of the keywords'",
      "SPEECH OUTPUT: 'combo box'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'separator'",
      "SPEECH OUTPUT: 'Status:'",
      "SPEECH OUTPUT: 'column header'",
@@ -168,7 +164,6 @@ sequence.append(utils.AssertPresentationAction(
      "SPEECH OUTPUT: 'contains'",
      "SPEECH OUTPUT: 'combo box'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Any one of:'",
      "SPEECH OUTPUT: 'the bug assignee'",
      "SPEECH OUTPUT: 'check box'",
@@ -188,19 +183,16 @@ sequence.append(utils.AssertPresentationAction(
      "SPEECH OUTPUT: 'contains'",
      "SPEECH OUTPUT: 'combo box'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'separator'",
      "SPEECH OUTPUT: 'Only include'",
      "SPEECH OUTPUT: 'combo box'",
      "SPEECH OUTPUT: 'bugs numbered:'",
      "SPEECH OUTPUT: '(comma-separated list)'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Bug Changes'",
      "SPEECH OUTPUT: 'Only bugs changed between:'",
      "SPEECH OUTPUT: '(YYYY-MM-DD or relative dates)'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'and'",
      "SPEECH OUTPUT: 'entry'",
      "SPEECH OUTPUT: 'Now'",
@@ -210,7 +202,6 @@ sequence.append(utils.AssertPresentationAction(
      "SPEECH OUTPUT: 'List with 26 items'",
      "SPEECH OUTPUT: 'and the new value was:'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'GNOME version:'",
      "SPEECH OUTPUT: 'column header'",
      "SPEECH OUTPUT: 'GNOME version:'",
@@ -241,7 +232,6 @@ sequence.append(utils.AssertPresentationAction(
      "SPEECH OUTPUT: '---'",
      "SPEECH OUTPUT: 'combo box'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Or'",
      "SPEECH OUTPUT: 'push button'",
      "SPEECH OUTPUT: 'And'",
diff --git a/test/keystrokes/firefox/say_all_enter_bug.py b/test/keystrokes/firefox/say_all_enter_bug.py
index c9dac75..b3c67dd 100644
--- a/test/keystrokes/firefox/say_all_enter_bug.py
+++ b/test/keystrokes/firefox/say_all_enter_bug.py
@@ -89,30 +89,24 @@ sequence.append(utils.AssertPresentationAction(
      "SPEECH OUTPUT: 'Summary:'",
      "SPEECH OUTPUT: 'Summary:'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Description:'",
      "SPEECH OUTPUT: 'Description:'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Optional Fields'",
      "SPEECH OUTPUT: 'Cc:'",
      "SPEECH OUTPUT: 'Cc:'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Keywords'",
      "SPEECH OUTPUT: 'link'",
      "SPEECH OUTPUT: ':'",
      "SPEECH OUTPUT: 'Keywords:'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Depends on:'",
      "SPEECH OUTPUT: 'Depends on:'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Blocks:'",
      "SPEECH OUTPUT: 'Blocks:'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Commit'",
      "SPEECH OUTPUT: 'push button'",
      "SPEECH OUTPUT: 'Remember values as bookmarkable template'",
diff --git a/test/keystrokes/firefox/say_all_entries.py b/test/keystrokes/firefox/say_all_entries.py
index 996e577..de25a83 100644
--- a/test/keystrokes/firefox/say_all_entries.py
+++ b/test/keystrokes/firefox/say_all_entries.py
@@ -22,54 +22,41 @@ sequence.append(utils.AssertPresentationAction(
      "SPEECH OUTPUT: 'here:'",
      "SPEECH OUTPUT: 'Type something rather amusing here:'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Amusing numbers fall between'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'and'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: '.'",
      "SPEECH OUTPUT: 'I'm a label'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Am I a label as well?'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'What the heck should we do here?'",
      "SPEECH OUTPUT: 'heading level 2'",
      "SPEECH OUTPUT: 'Looking at what follows visually, I'm not sure what I would type/i.e.'",
      "SPEECH OUTPUT: 'what the labels are.'",
      "SPEECH OUTPUT: 'Looking at what follows visually, I'm not sure what I would type/i.e. what the labels 
are.'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Too far away to be a label.'",
      "SPEECH OUTPUT: 'Distance doesn't count on the left'",
      "SPEECH OUTPUT: 'Distance doesn't count on the left'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Sometimes labels can be below the fields due to <br />'",
      "SPEECH OUTPUT: 'heading level 2'",
      "SPEECH OUTPUT: 'First Name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'M.I.'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Last Name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Other times it's due to layout tables'",
      "SPEECH OUTPUT: 'heading level 2'",
      "SPEECH OUTPUT: 'First name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Middle initial'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Last name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Middle",
      "initial'",
      "SPEECH OUTPUT: 'Last'",
@@ -85,13 +72,10 @@ sequence.append(utils.AssertPresentationAction(
      "SPEECH OUTPUT: 'link'",
      "SPEECH OUTPUT: 'First Name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Middle initial'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Last name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Decisions, decisions.... When in doubt, closest table cell text wins'",
      "SPEECH OUTPUT: 'heading level 2'",
      "SPEECH OUTPUT: 'First name'",
@@ -100,13 +84,10 @@ sequence.append(utils.AssertPresentationAction(
      "SPEECH OUTPUT: 'Last name'",
      "SPEECH OUTPUT: 'Given name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'initial'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Surname'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'initial'",
      "SPEECH OUTPUT: 'Surname'",
      "SPEECH OUTPUT: 'First name'",
@@ -115,13 +96,10 @@ sequence.append(utils.AssertPresentationAction(
      "SPEECH OUTPUT: 'Last name'",
      "SPEECH OUTPUT: 'First name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Middle initial'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Last name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Given name'",
      "SPEECH OUTPUT: 'initial'",
      "SPEECH OUTPUT: 'Surname'",
@@ -134,55 +112,40 @@ sequence.append(utils.AssertPresentationAction(
      "SPEECH OUTPUT: 'patched image'",
      "SPEECH OUTPUT: 'First name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Middle initial'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Last name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'patched image'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'First name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Middle initial'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Last name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'patched image'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'First name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Middle initial'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Last name'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'patched image'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'We mustn't forget images as labels -- even if that practice is lame'",
      "SPEECH OUTPUT: 'heading level 2'",
      "SPEECH OUTPUT: 'bandaid graphic'",
      "SPEECH OUTPUT: 'bandaid graphic'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'bandaid graphic redux'",
      "SPEECH OUTPUT: 'entry'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Magic disappearing text trick:'",
      "SPEECH OUTPUT: 'entry'",
      "SPEECH OUTPUT: 'tab to me and I disappear'",
      "SPEECH OUTPUT: 'Tell me a secret:'",
      "SPEECH OUTPUT: 'password text'",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Tell me a little more about yourself:'",
      "SPEECH OUTPUT: 'entry'",
      "SPEECH OUTPUT: 'I",
diff --git a/test/keystrokes/firefox/ui_role_combo_box.py b/test/keystrokes/firefox/ui_role_combo_box.py
index e2abc5b..a722f3a 100644
--- a/test/keystrokes/firefox/ui_role_combo_box.py
+++ b/test/keystrokes/firefox/ui_role_combo_box.py
@@ -10,6 +10,8 @@ sequence = MacroSequence()
 sequence.append(KeyComboAction("<Alt>e"))
 sequence.append(KeyComboAction("Up"))
 sequence.append(KeyComboAction("Return"))
+sequence.append(KeyComboAction("Tab"))
+sequence.append(KeyComboAction("Tab"))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Tab"))
@@ -17,7 +19,6 @@ sequence.append(utils.AssertPresentationAction(
     "1. Tab to combobox",
     ["BRAILLE LINE:  'Firefox application Firefox Preferences dialog Startup When Firefox starts: Show a 
blank page combo box'",
      "     VISIBLE:  'When Firefox starts: Show a blan', cursor=22",
-     "SPEECH OUTPUT: 'Startup panel'",
      "SPEECH OUTPUT: 'When Firefox starts: Show a blank page combo box'"]))
 
 sequence.append(utils.StartRecordingAction())
@@ -40,14 +41,15 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("<Alt>Down"))
 sequence.append(utils.AssertPresentationAction(
     "4. Alt Down Arrow to expand combobox",
-    ["KNOWN ISSUE: We should present something here.",
-     ""]))
+    ["BRAILLE LINE:  'Firefox application Firefox Preferences dialog Startup When Firefox starts: Show a 
blank page combo box'",
+     "     VISIBLE:  'When Firefox starts: Show a blan', cursor=22",
+     "SPEECH OUTPUT: 'expanded'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Down"))
 sequence.append(utils.AssertPresentationAction(
     "5. Down Arrow in expanded combobox",
-    ["BRAILLE LINE:  'Firefox application Firefox Preferences dialog Startup  combo boxWhen Firefox starts: 
Show a blank page Show my windows and tabs from last time'",
+    ["BRAILLE LINE:  'Firefox application Firefox Preferences dialog Startup When Firefox starts: Show a 
blank page combo box Show my windows and tabs from last time'",
      "     VISIBLE:  'Show my windows and tabs from la', cursor=1",
      "SPEECH OUTPUT: 'Show my windows and tabs from last time'"]))
 
@@ -55,7 +57,7 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Up"))
 sequence.append(utils.AssertPresentationAction(
     "6. Up Arrow in expanded combobox",
-    ["BRAILLE LINE:  'Firefox application Firefox Preferences dialog Startup  combo boxWhen Firefox starts: 
Show a blank page Show a blank page'",
+    ["BRAILLE LINE:  'Firefox application Firefox Preferences dialog Startup When Firefox starts: Show a 
blank page combo box Show a blank page'",
      "     VISIBLE:  'Show a blank page', cursor=1",
      "SPEECH OUTPUT: 'Show a blank page'"]))
 
@@ -90,14 +92,12 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(utils.AssertPresentationAction(
     "10. Basic Where Am I",
-    ["KNOWN ISSUE: Crazy chattines",
-     "BRAILLE LINE:  'Firefox application Firefox Preferences dialog Startup When Firefox starts: Show a 
blank page combo box'",
+    ["BRAILLE LINE:  'Firefox application Firefox Preferences dialog Startup When Firefox starts: Show a 
blank page combo box'",
      "     VISIBLE:  'When Firefox starts: Show a blan', cursor=22",
      "SPEECH OUTPUT: 'Firefox Preferences'",
      "SPEECH OUTPUT: 'Startup panel'",
      "SPEECH OUTPUT: 'collapsed'",
-     "SPEECH OUTPUT: 'Show a blank page combo box'",
-     "SPEECH OUTPUT: 'Show a blank page 1 of 2'"]))
+     "SPEECH OUTPUT: 'combo box Show a blank page 2 of 3'"]))
 
 sequence.append(KeyComboAction("Escape"))
 
diff --git a/test/keystrokes/firefox/ui_role_menu_bar.py b/test/keystrokes/firefox/ui_role_menu_bar.py
index 29704fe..0261bc7 100644
--- a/test/keystrokes/firefox/ui_role_menu_bar.py
+++ b/test/keystrokes/firefox/ui_role_menu_bar.py
@@ -36,7 +36,7 @@ sequence.append(utils.AssertPresentationAction(
     ["BRAILLE LINE:  'Firefox application Mozilla Firefox frame Menu Bar tool bar Application menu bar New 
Tab(Ctrl+T)'",
      "     VISIBLE:  'New Tab(Ctrl+T)', cursor=1",
      "SPEECH OUTPUT: 'File menu'",
-     "SPEECH OUTPUT: 'Menu Bar tool bar New Tab Ctrl+T 1 of 12.'",
+     "SPEECH OUTPUT: 'Menu Bar tool bar menu bar New Tab Ctrl+T 1 of 12.'",
      "SPEECH OUTPUT: 'T'"]))
 
 sequence.append(KeyComboAction("Escape"))
diff --git a/test/keystrokes/gtk-demo/role_combo_box.py b/test/keystrokes/gtk-demo/role_combo_box.py
index 121d979..243ad57 100644
--- a/test/keystrokes/gtk-demo/role_combo_box.py
+++ b/test/keystrokes/gtk-demo/role_combo_box.py
@@ -17,7 +17,7 @@ sequence.append(utils.AssertPresentationAction(
     "1. Warning combo box item",
     ["BRAILLE LINE:  'gtk-demo application window'",
      "     VISIBLE:  'gtk-demo application window', cursor=22",
-     "BRAILLE LINE:  'gtk-demo application Combo boxes frame Some stock icons panel  combo boxWarning 
Warning'",
+     "BRAILLE LINE:  'gtk-demo application Combo boxes frame Some stock icons panel Warning combo box 
Warning'",
      "     VISIBLE:  'Warning', cursor=1",
      "SPEECH OUTPUT: 'window'",
      "SPEECH OUTPUT: 'Warning'"]))
@@ -26,17 +26,17 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(utils.AssertPresentationAction(
     "2. Warning combo box item Where Am I",
-    ["BRAILLE LINE:  'gtk-demo application Combo boxes frame Some stock icons panel  combo boxWarning 
Warning'",
+    ["BRAILLE LINE:  'gtk-demo application Combo boxes frame Some stock icons panel Warning combo box 
Warning'",
      "     VISIBLE:  'Warning', cursor=1",
      "SPEECH OUTPUT: 'Combo boxes frame'",
      "SPEECH OUTPUT: 'Some stock icons panel'",
-     "SPEECH OUTPUT: 'Warning 1 of 5'"]))
+     "SPEECH OUTPUT: 'combo box Warning 1 of 5'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Down"))
 sequence.append(utils.AssertPresentationAction(
     "3. New combo box item",
-    ["BRAILLE LINE:  'gtk-demo application Combo boxes frame Some stock icons panel  combo boxWarning New'",
+    ["BRAILLE LINE:  'gtk-demo application Combo boxes frame Some stock icons panel Warning combo box New'",
      "     VISIBLE:  'New', cursor=1",
      "SPEECH OUTPUT: 'New'"]))
 
@@ -44,12 +44,11 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(utils.AssertPresentationAction(
     "4. New combo box item Where Am I",
-    ["KNOWN ISSUE: We're a bit chatty here and have some spacing issues",
-     "BRAILLE LINE:  'gtk-demo application Combo boxes frame Some stock icons panel  combo boxWarning New'",
+    ["BRAILLE LINE:  'gtk-demo application Combo boxes frame Some stock icons panel Warning combo box New'",
      "     VISIBLE:  'New', cursor=1",
      "SPEECH OUTPUT: 'Combo boxes frame'",
      "SPEECH OUTPUT: 'Some stock icons panel'",
-     "SPEECH OUTPUT: 'New 3 of 5'"]))
+     "SPEECH OUTPUT: 'combo box New 3 of 5'"]))
 
 sequence.append(KeyComboAction("Escape"))
 sequence.append(KeyComboAction("Tab"))
@@ -115,31 +114,26 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Tab"))
 sequence.append(utils.AssertPresentationAction(
     "11. Combo box with multiple levels",
-    ["KNOWN ISSUE: This is broken",
-     "BRAILLE LINE:  'gtk-demo application Combo boxes frame Where are we ? panel A - B combo box'",
-     "     VISIBLE:  'A - B combo box', cursor=1",
+    ["BRAILLE LINE:  'gtk-demo application Combo boxes frame Where are we ? panel Boston combo box'",
+     "     VISIBLE:  'Boston combo box', cursor=1",
      "SPEECH OUTPUT: 'Where are we ? panel'",
-     "SPEECH OUTPUT: 'A - B combo box'"]))
+     "SPEECH OUTPUT: 'Boston combo box'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Down"))
 sequence.append(utils.AssertPresentationAction(
     "12. Down arrow",
-    ["KNOWN ISSUE: This is broken",
-     "BRAILLE LINE:  'gtk-demo application Combo boxes frame Where are we ? panel  combo boxC - D C - D 
menu'",
-     "     VISIBLE:  'C - D menu', cursor=1",
-     "SPEECH OUTPUT: 'C - D menu'"]))
+    ["KNOWN ISSUE: Gtk+ has missing events and a broken selection interface in multi-level combo boxes",
+     ""]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(utils.AssertPresentationAction(
-    "12. Where Am I",
-    ["KNOWN ISSUE: This is broken",
-     "BRAILLE LINE:  'gtk-demo application Combo boxes frame Where are we ? panel  combo boxC - D C - D 
menu'",
-     "     VISIBLE:  'C - D menu', cursor=1",
-     "SPEECH OUTPUT: 'Combo boxes frame'",
-     "SPEECH OUTPUT: 'Where are we ? panel'",
-     "SPEECH OUTPUT: 'C - D menu 2 of 5'"]))
+    "13. Where Am I",
+    ["KNOWN ISSUE: The count is wrong due to a Gtk+ 2 bug which has been fixed in Gtk+ 3",
+     "BRAILLE LINE:  'gtk-demo application Combo boxes frame Where are we ? panel Carson City combo box'",
+     "     VISIBLE:  'Carson City combo box', cursor=1",
+     "SPEECH OUTPUT: 'combo box Carson City 1 of 1'"]))
 
 sequence.append(KeyComboAction("<Alt>F4"))
 sequence.append(utils.AssertionSummaryAction())
diff --git a/test/keystrokes/gtk-demo/role_combo_box2.py b/test/keystrokes/gtk-demo/role_combo_box2.py
index 1766e89..65be1aa 100644
--- a/test/keystrokes/gtk-demo/role_combo_box2.py
+++ b/test/keystrokes/gtk-demo/role_combo_box2.py
@@ -19,7 +19,7 @@ sequence.append(KeyComboAction("<Alt>o"))
 sequence.append(utils.AssertPresentationAction(
     "1. Combo box",
     ["BRAILLE LINE:  'gtk-demo application Print dialog Page Setup page tab Only print: All sheets combo 
box'",
-     "     VISIBLE:  'Only print: All sheets combo box', cursor=13",
+     "     VISIBLE:  'Only print: All sheets combo box', cursor=1",
      "SPEECH OUTPUT: 'Only print: All sheets combo box'"]))
 
 sequence.append(utils.StartRecordingAction())
@@ -27,7 +27,7 @@ sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(utils.AssertPresentationAction(
     "2. Where Am I",
     ["BRAILLE LINE:  'gtk-demo application Print dialog Page Setup page tab Only print: All sheets combo 
box'",
-     "     VISIBLE:  'Only print: All sheets combo box', cursor=13",
+     "     VISIBLE:  'Only print: All sheets combo box', cursor=1",
      "SPEECH OUTPUT: 'Only print: combo box All sheets 1 of 3.'",
      "SPEECH OUTPUT: 'Alt+O'"]))
 
@@ -35,7 +35,7 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Down"))
 sequence.append(utils.AssertPresentationAction(
     "3. Change selection",
-    ["BRAILLE LINE:  'gtk-demo application Print dialog Page Setup page tab  combo boxOnly print: Even 
sheets Even sheets'",
+    ["BRAILLE LINE:  'gtk-demo application Print dialog Page Setup page tab Only print: Even sheets combo 
box Even sheets'",
      "     VISIBLE:  'Even sheets', cursor=1",
      "SPEECH OUTPUT: 'Even sheets'"]))
 
@@ -43,12 +43,11 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(utils.AssertPresentationAction(
     "4. Where Am I",
-    ["KNOWN ISSUE: The combo box role is missing and spacing is wrong in braille",
-     "BRAILLE LINE:  'gtk-demo application Print dialog Page Setup page tab  combo boxOnly print: Even 
sheets Even sheets'",
+    ["BRAILLE LINE:  'gtk-demo application Print dialog Page Setup page tab Only print: Even sheets combo 
box Even sheets'",
      "     VISIBLE:  'Even sheets', cursor=1",
      "SPEECH OUTPUT: 'Print'",
      "SPEECH OUTPUT: 'Page Setup page tab'",
-     "SPEECH OUTPUT: 'Even sheets 2 of 3'"]))
+     "SPEECH OUTPUT: 'combo box Even sheets 2 of 3'"]))
 
 sequence.append(KeyComboAction("<Alt>F4"))
 
diff --git a/test/keystrokes/gtk3-demo/role_combo_box.py b/test/keystrokes/gtk3-demo/role_combo_box.py
index 427c977..970591a 100644
--- a/test/keystrokes/gtk3-demo/role_combo_box.py
+++ b/test/keystrokes/gtk3-demo/role_combo_box.py
@@ -36,7 +36,7 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Down"))
 sequence.append(utils.AssertPresentationAction(
     "3. New combo box item",
-    ["BRAILLE LINE:  'gtk3-demo application Combo boxes frame Items with icons panel  combo boxNew New'",
+    ["BRAILLE LINE:  'gtk3-demo application Combo boxes frame Items with icons panel New combo box New'",
      "     VISIBLE:  'New', cursor=1",
      "SPEECH OUTPUT: 'New'"]))
 
@@ -44,12 +44,11 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(utils.AssertPresentationAction(
     "4. New combo box item Where Am I",
-    ["KNOWN ISSUE: The combo box role is missing and spacing is wrong in braille",
-     "BRAILLE LINE:  'gtk3-demo application Combo boxes frame Items with icons panel  combo boxNew New'",
+    ["BRAILLE LINE:  'gtk3-demo application Combo boxes frame Items with icons panel New combo box New'",
      "     VISIBLE:  'New', cursor=1",
      "SPEECH OUTPUT: 'Combo boxes frame'",
      "SPEECH OUTPUT: 'Items with icons panel'",
-     "SPEECH OUTPUT: 'New 3 of 5'"]))
+     "SPEECH OUTPUT: 'combo box New 3 of 5'"]))
 
 sequence.append(KeyComboAction("Tab"))
 
@@ -115,31 +114,29 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Tab"))
 sequence.append(utils.AssertPresentationAction(
     "11. Combo box with multiple levels",
-    ["KNOWN ISSUE: This is broken",
-     "BRAILLE LINE:  'gtk3-demo application Combo boxes frame Where are we ? panel A - B combo box'",
-     "     VISIBLE:  'A - B combo box', cursor=1",
+    ["BRAILLE LINE:  'gtk3-demo application Combo boxes frame Where are we ? panel Boston combo box'",
+     "     VISIBLE:  'Boston combo box', cursor=1",
      "SPEECH OUTPUT: 'Where are we ? panel'",
-     "SPEECH OUTPUT: 'A - B combo box'"]))
+     "SPEECH OUTPUT: 'Boston combo box'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Down"))
 sequence.append(utils.AssertPresentationAction(
     "12. Down arrow",
-    ["KNOWN ISSUE: This is broken",
-     "BRAILLE LINE:  'gtk3-demo application Combo boxes frame Where are we ? panel  combo boxC - D C - D 
menu'",
-     "     VISIBLE:  'C - D menu', cursor=1",
-     "SPEECH OUTPUT: 'C - D menu'"]))
+    ["BRAILLE LINE:  'gtk3-demo application Combo boxes frame Where are we ? panel Carson City combo box 
Carson City'",
+     "     VISIBLE:  'Carson City', cursor=1",
+     "SPEECH OUTPUT: 'Carson City'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(utils.AssertPresentationAction(
-    "12. Where Am I",
-    ["KNOWN ISSUE: This is broken",
-     "BRAILLE LINE:  'gtk3-demo application Combo boxes frame Where are we ? panel  combo boxC - D C - D 
menu'",
-     "     VISIBLE:  'C - D menu', cursor=1",
+    "13. Where Am I",
+    ["BRAILLE LINE:  'gtk3-demo application Combo boxes frame Where are we ? panel Carson City combo box 
Carson City'",
+     "     VISIBLE:  'Carson City', cursor=1",
      "SPEECH OUTPUT: 'Combo boxes frame'",
      "SPEECH OUTPUT: 'Where are we ? panel'",
-     "SPEECH OUTPUT: 'C - D menu 2 of 5'"]))
+     "SPEECH OUTPUT: 'C - D menu'",
+     "SPEECH OUTPUT: 'combo box Carson City 2 of 10'"]))
 
 sequence.append(KeyComboAction("<Alt>F4"))
 
diff --git a/test/keystrokes/gtk3-demo/role_combo_box2.py b/test/keystrokes/gtk3-demo/role_combo_box2.py
index 6102624..2a76bec 100644
--- a/test/keystrokes/gtk3-demo/role_combo_box2.py
+++ b/test/keystrokes/gtk3-demo/role_combo_box2.py
@@ -19,7 +19,7 @@ sequence.append(KeyComboAction("<Alt>o"))
 sequence.append(utils.AssertPresentationAction(
     "1. Combo box",
     ["BRAILLE LINE:  'gtk3-demo application Print dialog Page Setup page tab Layout panel Only print: All 
sheets combo box'",
-     "     VISIBLE:  'Only print: All sheets combo box', cursor=13",
+     "     VISIBLE:  'Only print: All sheets combo box', cursor=1",
      "SPEECH OUTPUT: 'Layout panel'",
      "SPEECH OUTPUT: 'Only print: All sheets combo box'"]))
 
@@ -28,7 +28,7 @@ sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(utils.AssertPresentationAction(
     "2. Where Am I",
     ["BRAILLE LINE:  'gtk3-demo application Print dialog Page Setup page tab Layout panel Only print: All 
sheets combo box'",
-     "     VISIBLE:  'Only print: All sheets combo box', cursor=13",
+     "     VISIBLE:  'Only print: All sheets combo box', cursor=1",
      "SPEECH OUTPUT: 'Only print: combo box All sheets 1 of 3.'",
      "SPEECH OUTPUT: 'Alt+O'"]))
 
@@ -36,7 +36,7 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Down"))
 sequence.append(utils.AssertPresentationAction(
     "3. Change selection",
-    ["BRAILLE LINE:  'gtk3-demo application Print dialog Page Setup page tab Layout panel  combo boxOnly 
print: Even sheets Even sheets'",
+    ["BRAILLE LINE:  'gtk3-demo application Print dialog Page Setup page tab Layout panel Only print: Even 
sheets combo box Even sheets'",
      "     VISIBLE:  'Even sheets', cursor=1",
      "SPEECH OUTPUT: 'Even sheets'"]))
 
@@ -44,13 +44,12 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(utils.AssertPresentationAction(
     "4. Where Am I",
-    ["KNOWN ISSUE: The combo box role is missing and spacing is wrong in braille",
-     "BRAILLE LINE:  'gtk3-demo application Print dialog Page Setup page tab Layout panel  combo boxOnly 
print: Even sheets Even sheets'",
+    ["BRAILLE LINE:  'gtk3-demo application Print dialog Page Setup page tab Layout panel Only print: Even 
sheets combo box Even sheets'",
      "     VISIBLE:  'Even sheets', cursor=1",
      "SPEECH OUTPUT: 'Print'",
      "SPEECH OUTPUT: 'Page Setup page tab'",
      "SPEECH OUTPUT: 'Layout panel'",
-     "SPEECH OUTPUT: 'Even sheets 2 of 3'"]))
+     "SPEECH OUTPUT: 'combo box Even sheets 2 of 3'"]))
 
 sequence.append(KeyComboAction("<Alt>F4"))
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]