[orca] Fix several issues related to rich-text editors in web apps
- From: Joanmarie Diggs <joanied src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [orca] Fix several issues related to rich-text editors in web apps
- Date: Wed, 11 Mar 2020 23:56:56 +0000 (UTC)
commit 7226fb5fda0abaf622d1d234dc632d25c6a58bfa
Author: Joanmarie Diggs <jdiggs igalia com>
Date: Wed Mar 11 19:33:09 2020 -0400
Fix several issues related to rich-text editors in web apps
* Treat multi-line elements with role entry as content editable with
embedded objects if they have at least one text block element as a
descendant. Needed to identify editors we were failing to treat as
editors (e.g. Gmail).
* Don't speak labelOrName when using caret-navigation keys within
content-editable content with embedded objects. Needed to fix
chattiness from focus events within the editor.
* Prevent presentation of objects which are not on the same line
during navigation within and between lines.
src/orca/scripts/web/script.py | 10 ++++-
src/orca/scripts/web/script_utilities.py | 65 +++++++++++++++++++++++++++-----
src/orca/scripts/web/speech_generator.py | 5 +++
3 files changed, 70 insertions(+), 10 deletions(-)
---
diff --git a/src/orca/scripts/web/script.py b/src/orca/scripts/web/script.py
index 0544dc40f..142c2a26d 100644
--- a/src/orca/scripts/web/script.py
+++ b/src/orca/scripts/web/script.py
@@ -840,7 +840,15 @@ class Script(default.Script):
if not obj:
return
- contents = self.utilities.getCharacterContentsAtOffset(obj, offset)
+ contents = None
+ if self.utilities.treatAsEndOfLine(obj, offset) and "Text" in pyatspi.listInterfaces(obj):
+ char = obj.queryText().getText(offset, offset + 1)
+ if char == self.EMBEDDED_OBJECT_CHARACTER:
+ char = ""
+ contents = [[obj, offset, offset + 1, char]]
+ else:
+ contents = self.utilities.getCharacterContentsAtOffset(obj, offset)
+
if not contents:
return
diff --git a/src/orca/scripts/web/script_utilities.py b/src/orca/scripts/web/script_utilities.py
index b411a764b..5f3169105 100644
--- a/src/orca/scripts/web/script_utilities.py
+++ b/src/orca/scripts/web/script_utilities.py
@@ -1160,7 +1160,7 @@ class Utilities(script_utilities.Utilities):
debug.println(debug.LEVEL_INFO, msg, True)
return string, start, end
- if boundary == pyatspi.TEXT_BOUNDARY_LINE_START and offset == text.characterCount:
+ if boundary == pyatspi.TEXT_BOUNDARY_LINE_START and self.treatAsEndOfLine(obj, offset):
offset -= 1
msg = "WEB: Line sought for %s at end of text. Adjusting offset to %i." % (obj, offset)
debug.println(debug.LEVEL_INFO, msg, True)
@@ -1543,6 +1543,39 @@ class Utilities(script_utilities.Utilities):
(i, acc, start, end, string, extents, states, attrs)
debug.println(debug.LEVEL_INFO, msg, True)
+ def treatAsEndOfLine(self, obj, offset):
+ if not self.isContentEditableWithEmbeddedObjects(obj):
+ return False
+
+ if "Text" not in pyatspi.listInterfaces(obj):
+ return False
+
+ if self.isDocument(obj):
+ return False
+
+ text = obj.queryText()
+ if offset == text.characterCount:
+ msg = "WEB: %s offset %i is end of line: offset is characterCount" % (obj, offset)
+ debug.println(debug.LEVEL_INFO, msg, True)
+ return True
+
+ char = text.getText(offset, offset + 1)
+ if char == "\n":
+ msg = "WEB: %s offset %i is end of line: char at offset is newline" % (obj, offset)
+ debug.println(debug.LEVEL_INFO, msg, True)
+ return True
+
+ if char == self.EMBEDDED_OBJECT_CHARACTER:
+ prevExtents = self.getExtents(obj, offset - 1, offset)
+ thisExtents = self.getExtents(obj, offset, offset + 1)
+ sameLine = self.extentsAreOnSameLine(prevExtents, thisExtents)
+ msg = "WEB: %s offset %i is [obj]. Same line: %s Is end of line: %s" % \
+ (obj, offset, sameLine, not sameLine)
+ debug.println(debug.LEVEL_INFO, msg, True)
+ return not sameLine
+
+ return False
+
def getLineContentsAtOffset(self, obj, offset, layoutMode=None, useCache=True):
if not obj:
return []
@@ -1559,7 +1592,11 @@ class Utilities(script_utilities.Utilities):
layoutMode = _settingsManager.getSetting('layoutMode') or self._script.inFocusMode()
objects = []
- extents = self.getExtents(obj, offset, offset + 1)
+ if offset > 0 and self.treatAsEndOfLine(obj, offset):
+ extents = self.getExtents(obj, offset - 1, offset)
+ else:
+ extents = self.getExtents(obj, offset, offset + 1)
+
if self.isInlineListDescendant(obj):
container = self.listForInlineListDescendant(obj)
if container:
@@ -4150,9 +4187,11 @@ class Utilities(script_utilities.Utilities):
debug.println(debug.LEVEL_INFO, msg, True)
return rv
- isTextBlockRole = role in self._textBlockElementRoles() or self.isLink(obj)
- if state.contains(pyatspi.STATE_EDITABLE):
- rv = isTextBlockRole
+ hasTextBlockRole = lambda x: x and x.getRole() in self._textBlockElementRoles()
+ if role == pyatspi.ROLE_ENTRY and state.contains(pyatspi.STATE_MULTI_LINE):
+ rv = pyatspi.findDescendant(obj, hasTextBlockRole)
+ elif state.contains(pyatspi.STATE_EDITABLE):
+ rv = hasTextBlockRole(obj) or self.isLink(obj)
elif not self.isDocument(obj):
document = self.getDocumentForObject(obj)
rv = self.isContentEditableWithEmbeddedObjects(document)
@@ -4526,11 +4565,19 @@ class Utilities(script_utilities.Utilities):
return obj, 0
if text and offset >= text.characterCount:
- if self.isContentEditableWithEmbeddedObjects(obj) and not self.lastInputEventWasLineNav():
+ if self.isContentEditableWithEmbeddedObjects(obj) \
+ and not self.lastInputEventWasLineNav() \
+ and not self.lastInputEventWasLineBoundaryNav():
nextObj, nextOffset = self.nextContext(obj, text.characterCount)
- if nextObj:
- msg = "WEB: First caret context at end of %s, %i is next context %s, %i" % \
- (obj, offset, nextObj, nextOffset)
+ if not nextObj:
+ msg = "WEB: No next object found at end of contenteditable %s" % obj
+ debug.println(debug.LEVEL_INFO, msg, True)
+ elif not self.isContentEditableWithEmbeddedObjects(nextObj):
+ msg = "WEB: Next object found at end of contenteditable %s is not editable %s" % (obj,
nextObj)
+ debug.println(debug.LEVEL_INFO, msg, True)
+ else:
+ msg = "WEB: First caret context at end of contenteditable %s is next context %s, %i" % \
+ (obj, nextObj, nextOffset)
debug.println(debug.LEVEL_INFO, msg, True)
return nextObj, nextOffset
diff --git a/src/orca/scripts/web/speech_generator.py b/src/orca/scripts/web/speech_generator.py
index 47e9d89d6..4c1b55031 100644
--- a/src/orca/scripts/web/speech_generator.py
+++ b/src/orca/scripts/web/speech_generator.py
@@ -315,6 +315,11 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
and not self._script.utilities.isContentSuggestion(obj):
return []
+ if self._script.utilities.isContentEditableWithEmbeddedObjects(obj):
+ lastKey, mods = self._script.utilities.lastKeyAndModifiers()
+ if lastKey in ["Home", "End", "Up", "Down", "Left", "Right", "Page_Up", "Page_Down"]:
+ return []
+
if obj.name:
name = obj.name
if not self._script.utilities.hasExplicitName(obj):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]