Skip to content
Permalink
Browse files

MozillaCompoundTextInfo: Don't skip ignored objects when determining …

…whether the range covers the end of any ancestors of endObj. (PR #9477)

MozillaCompoundTextInfo: Don't skip ignored objects when determining whether the range covers the end of any ancestors of endObj.

In order to display markers at the end of containers in braille (e.g. "lst end" at the end of lists), MozillaCompoundTextInfo needs to check the object at the end of the range (endObj) and its ancestors.
If the range is at the end of any of these objects, we mark the associated control field as being at the end of its node.
For example, if the range is at the end of a list, we mark the list's control field as being at end of node, which causes braille to display "lst end".
However, if the range is *not* at the end of one of these objects, the range also can't be at the end of any of its ancestors.
For example, if the range ends in the middle of a list item, it obviously doesn't cover the end of the list, even if the list item (as a whole) is the last item in the list.
In this case, we don't check the remaining ancestors.

Previously, this was failing where there was a single paragraph inside the last list item in a list.
The list would be marked as end of node, even if the range ended in the middle of the paragraph, resulting in a spurious "lst end" indicator before the cursor in braille.

This happened because we ignore paragraphs as irrelevant and thus don't generate control fields for them.
However, the code which checks whether the range is at the end of endObj or its ancestors only ran if there was a control field.
Because the paragraph had no control field, we'd skip it completely, not determining that the range ended in the middle of it and thus aborting the search.
We'd then check the next ancestor, and since the paragraph was the last child of the list item which was in turn the last child of the list, we'd mark all of these as end of node, including the list.

To fix this, we now run this check regardless of whether we generated a control field for an object; i.e. regardless of whether we ignored a paragraph.

Further explanation:
We choose to ignore paragraphs because they aren't useful; they're essentially presentational for our purposes. So, we don't generate a control field for them. This is where field will be None. However, just because we choose to ignore it in terms of control fields doesn't change the fact that there's an object for it. So, we still need to check whether we're at the end of that object. If we are, we won't do anything here because there's no field to annotate. However, if this is *not* the end, we will still break out of the loop, since we're not at the end of any of its ancestors. This is the key change here. Previously, we wouldn't break out of the ancestor loop for a paragraph (because no field), even if we were at the end of that paragraph. It's essential that we break out of the loop here if we're at the end of any object, regardless of whether we chose to generate a field for it.
  • Loading branch information...
jcsteh authored and feerrenrut committed Apr 12, 2019
1 parent 6e5e405 commit f876635252658326cce988354f5fcb5585c50a91
Showing with 7 additions and 5 deletions.
  1. +1 −0 contributors.txt
  2. +6 −5 source/NVDAObjects/IAccessible/ia2TextMozilla.py
@@ -184,3 +184,4 @@ Thomas Stivers
Eurobraille
Bachir Benanou
Arnold Loubriat
Mozilla Corporation
@@ -2,7 +2,7 @@
#A part of NonVisual Desktop Access (NVDA)
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.
#Copyright (C) 2015-2017 NV Access Limited
#Copyright (C) 2015-2019 NV Access Limited, Mozilla Corporation

"""Support for the IAccessible2 rich text model first implemented by Mozilla.
This is now used by other applications as well.
@@ -324,11 +324,12 @@ def _getText(self, withFields, formatConfig=None):
field = controlStack.pop()
if field:
fields.append(textInfos.FieldCommand("controlEnd", None))
if ti.compareEndPoints(self._makeRawTextInfo(obj, textInfos.POSITION_ALL), "endToEnd") == 0:
if ti.compareEndPoints(self._makeRawTextInfo(obj, textInfos.POSITION_ALL), "endToEnd") == 0:
if field:
field["_endOfNode"] = True
else:
# We're not at the end of this object, which also means we're not at the end of any ancestors.
break
else:
# We're not at the end of this object, which also means we're not at the end of any ancestors.
break
ti = self._getEmbedding(obj)
obj = ti.obj

0 comments on commit f876635

Please sign in to comment.
You can’t perform that action at this time.