Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fallback to UIA if supported in Chromium and we don't have access to IA2 #13032

Merged
merged 20 commits into from
Jul 13, 2022

Conversation

LeonarddeR
Copy link
Collaborator

@LeonarddeR LeonarddeR commented Nov 4, 2021

Link to issue number:

None

Summary of the issue:

Currently, UIA is used in the following cases in Chromium (notably Edge)

  1. It is force enabled
  2. We aren't able to inject in process

There is a particular case missing here. When running Edge under a different user, the IAccessible2 implementation is not available, whereas UIA works pretty nicely.

Description of how this pull request fixes the issue:

Add another check to ensure we use UIA in this case.

Testing strategy:

Run edge under a different user and ensure that it is accessible.

Known issues with pull request:

None known

Change log entries:

Bug fixes

  • When running under a different user, Microsoft Edge is no longer inaccessible (i.e. by falling back to its UIA implementation).

Code Review Checklist:

  • Pull Request description:
    • description is up to date
    • change log entries
  • Testing:
    • Unit tests
    • System (end to end) tests
    • Manual testing
  • API is compatible with existing add-ons.
  • Documentation:
    • User Documentation
    • Developer / Technical Documentation
    • Context sensitive help for GUI changes
  • UX of all users considered:
    • Speech
    • Braille
    • Low Vision
    • Different web browsers
    • Localization in other languages / culture than English

@LeonarddeR LeonarddeR requested a review from a team as a code owner November 4, 2021 11:15
@LeonarddeR LeonarddeR requested a review from seanbudd November 4, 2021 11:15
@seanbudd
Copy link
Member

seanbudd commented Nov 7, 2021

When running Edge under a different user, the IAccessible2 implementation is not available

Do we know if this is intentional, and if so, why that is the case? Does Google Chrome have this behaviour? Maybe we should raise this with MS.

@LeonarddeR
Copy link
Collaborator Author

When running Edge under a different user, the IAccessible2 implementation is not available

Do we know if this is intentional, and if so, why that is the case? Does Google Chrome have this behaviour? Maybe we should raise this with MS.

Chrome has this behavior and so have Electron apps. Firefox is also not accessible with IA2 when running under a different user. I am under the impression that it might be an IA2 specific thing.
May be it helps if we add a function to NVDAHelperRemote that gives us an IAccessible pointer to work with, but I'm afraid that requires us to do all the IA2 work in process, which is a rediculous job for just this issue to address.

seanbudd
seanbudd previously approved these changes Nov 22, 2021
@michaelDCurran
Copy link
Member

It would be good to be more clearer on the actual user scenarios here. What is the reasoning for running Edge as another user, and how does one actually do this?
I just logged in a s a standard user (not in the administrators group), Found Edge in the Start screen and chose Run as Administrator, and logged in as my normal (administrator group) account, and it worked fine.
No doubt this is not the scenario you were thinking of.
I do remember testing many mahy years back, that running an app as a different user caused a lot of things in NVDA not work work, including in-process injection, and even some of Microsoft's own MSAA code, such as successfully getting accName of a syslistview32 list item.

@michaelDCurran
Copy link
Member

Calling accessibleObjectFromWindow is not a cheep call, nor probably is the accChild call.
I think I would prefer adding functionality to appModuleHandler.AppModule to query whether the process is running as a different user. This would be a lot more light-weight, only playing with the process token. and it would only need to be checked once and cached.

@seanbudd seanbudd dismissed their stale review November 22, 2021 03:18

more information is needed

@LeonarddeR
Copy link
Collaborator Author

It would be good to be more clearer on the actual user scenarios here. What is the reasoning for running Edge as another user, and how does one actually do this? I just logged in a s a standard user (not in the administrators group), Found Edge in the Start screen and chose Run as Administrator, and logged in as my normal (administrator group) account, and it worked fine. No doubt this is not the scenario you were thinking of. I do remember testing many mahy years back, that running an app as a different user caused a lot of things in NVDA not work, including in-process injection, and even some of Microsoft's own MSAA code, such as successfully getting accName of a syslistview32 list item.

The scenario I use is

  1. Logon to the system as a local administrator
  2. Start Edge as a different user, shift+applications key on the shortcut
  3. Logon as another user.

I'm working in a corporate environment where we use a different account for administrative tasks, such as database administratio. Running the browser under another user eases with single sign on.

Calling accessibleObjectFromWindow is not a cheep call, nor probably is the accChild call. I think I would prefer adding functionality to appModuleHandler.AppModule to query whether the process is running as a different user. This would be a lot more light-weight, only playing with the process token. and it would only need to be checked once and cached.

That makes sense. I'll close this and will think about another approach.

@LeonarddeR LeonarddeR closed this Nov 22, 2021
@LeonarddeR LeonarddeR reopened this Nov 22, 2021
@LeonarddeR
Copy link
Collaborator Author

I implemented a new approach that gets the logon session ID from NVDA's process, caches that, and compares it with the logon session id of the appModule. Pretty sure that will be much cheaper indeed.
I still need to test this with your scenario, though.

@LeonarddeR
Copy link
Collaborator Author

I just logged in a s a standard user (not in the administrators group), Found Edge in the Start screen and chose Run as Administrator, and logged in as my normal (administrator group) account, and it worked fine.

