Skip to content

Commit

Permalink
Support IA2 labelled-by relation
Browse files Browse the repository at this point in the history
 ### Link to issue number:

Fixes nvaccess#17436

 ### Summary of the issue:

IAccessible2 has an IA2_RELATION_LABELLED_BY relation type, see
https://accessibility.linuxfoundation.org/a11yspecs/ia2/docs/html/group__grp_relations.html#ga7bbace7ffb57476b75d621af2e27d1ff .
However, that one was not taken into account by NVDA's "labeledBy"
property for objects. This could could be seen for example with LibreOffice that
implements handling of that IAccessible2 relation.

Additionally, a `None` return value from
IAccessibleHandler.accNavigate wasn't handled
in `IAccessible._get_labeledBy`, triggering an error, e.g.:

    >>> focus.labeledBy
    Traceback (most recent call last):
      File "<console>", line 1, in <module>
      File "C:\tools\cygwin\home\user\development\git\nvda\source\baseObject.py", line 59, in __get__
        return instance._getPropertyViaCache(self.fget)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "C:\tools\cygwin\home\user\development\git\nvda\source\baseObject.py", line 167, in _getPropertyViaCache
        val = getterMethod(self)
              ^^^^^^^^^^^^^^^^^^
      File "C:\tools\cygwin\home\user\development\git\nvda\source\NVDAObjects\IAccessible\__init__.py", line 1167, in _get_labeledBy
        (pacc, accChild) = IAccessibleHandler.accNavigate(
        ^^^^^^^^^^^^^^^^
    TypeError: cannot unpack non-iterable NoneType object

 ### Description of user facing changes

In NVDA's Python console, retrieving the target of the "labeledBy" property now works for objects
in applications implementing the "labelled-by" IAccessible2 relation and no longer triggers an error.

 ### Description of development approach

* Extend `IAccessibleHandler.RelationType` with the `LABELLED_BY` relation
  as defined in the IAccessible2 spec.
* Use this relation in `IAccessible._get_labeledBy` before
  falling back to trying a custom Mozilla/Gecko specific NAVRELATION.
* Add handling for the case where `IAccessibleHandler.accNavigate`
  for the custom Mozilla/Geck approach returns `None` to fix
  the error triggered otherwise when no relation is set.

 ### Testing strategy:

1. start NVDA
2. Start LibreOffice Writer
3. open the options dialog: "Tools" -> "Options", go to the "User Data" page
4. move focus to the "Company" text edit
5. open NVDA's Python console (Ctrl+Insert+Z)
6. print the object that the currently focused edit is labelled by,
   and it's accessible name and role:

    >>> focus.labeledBy
    <NVDAObjects.IAccessible.IAccessible object at 0x0BC55A70>
    >>> focus.labeledBy.name
    'Company:'
    >>> focus.labeledBy.role
    <Role.STATICTEXT: 7>

 ### Known issues with pull request:

None

 ### Code Review Checklist:

- [x] Documentation:
  - Change log entry
  - User Documentation
  - Developer / Technical Documentation
  - Context sensitive help for GUI changes
- [x] Testing:
  - Unit tests
  - System (end to end) tests
  - Manual testing
- [x] UX of all users considered:
  - Speech
  - Braille
  - Low Vision
  - Different web browsers
  - Localization in other languages / culture than English
- [x] API is compatible with existing add-ons.
- [x] Security precautions taken.

<!-- Please keep the following -->
@coderabbitai summary
  • Loading branch information
michaelweghorn committed Nov 26, 2024
1 parent c32e0e7 commit 1fe70b5
Show file tree
Hide file tree
Showing 2 changed files with 9 additions and 1 deletion.
1 change: 1 addition & 0 deletions source/IAccessibleHandler/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ class RelationType(str, enum.Enum):
CONTROLLER_FOR = "controllerFor"
ERROR = "error"
ERROR_FOR = "errorFor"
LABELLED_BY = "labelledBy"
9 changes: 8 additions & 1 deletion source/NVDAObjects/IAccessible/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1163,12 +1163,19 @@ def isPointInObject(self, x, y):
return True

def _get_labeledBy(self):
label = self._getIA2RelationFirstTarget(IAccessibleHandler.RelationType.LABELLED_BY)
if label:
return label

try:
(pacc, accChild) = IAccessibleHandler.accNavigate(
ret = IAccessibleHandler.accNavigate(
self.IAccessibleObject,
self.IAccessibleChildID,
IAccessibleHandler.NAVRELATION_LABELLED_BY,
)
if not ret:
return None
(pacc, accChild) = ret
obj = IAccessible(IAccessibleObject=pacc, IAccessibleChildID=accChild)
return obj
except COMError:
Expand Down

0 comments on commit 1fe70b5

Please sign in to comment.