[orca] Web: Attempt to identify and present custom images



commit af6729fa567927a48b22480dd862fb38f2dda566
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Wed Nov 3 14:18:07 2021 +0100

    Web: Attempt to identify and present custom images
    
    A custom element with an author-provided name and one or more child
    images (often lacking an accessible name) should be treated as an
    image.

 src/orca/scripts/web/braille_generator.py |  2 ++
 src/orca/scripts/web/script_utilities.py  | 35 +++++++++++++++++++++++++++++++
 src/orca/scripts/web/speech_generator.py  |  2 ++
 3 files changed, 39 insertions(+)
---
diff --git a/src/orca/scripts/web/braille_generator.py b/src/orca/scripts/web/braille_generator.py
index 3d028af4b..36b943325 100644
--- a/src/orca/scripts/web/braille_generator.py
+++ b/src/orca/scripts/web/braille_generator.py
@@ -230,6 +230,8 @@ class BrailleGenerator(braille_generator.BrailleGenerator):
         if self._script.utilities.isClickableElement(obj) \
            or self._script.utilities.isLink(obj):
             oldRole = self._overrideRole(pyatspi.ROLE_LINK, args)
+        elif self._script.utilities.isCustomImage(obj):
+            oldRole = self._overrideRole(pyatspi.ROLE_IMAGE, args)
         elif self._script.utilities.isAnchor(obj):
             oldRole = self._overrideRole(pyatspi.ROLE_STATIC, args)
         elif self._script.utilities.treatAsDiv(obj, offset=args.get('startOffset')):
diff --git a/src/orca/scripts/web/script_utilities.py b/src/orca/scripts/web/script_utilities.py
index d45a188e6..e8a78e8f5 100644
--- a/src/orca/scripts/web/script_utilities.py
+++ b/src/orca/scripts/web/script_utilities.py
@@ -94,6 +94,7 @@ class Utilities(script_utilities.Utilities):
         self._isListDescendant = {}
         self._isNonNavigablePopup = {}
         self._isNonEntryTextWidget = {}
+        self._isCustomImage = {}
         self._isUselessImage = {}
         self._isRedundantSVG = {}
         self._isUselessEmptyElement = {}
@@ -187,6 +188,7 @@ class Utilities(script_utilities.Utilities):
         self._isListDescendant = {}
         self._isNonNavigablePopup = {}
         self._isNonEntryTextWidget = {}
+        self._isCustomImage = {}
         self._isUselessImage = {}
         self._isRedundantSVG = {}
         self._isUselessEmptyElement = {}
@@ -1187,6 +1189,9 @@ class Utilities(script_utilities.Utilities):
         if self.isFakePlaceholderForEntry(obj):
             return True
 
+        if self.isCustomImage(obj):
+            return True
+
         return False
 
     def __findRange(self, text, offset, start, end, boundary):
@@ -2303,6 +2308,8 @@ class Utilities(script_utilities.Utilities):
             rv = False
         elif role in [pyatspi.ROLE_DOCUMENT_FRAME, pyatspi.ROLE_DOCUMENT_WEB]:
             rv = True
+        elif self.isCustomImage(obj):
+            rv = False
         elif not state.contains(pyatspi.STATE_FOCUSABLE) and not state.contains(pyatspi.STATE_FOCUSED):
             rv = not self.hasNameAndActionAndNoUsefulChildren(obj)
         else:
@@ -2454,6 +2461,10 @@ class Utilities(script_utilities.Utilities):
 
         return 'suggestion' in self._getXMLRoles(obj)
 
+    def isCustomElement(self, obj):
+        tag = self._getTag(obj)
+        return tag and '-' in tag
+
     def isInlineIframe(self, obj):
         if not (obj and obj.getRole() == pyatspi.ROLE_INTERNAL_FRAME):
             return False
@@ -3096,6 +3107,8 @@ class Utilities(script_utilities.Utilities):
             rv = not self.hasExplicitName(obj)
         elif role == pyatspi.ROLE_TABLE_ROW and not state.contains(pyatspi.STATE_EXPANDABLE):
             rv = not self.hasExplicitName(obj)
+        elif self.isCustomImage(obj):
+            rv = False
         else:
             rv = super().isLayoutOnly(obj)
 
@@ -3939,6 +3952,28 @@ class Utilities(script_utilities.Utilities):
         self._isRedundantSVG[hash(obj)] = rv
         return rv
 
+    def isCustomImage(self, obj):
+        if not (obj and self.inDocumentContent(obj)):
+            return False
+
+        rv = self._isCustomImage.get(hash(obj))
+        if rv is not None:
+            return rv
+
+        rv = False
+        if self.isCustomElement(obj) and self.hasExplicitName(obj) \
+           and 'Text' in pyatspi.listInterfaces(obj) \
+           and not re.search(r'[^\s\ufffc]', obj.queryText().getText(0, -1)):
+            for child in obj:
+                if child.getRole() not in [pyatspi.ROLE_IMAGE, pyatspi.ROLE_CANVAS] \
+                   and self._getTag(child) != 'svg':
+                    break
+            else:
+                rv = True
+
+        self._isCustomImage[hash(obj)] = rv
+        return rv
+
     def isUselessImage(self, obj):
         if not (obj and self.inDocumentContent(obj)):
             return False
diff --git a/src/orca/scripts/web/speech_generator.py b/src/orca/scripts/web/speech_generator.py
index 0abd658e9..3dcc74082 100644
--- a/src/orca/scripts/web/speech_generator.py
+++ b/src/orca/scripts/web/speech_generator.py
@@ -776,6 +776,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             oldRole = self._overrideRole('default', args)
         elif self._script.utilities.isLink(obj):
             oldRole = self._overrideRole(pyatspi.ROLE_LINK, args)
+        elif self._script.utilities.isCustomImage(obj):
+            oldRole = self._overrideRole(pyatspi.ROLE_IMAGE, args)
         elif self._script.utilities.treatAsDiv(obj, offset=args.get('startOffset')):
             oldRole = self._overrideRole(pyatspi.ROLE_SECTION, args)
         else:


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