I discussed this with a colleague and this indeed seems to differ significantly in that "run as admin" only ensures elevated permissions for the account that is currently running. So in your scenario, Edge would still be running as your standard user.
What happens if you do run as a different user and enter your admin credentials? I'm pretty sure the issue will occur in that case.

Also, while we are at it, could you may be elaborate on what the IA2support in NVDAHelper is supposed to do? Is that meant to initialize IA2 support for the helper process, or is it also necessary to initialize IA2 in such a way that out of process calls can use it?

@michaelDCurran
Copy link
Member

Ah, I forgot about shift+applications key. Now I do see Run as user. I reproduced your problem by being logged in as a standard user, then running Edge as my other admin user. Everything just says "unknown".

@michaelDCurran
Copy link
Member

Re IA2 in-process:
That code registers the IA2 proxy dll in that process, so that the IAccessible2 interfaces can be marshaled cross-process. In other words, to use IAccessible2 out-of-process, it is necessary to register it in-process. Though, code (like the gecko_ia2 virtualBuffers) would still work in theory.
However, since roughly 2018, Windows 10 has included the IAccessible2 proxy dll natively, with it registered system-wide. Thus making it unnecessary to register it in-process. They did this for their IA2 to UIA bridge. However, I really don't know how much they are keeping that up to date, so it is probably still necessary for us to register any newer interfaces, and of course on older Windows versions we need to register it all anyway.

@LeonarddeR
Copy link
Collaborator Author

Re IA2 in-process:

Thanks for clarifying.

I really want to get to the bottom of this somehow. It looks like the IA2 to UIA bridge. is perfectly able to expose stuff from Firefox as well, when that's running under a different user. On the other hand, NVDA is unable to read anything. This suggests there might be something permission wise that blocks us from accessing Firefox. As expected, when running NVDA as admin, all works perfectly fine.

@seanbudd
Copy link
Member

I am marking this as a draft as this review comment is unresolved:

This call could fail due to not enough permissions on the process handle. Perhaps return None or raise an exception if it returns false

@seanbudd seanbudd marked this pull request as draft February 16, 2022 23:09
@LeonarddeR LeonarddeR marked this pull request as ready for review February 18, 2022 07:35
@LeonarddeR
Copy link
Collaborator Author

The review comment has been fixed.

@AppVeyorBot
Copy link

See test results for failed build of commit 88fcb449fb

@AppVeyorBot
Copy link

See test results for failed build of commit f9ffef5225

@AppVeyorBot
Copy link

@AppVeyorBot
Copy link

See test results for failed build of commit 258c421f9f

source/nvda.pyw Outdated
@@ -77,6 +77,15 @@ import logHandler
from logHandler import log
import winUser
import winKernel
# Avoid a E402 'module level import not at top of file' warning,
# because monkeypatches need to be applied first first
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the second "first" a mistake, or have you added it just for emphasis?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was definitely a mistake.

source/nvda.pyw Outdated
from systemUtils import getProcessTokenOrigin # noqa: E402

try:
globalVars.appLogonSessionID = getProcessTokenOrigin(winKernel.GetCurrentProcess())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative implementation would be to create a\ function in systemUtils named getAppLoginID or similar which just retrieves logon ID of the current process first time it is called. If you're worried about performance you can cache the result similar to what is being done in hasSyswow64Dir. I see several advantages:

  • The NVDA's logon ID is retrieved only when needed i.e. only in cases where user accesses Chromium
  • In the current approach if there is an error when getting NVDA's logon ID we won't know about it since logging is not yet ready and exception is swallowed.
  • The NOQA comment would not be necessary :-D

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this idea.

@seanbudd seanbudd self-assigned this Jun 22, 2022
Copy link
Member

@seanbudd seanbudd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @LeonarddeR, the approach looks good to me. I've added some suggestions around coding style and documentation.

source/systemUtils.py Outdated Show resolved Hide resolved
source/systemUtils.py Outdated Show resolved Hide resolved
source/systemUtils.py Outdated Show resolved Hide resolved
source/systemUtils.py Outdated Show resolved Hide resolved
_fields_ = [("OriginatingLogonSession", ctypes.c_ulonglong)]


def getProcessTokenOrigin(processHandle: int) -> TOKEN_ORIGIN:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A docstring describing what this is getting, and what it is used for is needed here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to merge the approach to get the logon session ID in one function, it looks more obvious now.

@seanbudd seanbudd marked this pull request as draft June 27, 2022 01:06
@seanbudd seanbudd added the merge-early Merge Early in a developer cycle label Jun 28, 2022
@AppVeyorBot
Copy link

See test results for failed build of commit b7596ca0e1

@seanbudd
Copy link
Member

Is this still meant to be a draft? What's remaining here?

@LeonarddeR LeonarddeR marked this pull request as ready for review July 12, 2022 05:52
@LeonarddeR
Copy link
Collaborator Author

Ah I'm sorry, I think it's ready now.

Copy link
Member

@seanbudd seanbudd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @LeonarddeR

@seanbudd seanbudd merged commit a09aef3 into nvaccess:master Jul 13, 2022
@nvaccessAuto nvaccessAuto added this to the 2022.3 milestone Jul 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked/needs-code-review conceptApproved Similar 'triaged' for issues, PR accepted in theory, implementation needs review. merge-early Merge Early in a developer cycle
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants