[orca] Implement initial recovery from the current focused object being removed



commit cfb12731ea747f6b523028bb7471a3da3a9d65aa
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Fri May 1 17:28:12 2020 -0400

    Implement initial recovery from the current focused object being removed
    
    If we get a child-removed event for the removal of the current focus,
    and if that removal immediately followed line nav, attempt to locate
    the nearest object to the removed child based on the direction of
    navigation. If we find such an object, move there and present the line.
    
    Note: The goal of this recovery is not perfection; it's to improve
    unpredictable jumping around when the web author destroys the object
    we're in.
    
    This seems to improve the situation when we wander into Riot's Message
    Actions toolbar in Chromium and then that toolbar gets destroyed while
    we're in it. (The issue with our getting stuck in that same toolbar is
    a different issue which still needs to be solved.)

 src/orca/script_utilities.py             |  4 +-
 src/orca/scripts/web/script.py           | 23 ++++++----
 src/orca/scripts/web/script_utilities.py | 79 ++++++++++++++++++++++++++++++++
 3 files changed, 95 insertions(+), 11 deletions(-)
---
diff --git a/src/orca/script_utilities.py b/src/orca/script_utilities.py
index 25ee8e941..97264ff2f 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -2617,7 +2617,7 @@ class Utilities:
                 return relation.getTarget(0)
 
         index = obj.getIndexInParent() - 1
-        if obj.parent and not (0 <= index < obj.parent.childCount - 1):
+        while obj.parent and not (0 <= index < obj.parent.childCount - 1):
             obj = obj.parent
             index = obj.getIndexInParent() - 1
 
@@ -2642,7 +2642,7 @@ class Utilities:
                 return relation.getTarget(0)
 
         index = obj.getIndexInParent() + 1
-        if obj.parent and not (0 < index < obj.parent.childCount):
+        while obj.parent and not (0 < index < obj.parent.childCount):
             obj = obj.parent
             index = obj.getIndexInParent() + 1
 
diff --git a/src/orca/scripts/web/script.py b/src/orca/scripts/web/script.py
index 5f1021714..65497a141 100644
--- a/src/orca/scripts/web/script.py
+++ b/src/orca/scripts/web/script.py
@@ -1271,6 +1271,12 @@ class Script(default.Script):
             debug.println(debug.LEVEL_INFO, msg, True)
             contents = self.utilities.getLineContentsAtOffset(newFocus, caretOffset)
             utterances = self.speechGenerator.generateContents(contents)
+        elif self.utilities.lastInputEventWasLineNav() and event \
+             and event.type.startswith("object:children-changed"):
+            msg = "WEB: Last input event was line nav and children changed. Generating line contents."
+            debug.println(debug.LEVEL_INFO, msg, True)
+            contents = self.utilities.getLineContentsAtOffset(newFocus, caretOffset)
+            utterances = self.speechGenerator.generateContents(contents)
         else:
             msg = "WEB: New focus %s is not a special case. Generating speech." % newFocus
             debug.println(debug.LEVEL_INFO, msg, True)
@@ -1739,11 +1745,15 @@ class Script(default.Script):
             debug.println(debug.LEVEL_INFO, msg, True)
             return False
 
-        # TODO - JD: Handle this case.
-        if event.any_data == orca_state.locusOfFocus:
-            msg = "WEB: Removed child is locusOfFocus."
+        if self._loadingDocumentContent:
+            msg = "WEB: Ignoring because document content is being loaded."
             debug.println(debug.LEVEL_INFO, msg, True)
-            return False
+            return True
+
+        if self.utilities.handleEventForRemovedChild(event):
+            msg = "WEB: Event handled for removed child."
+            debug.println(debug.LEVEL_INFO, msg, True)
+            return True
 
         # TODO - JD: Handle this case.
         if event.source == orca_state.locusOfFocus:
@@ -1756,11 +1766,6 @@ class Script(default.Script):
             debug.println(debug.LEVEL_INFO, msg, True)
             return True
 
-        if self._loadingDocumentContent:
-            msg = "WEB: Ignoring because document content is being loaded."
-            debug.println(debug.LEVEL_INFO, msg, True)
-            return True
-
         return False
 
     def onDocumentLoadComplete(self, event):
diff --git a/src/orca/scripts/web/script_utilities.py b/src/orca/scripts/web/script_utilities.py
index 94a4c9147..3ac4bdd93 100644
--- a/src/orca/scripts/web/script_utilities.py
+++ b/src/orca/scripts/web/script_utilities.py
@@ -890,6 +890,36 @@ class Utilities(script_utilities.Utilities):
 
         return -1
 
+    def findPreviousObject(self, obj):
+        result = super().findPreviousObject(obj)
+        if not (obj and self.inDocumentContent(obj)):
+            return result
+
+        if not (result and self.inDocumentContent(result)):
+            return None
+
+        if self.getTopLevelDocumentForObject(result) != self.getTopLevelDocumentForObject(obj):
+            return None
+
+        msg = "WEB: Previous object for %s is %s." % (obj, result)
+        debug.println(debug.LEVEL_INFO, msg, True)
+        return result
+
+    def findNextObject(self, obj):
+        result = super().findNextObject(obj)
+        if not (obj and self.inDocumentContent(obj)):
+            return result
+
+        if not (result and self.inDocumentContent(result)):
+            return None
+
+        if self.getTopLevelDocumentForObject(result) != self.getTopLevelDocumentForObject(obj):
+            return None
+
+        msg = "WEB: Next object for %s is %s." % (obj, result)
+        debug.println(debug.LEVEL_INFO, msg, True)
+        return result
+
     def isNonEntryTextWidget(self, obj):
         rv = self._isNonEntryTextWidget.get(hash(obj))
         if rv is not None:
@@ -4541,6 +4571,55 @@ class Utilities(script_utilities.Utilities):
         self.setCaretContext(replicant, offset, documentFrame)
         return True
 
+    def handleEventForRemovedChild(self, event):
+        if event.any_data != orca_state.locusOfFocus:
+            return False
+
+        msg = "WEB: Removed child is locusOfFocus."
+        debug.println(debug.LEVEL_INFO, msg, True)
+
+        obj, offset = None, -1
+        keyString, mods = self.lastKeyAndModifiers()
+        if keyString == "Up":
+            if event.detail1 >= event.source.childCount:
+                msg = "WEB: Last child removed. Getting new location from end of parent."
+                debug.println(debug.LEVEL_INFO, msg, True)
+                obj, offset = self.previousContext(event.source, -1)
+            elif 0 <= event.detail1 - 1 < event.source.childCount:
+                child = event.source[event.detail1 - 1]
+                msg = "WEB: Getting new location from end of previous child %s." % child
+                debug.println(debug.LEVEL_INFO, msg, True)
+                obj, offset = self.previousContext(child, -1)
+            else:
+                prevObj = self.findPreviousObject(event.source)
+                msg = "WEB: Getting new location from end of source's previous object %s." % prevObj
+                debug.println(debug.LEVEL_INFO, msg, True)
+                obj, offset = self.previousContext(prevObj, -1)
+
+        elif keyString == "Down":
+            if event.detail1 == 0:
+                msg = "WEB: First child removed. Getting new location from start of parent."
+                debug.println(debug.LEVEL_INFO, msg, True)
+                obj, offset = self.nextContext(event.source, -1)
+            elif 0 < event.detail1 < event.source.childCount:
+                child = event.source[event.detail1]
+                msg = "WEB: Getting new location from start of child %i %s." % (child, event.detail1)
+                debug.println(debug.LEVEL_INFO, msg, True)
+                obj, offset = self.nextContext(child, -1)
+            else:
+                nextObj = self.findNextObject(event.source)
+                msg = "WEB: Getting new location from start of source's next object %s." % nextObj
+                debug.println(debug.LEVEL_INFO, msg, True)
+                obj, offset = self.nextContext(nextObj, -1)
+
+        if obj:
+            msg = "WEB: Setting locusOfFocus and context to: %s, %i" % (obj, offset)
+            orca.setLocusOfFocus(event, obj, True)
+            self.setCaretContext(obj, offset)
+            return True
+
+        return False
+
     def findContextReplicant(self, documentFrame=None, matchRole=True, matchName=True):
         path, oldRole, oldName = self.getCaretContextPathRoleAndName(documentFrame)
         obj = self.getObjectFromPath(path)


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