[orca] Web: Make ARIA treeitems descendable in browse mode



commit df5e0b7136f17b08cc9c5a610d93c78a95e2bf5b
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Tue Mar 23 13:38:20 2021 +0100

    Web: Make ARIA treeitems descendable in browse mode
    
    Now that trees can be navigated in browse mode (mainly to work around
    bad authoring), we need to conditionally descend treeitems in case we
    have an instance of nested treeitems. This change in and of itself is
    simple. However, if instead of nested treeitems, the descendants are
    the non-interactive text and/or image elements used to display the
    items content, we don't want to descend into them.
    
    It's not performant to do a tree dive just to check which kind of
    tree we have. Therefore, assume the treeitem is descendable, but
    treat its non-interactive descendants as non-valid caret contexts.

 src/orca/scripts/web/script_utilities.py | 44 ++++++++++++++++++++++++++++++--
 1 file changed, 42 insertions(+), 2 deletions(-)
---
diff --git a/src/orca/scripts/web/script_utilities.py b/src/orca/scripts/web/script_utilities.py
index 97252b6d5..ef3c12c05 100644
--- a/src/orca/scripts/web/script_utilities.py
+++ b/src/orca/scripts/web/script_utilities.py
@@ -80,6 +80,7 @@ class Utilities(script_utilities.Utilities):
         self._hasVisibleCaption = {}
         self._hasDetails = {}
         self._isDetails = {}
+        self._isNonInteractiveDescendantOfControl = {}
         self._hasUselessCanvasDescendant = {}
         self._isClickableElement = {}
         self._isAnchor = {}
@@ -173,6 +174,7 @@ class Utilities(script_utilities.Utilities):
         self._hasDetails = {}
         self._isDetails = {}
         self._hasUselessCanvasDescendant = {}
+        self._isNonInteractiveDescendantOfControl = {}
         self._isClickableElement = {}
         self._isAnchor = {}
         self._isEditableComboBox = {}
@@ -1096,6 +1098,40 @@ class Utilities(script_utilities.Utilities):
         self._hasNameAndActionAndNoUsefulChildren[hash(obj)] = rv
         return rv
 
+    def isNonInteractiveDescendantOfControl(self, obj):
+        if not (obj and self.inDocumentContent(obj)):
+            return False
+
+        rv = self._isNonInteractiveDescendantOfControl.get(hash(obj))
+        if rv is not None:
+            return rv
+
+        try:
+            role = obj.getRole()
+            state = obj.getState()
+        except:
+            msg = "WEB: Exception getting role and state for %s" % obj
+            debug.println(debug.LEVEL_INFO, msg, True)
+            return False
+
+        rv = False
+        roles = self._textBlockElementRoles()
+        roles.extend([pyatspi.ROLE_IMAGE, pyatspi.ROLE_CANVAS])
+        if role in roles and not state.contains(pyatspi.STATE_FOCUSABLE):
+            controls = [pyatspi.ROLE_CHECK_BOX,
+                        pyatspi.ROLE_CHECK_MENU_ITEM,
+                        pyatspi.ROLE_LIST_BOX,
+                        pyatspi.ROLE_MENU_ITEM,
+                        pyatspi.ROLE_RADIO_MENU_ITEM,
+                        pyatspi.ROLE_RADIO_BUTTON,
+                        pyatspi.ROLE_PUSH_BUTTON,
+                        pyatspi.ROLE_TOGGLE_BUTTON,
+                        pyatspi.ROLE_TREE_ITEM]
+            rv = pyatspi.findAncestor(obj, lambda x: x and x.getRole() in controls)
+
+        self._isNonInteractiveDescendantOfControl[hash(obj)] = rv
+        return rv
+
     def _treatObjectAsWhole(self, obj, offset=None):
         always = [pyatspi.ROLE_CHECK_BOX,
                   pyatspi.ROLE_CHECK_MENU_ITEM,
@@ -1104,13 +1140,13 @@ class Utilities(script_utilities.Utilities):
                   pyatspi.ROLE_RADIO_MENU_ITEM,
                   pyatspi.ROLE_RADIO_BUTTON,
                   pyatspi.ROLE_PUSH_BUTTON,
-                  pyatspi.ROLE_TOGGLE_BUTTON,
-                  pyatspi.ROLE_TREE_ITEM]
+                  pyatspi.ROLE_TOGGLE_BUTTON]
 
         descendable = [pyatspi.ROLE_MENU,
                        pyatspi.ROLE_MENU_BAR,
                        pyatspi.ROLE_TOOL_BAR,
                        pyatspi.ROLE_TREE,
+                       pyatspi.ROLE_TREE_ITEM,
                        pyatspi.ROLE_TREE_TABLE]
 
         role = obj.getRole()
@@ -4643,6 +4679,10 @@ class Utilities(script_utilities.Utilities):
             msg = "WEB: Fake placeholder for entry cannot have caret context %s" % obj
             debug.println(debug.LEVEL_INFO, msg, True)
             return False
+        if self.isNonInteractiveDescendantOfControl(obj):
+            msg = "WEB: Non interactive descendant of control cannot have caret context %s" % obj
+            debug.println(debug.LEVEL_INFO, msg, True)
+            return False
 
         return True
 


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