UIA handler/notification event handling: fetch native window handle if present in runtime ID#16872
UIA handler/notification event handling: fetch native window handle if present in runtime ID#16872josephsl wants to merge 7 commits into
Conversation
…w handle when the element reports nothing. Re nvaccess#16871. Sometimes a UIA element may say there is no native window handle when in fact it is part of its runtime ID. Therefore fetch the runtime ID tuple if possible, logging the process along the way. If the runtime ID tuple (really an array) is empty, return early as there is nothing NVDA can do at this time.
…e window handle to UIA object constructor. Re nvaccess#16871. Pass in the just located native window handle when instantiating UIA NVDA object to align with most event handlers 9such as focus handling).
WalkthroughThe changes in this update focus on enhancing NVDA's handling of UIA notification events, particularly for Windows 11 Voice Access. This involves explicitly defining argument types for a function, updating a conditional block for handling native window handles, and ensuring NVDA does not give up on finding a native window handle when it's available through the UIA runtime ID. Changes
Sequence Diagram(s)sequenceDiagram
participant NVDA
participant UIAHandler
participant NVDAObjects
NVDA->>UIAHandler: IUIAutomationNotificationEventHandler_HandleNotificationEvent
UIAHandler->>NVDAObjects: Create UIA object with windowHandle and UIAElement
Note right of NVDAObjects: If windowHandle is not found, use UIA runtime ID.
NVDAObjects->>NVDA: Return UIA object
NVDA->>User: Announce notification (e.g., "microphone active/sleep")
Assessment against linked issues
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (invoked as PR comments)
Additionally, you can add CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 0
Outside diff range, codebase verification and nitpick comments (2)
user_docs/en/changes.md (2)
Line range hint
1-3: Ensure consistency in change log entries.The change log entry accurately reflects the PR's focus on enhancing event handling for caret movement. However, the style guideline of one sentence per line is not followed.
- * NVDA once again relies on events for caret movement in several cases, rather than only on manual querying of the caret position. + * NVDA once again relies on events for caret movement in several cases. + * This is rather than only on manual querying of the caret position.
21-21: Ensure clarity and completeness in documentation.The entry mentions the enhancement related to UIA notifications but could be more descriptive about the specific functionality added.
- * NVDA can announce more UIA notifications in apps such as Windows 11 Voice Access. + * NVDA now supports enhanced announcement of UIA notifications in applications like Windows 11 Voice Access, improving accessibility.
See test results for failed build of commit 6d0b097280 |
| f"activityId={activityId}" | ||
| ) | ||
| if any(runtimeID := sender.getRuntimeID()): | ||
| # Second item in runtime ID array is native window handle. |
There was a problem hiding this comment.
Can you say with certainty that this always applies?
|
Hi, so far, yes – have to come across a case that doesn’t. The commit log has a note on the empty tuple, so may need to add that comment to the source code as well. Thanks.
|
|
Hi, turns out File Explorer’s folders/files view only shows three items in its runtime ID, so I’ll edit the source code comment. Thanks.
|
…cess#16871. Comment from Leonard de Ruijter: make sure we're dealing with the correct UIA runtime ID representatio (the one with native window handle has four elements; sometimes tuple item count can vary, including an empty tuple).
|
@coderabbitai is there anything you can say about the assumption that the window handle is part of an UIA runtime id? |
|
Tip For best results, initiate chat on the files or code changes. @LeonarddeR, UI Automation (UIA) runtime IDs are unique identifiers assigned to automation elements, and their structure can vary depending on the element and the application providing the UIA implementation. Typically, a runtime ID is an array of integers, where the presence of a window handle as part of this array is not guaranteed but can occur, especially for elements associated directly with a window. In the context of the NVDA changes you're reviewing, the assumption that a window handle could be part of the runtime ID is based on practical observations and specific use cases, such as with Windows 11 Voice Access. However, it's important to note that this is not a universal rule for all UIA elements. The modification made in the PR to check the runtime ID for a window handle when This approach helps in cases where the window handle is crucial for properly associating the UIA element with its corresponding UI context, enhancing NVDA's ability to provide accurate notifications and interactions. |
|
I know that at least accesskit creates a runtime identifier based on its internal node ID. Therefore I don't think the current approach is feasible. |
|
Hi, Okay, let's see what Code Rabbit says. @coderabbitai analyze other event handlers in UIA handler module and tell us why a window handle is a must. Postscript: I'm doing this to train the bot and point out possible inaccuracies from AI summaries. Thanks. |
|
Tip For best results, initiate chat on the files or code changes. @josephsl, after analyzing the event handlers in the
In summary, window handles are essential for ensuring that UIA events are accurately associated with their UI context, processed correctly, and handled efficiently. This is why the presence of a window handle is often checked and utilized in the event handlers within the Analysis chainScripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Search for the UIA handler module and extract the implementation of event handlers to analyze the use of window handles.
fd "UIAHandler" --exec cat {} \; | grep -A 5 -B 5 "event handler"
Length of output: 109 Script: #!/bin/bash
# Search for function definitions related to event handlers within the UIA handler module files to understand the use of window handles.
ast-grep --lang python --pattern $'def $_($_, $_, $_, $_, $_) -> $_:\n $$$\n # Event handler' --source-path source/UIAHandler
Length of output: 345 Script: #!/bin/bash
# Correct the syntax to search for function definitions related to event handlers within the UIA handler module files.
ast-grep --lang python --pattern $'def $_($_, $_, $_, $_, $_) -> $_:\n $$$\n # Event handler' source/UIAHandler
Length of output: 116 Script: #!/bin/bash
# Search for all function definitions within the UIA handler module files to identify event handlers and their use of window handles.
ast-grep --lang python --pattern $'def $_($_, $$$):\n $$$' source/UIAHandler
Length of output: 176299 Learnings added
|
|
Hi, At least Code Rabbit gave some answers, but that's only part of the picture. It is correct that window handles are needed to identify UIA elements. More importantly, window handles are needed to construct a more accurate view of a UIA element, as without this, UIAHandler.handler.getNearestWindowHandle can fail to notice elements due to COM error while executing walkers. Also, creation of UIA NVDA object ensures consistency with IAccessible/IA2 objects and allows other components such as app modules and default object implementation to handle events, although creation of objects and a chain of events can take some time to complete (performance). The heart of the issue is inaccurate UIA element (sender) information in apps such as Voice Access when they raise notification event. The fact that another assistive technology (Narrator) must be active to see correct sender information is concerning. While I agree that retrieving window handle through runtime ID is a fragile approach, it is one sure way to identify elements (window handle check was also performed via dev info (NVDA+F1) command). Thanks. |
This makes me think about how we handle invalid window handles with WDAG. May be that approach should be generalized instead?
|
|
Hi, Generalizing WDAG approach: perhaps we could recycle that approach but to cover background events as well (WDAG is deprecated). For Voice Access, the most effective use case is dictation while not focused on Voice Access interface itself. Comment on sender info with Narrator active: when you run Voice Access whiel using NVDA with debug logging on (with UIA included), you'll notice that NVDA will log that there is no window handle. However, when Narrator runs, notification is announced and the UIA object is created successfully; when Narrator exits, NVDA reverts to the inablity to obtain window handle. Thanks. |
| else: | ||
| # No runtime ID (tuple is empty). | ||
| if _isDebug(): | ||
| log.debugWarning( | ||
| "HandleNotificationEvent: native window handle not found in runtime ID: " | ||
| f"NotificationProcessing={NotificationProcessing} " | ||
| f"displayString={displayString} " | ||
| f"activityId={activityId}" | ||
| ) | ||
| return |
There was a problem hiding this comment.
shouldn't this be?
| else: | |
| # No runtime ID (tuple is empty). | |
| if _isDebug(): | |
| log.debugWarning( | |
| "HandleNotificationEvent: native window handle not found in runtime ID: " | |
| f"NotificationProcessing={NotificationProcessing} " | |
| f"displayString={displayString} " | |
| f"activityId={activityId}" | |
| ) | |
| return | |
| else: | |
| # No runtime ID (tuple is empty). | |
| if _isDebug(): | |
| log.debugWarning( | |
| "HandleNotificationEvent: native window handle not found in runtime ID: " | |
| f"NotificationProcessing={NotificationProcessing} " | |
| f"displayString={displayString} " | |
| f"activityId={activityId}" | |
| ) | |
| return |
|
Hi, Upon further investigation, while handling notification events, it turns out elements raising this event become "native" while Narrator is running, "non-native" otherwise. Therefore, this issue may require editing native UIA element fetcher (UIA handler) to tell NVDA to treat some elements as native even though it is not seen as such at first. While editing notification event handler could be a useful change, I think the issue lies deeper (this is why I'm not taking Sean's suggested edits yet). Thanks. |
|
May be it makes sense to contact Microsoft about this? |
|
Hi, Sure. I have narrowed the issue to UIAHandler.handler.getNearestWindowHandle - the method that says yes or no about native UIA element depends on nearest window handle getter. In short, tree walker and window handle caching succeeds or fails in Voice Access while Narrator is enabled and disabled, respectively. There is a related issue that also deals with UIA notification: input language switcher. For that one, runtiem ID is undefined (empty). Given that language switch can happen everywhere, I think we should revisit the decision to limit notification handling to focused app (I think our justification back then (annoyance) does not hold well); that's for another issue. Thanks. |
|
I would be uncomfortable accepting this pr as there is no clear evidence that the second value is always a native window handle. Clearly there is something special going on between narrator and VoiceAccess. This really needs to be talked about with Microsoft, specifically the VoiceAccess team. I am happy to reach out to them through my other Microsoft contacts and get them connected with you @josephsl if that would help? |
|
Hi, I came to the same conclusion. At this point, there is no direction, and turns out the PR won't work with portable NVDA anyway. It could be a good use case for add-ons but not up to par with NVDA Core code quality. I think we could also raise a point about File Explorer/shell/input switch notification event as this is perhaps the weirdest element we came across (no runtime ID at all). Thanks. |
|
Closing based on above comments. Hopefully it can be addressed in VoiceAccess itself. |
Link to issue number:
Closes #16871
Summary of the issue:
NVDA does not handle UIA notification events for elements with native window handle despite shown in runtime ID.
Description of user facing changes
NVDA will announce UIA notifications in more apps such as Windows 11 Voice Access.
Description of development approach
Updated UIA handler/notification event handler:
Testing strategy:
Manual testing (see #16862 for details):
Note that the test build must be installed or run with admin rights.
Known issues with pull request:
In some cases runtime ID is empty, and for that case, there's nothing NVDA can do. The native window handle check is done early for performance - if UIA NVDA object constructor does not see a window handle, the constructor will call UIAHandler.handler.getNearestWindowHandle method to obtain a window handle. Also, while more UIA notifications are announced, it may need some tweaks such as making sure the window handle is indeed a native window handle, and at least runtime ID tuple check is performed in this PR; these should be dealt with on a ase-by-case basis (as issues arise).
Code Review Checklist:
Summary by CodeRabbit
New Features
Documentation