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

Implement support for writing UI Automation Remote Operations natively in NVDA using Python #16214

Merged
merged 117 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
df1752c
wip: low level remote ops
michaelDCurran Dec 25, 2023
cf31290
Remove dependency on Microsoft.UI.UIAutomation Remote Ops library, as…
michaelDCurran Dec 29, 2023
a83e41c
Merge branch 'master' into remoteOpsLowLevel
michaelDCurran Dec 30, 2023
cc91e73
High level remote ops: Else is now conveyed as its own ElseBlock cont…
michaelDCurran Dec 30, 2023
daa10f6
Don't uppercase first letter of ifBlock and elseBlock
michaelDCurran Dec 30, 2023
dc78787
Remove opcode checking from highLevel as it only works after importin…
michaelDCurran Dec 30, 2023
6b8c893
UIARemote lowLevel c++: add support for bringing back IUnknown
michaelDCurran Jan 1, 2024
f41b6c8
Highlevel remoteOps: Add support for binary and inplace add, subtract…
michaelDCurran Jan 1, 2024
42d2b8a
Restructure high-level API to add a new execute function which takes …
michaelDCurran Jan 1, 2024
242511b
Rename highLevel to midLevel and move the execute function and its Re…
michaelDCurran Jan 1, 2024
9294f8b
midLevel remote ops: add support for comparison operators.
michaelDCurran Jan 1, 2024
854bfa4
Remote ops: add support for while loops. Also fix binary / inplace op…
michaelDCurran Jan 1, 2024
7b7f25e
Remote ops: add support for BreakLoop and continueLoop commands. Add …
michaelDCurran Jan 1, 2024
6e74460
Remote ops: add try catch blocks.
michaelDCurran Jan 2, 2024
cabd5b4
lowLevel remote ops: add missing comparisonType enum.
michaelDCurran Jan 2, 2024
c0c1c6d
Remote ops midLevel: add RelativeOffset and OperandId nicely printed…
michaelDCurran Jan 2, 2024
5b78db1
Fix GUID handling.
michaelDCurran Jan 2, 2024
34ce721
MidLevel remote ops: make flake8 and myPy clean.
michaelDCurran Jan 4, 2024
44d2f4f
low level remote ops: add OperandId type.
michaelDCurran Jan 4, 2024
6fe197b
More features and refactoring:
michaelDCurran Jan 7, 2024
e679b8f
* Keyword argument: enableLogging now renamed to remoteLogging
michaelDCurran Jan 7, 2024
1029ed8
Again log instruciton where remote error occured.
michaelDCurran Jan 7, 2024
23f4ba9
Remote ops: Remote midLevel.RemoteOperationExecutor and just handle l…
michaelDCurran Jan 8, 2024
c77e063
Remote ops CatchBlock: return the error operation status from __enter…
michaelDCurran Jan 8, 2024
e48272a
Remote ops midLevel: rename all remote classes to public (no undersco…
michaelDCurran Jan 8, 2024
6be6f8b
Remote ops: add instruction comments denoting loading of arguments an…
michaelDCurran Jan 8, 2024
d438c75
remote ops logMessage: cut down minium instructions from 2 to 1.
michaelDCurran Jan 8, 2024
431d9d9
Remote ops: generalize comments to meta strings, and add methods for …
michaelDCurran Jan 8, 2024
8c445a7
Remote ops: add support for arrays.
michaelDCurran Jan 10, 2024
dde8f88
Remote ops: add RemoteUint (unsigned integer) type.
michaelDCurran Jan 10, 2024
9158702
RemoteBaseObject: add a copy() method.
michaelDCurran Jan 10, 2024
61ecf9d
Automatically bind remote objecs on function calls.
michaelDCurran Jan 14, 2024
747e91f
Again support auto conversion of local types to remote types. Add sup…
michaelDCurran Jan 16, 2024
7a65a36
Remote ops: add navigation methods to RemoteElement
michaelDCurran Jan 16, 2024
263a903
Introduce RemoteOperationBuilder.addInstruction2, which allows specif…
michaelDCurran Jan 17, 2024
389a65c
Remote ops: support IntEnum, and show it with in dumped instructions.
michaelDCurran Jan 17, 2024
ab9be62
Add RemoteTextRangeLogicalAdapter and forEachUnitOfTextRange algorithm.
michaelDCurran Jan 17, 2024
09d2452
WIP
michaelDCurran Jan 21, 2024
b859f4e
Use remote ops to majorly speed up searching for headings with quick …
michaelDCurran Jan 23, 2024
a8e8c49
Remote ops: fix typing.
michaelDCurran Jan 24, 2024
ee50e5f
Merge branch 'master' into remoteOpsLowLevel
michaelDCurran Jan 25, 2024
c77b026
lowLevel.py: add docstrings.
michaelDCurran Jan 25, 2024
719eec7
Remote ops: further refactoring, including:
michaelDCurran Jan 29, 2024
5040f66
remote ops: More refactoring:
michaelDCurran Feb 4, 2024
5a5b76d
Allow remote operations to be executed locally for debugging purposes…
michaelDCurran Feb 11, 2024
2a8e189
Start adding some unit tests for remote ops, using the LocalOperation…
michaelDCurran Feb 12, 2024
ae86d88
Add more tests. Make sure remote ops tests can run with our rununitte…
michaelDCurran Feb 13, 2024
76fd29c
Move requirement that an operation can only be built / executed if it…
michaelDCurran Feb 13, 2024
fb3fef4
More remote ops tests.
michaelDCurran Feb 13, 2024
7ec381b
Remote ops: fix local execute of ElementGetPropertyValue.
michaelDCurran Feb 15, 2024
1a581d8
Remote ops: LocalOperation supports setting an instruction count limi…
michaelDCurran Feb 15, 2024
aec1dea
Remote ops: ad the concept of static variables, that keep their valu…
michaelDCurran Feb 16, 2024
a0c0529
Remote ops: Add Return and Yield functions to the RemoteAPI object, a…
michaelDCurran Feb 18, 2024
7b320c2
Remote ops: add NoReturnException which is thrown by Operation.execut…
michaelDCurran Feb 18, 2024
2f9869a
Remote ops: rimtime logging and compiletime logging can now be enable…
michaelDCurran Feb 18, 2024
cf7c296
Linting
michaelDCurran Feb 18, 2024
6aef879
Remote ops: refactor operation.execute and operation.iterExecute to i…
michaelDCurran Feb 19, 2024
963156a
Linting
michaelDCurran Feb 19, 2024
56e3c08
Remote ops c++: add missing break statements which caused booleans no…
michaelDCurran Feb 19, 2024
ef828ec
Remote ops: add support for the modulo operator. This is not natively…
michaelDCurran Feb 19, 2024
b9374a8
Remote ops: add tests for float operations.
michaelDCurran Feb 19, 2024
36bbf16
Remote ops: arrays now automatically have any IUnknown COM pointers a…
michaelDCurran Feb 19, 2024
c8fc67f
Add some rough documentation for remote operations.
michaelDCurran Feb 21, 2024
8fe4775
Remote some old code from remoteAlgorithms.py. Add forEachNumInRange …
michaelDCurran Feb 22, 2024
deab803
Linting. Remove some old code.
michaelDCurran Feb 22, 2024
556b560
Remove old unneeded code.
michaelDCurran Feb 24, 2024
5354ce8
Remote ops lowLevel c++: remoteOpResult_free now returns an HRESULT t…
michaelDCurran Feb 24, 2024
b92032c
Fix typo
michaelDCurran Feb 24, 2024
0f4ae89
UIAHandler.remote.findFirstHeadingInTextRange: don't enable compileti…
michaelDCurran Feb 24, 2024
9695930
Oremote ops readme: fix several typos and spelling errors.
michaelDCurran Feb 24, 2024
3dcf20a
Linting.
michaelDCurran Feb 24, 2024
c80b498
Linting: fix hanging indent.
michaelDCurran Feb 25, 2024
95607ba
Remote ops readme: add needed blank lines.
michaelDCurran Feb 25, 2024
2f69a6d
UIAHandler._remoteOps: add an empty __init__.py so that py2exe correc…
michaelDCurran Feb 25, 2024
9a938fc
Remote ops readme: more spelling fixes.
michaelDCurran Feb 25, 2024
9edf0ac
Update copyright headers, remove unneeded variable.
michaelDCurran Feb 25, 2024
b98912c
Address review action.
michaelDCurran Feb 25, 2024
44fb34e
Remove references to microsoft-ui-uiAutomation remote ops library in …
michaelDCurran Feb 25, 2024
16c8827
Apply suggestions from code review
seanbudd May 8, 2024
fb3cebc
Merge branch 'master' into remoteOpsLowLevel
michaelDCurran May 13, 2024
84d11de
Review actions.
michaelDCurran May 13, 2024
a9f7e9f
Review actions - use match-case.
michaelDCurran May 13, 2024
1ed7829
Review acitons - return type.
michaelDCurran May 13, 2024
55fb396
Review actions - heading len constant.
michaelDCurran May 13, 2024
1653bad
Review actions - docstring
michaelDCurran May 13, 2024
fc033ef
Review actions: remote ops readme: mark Python code blocks as Python.…
michaelDCurran May 13, 2024
1165e77
Split UIA remote ops tests into files by category.
michaelDCurran May 13, 2024
e6ca54c
Further split UIA remote ops numeric tests into separate files.
michaelDCurran May 13, 2024
e850d4b
Apply suggestions from code review
michaelDCurran May 15, 2024
d1fbdc6
Merge remote-tracking branch 'origin/remoteOpsLowLevel' into remoteOp…
michaelDCurran May 15, 2024
0d9524c
Remove unneeded imports.
michaelDCurran May 15, 2024
1136d3b
Apply suggestions from code review
michaelDCurran May 15, 2024
0a719f2
Remote ops: convert instructions.py into a package, splitting all the…
michaelDCurran May 16, 2024
5289ea6
Linting
michaelDCurran May 16, 2024
12ac82d
remote ops: instructions package: expose BaseInstruction as well.
michaelDCurran May 19, 2024
48fe203
Move all Remote object classes out of remoteAPI.py into remoteTypes.p…
michaelDCurran May 19, 2024
d7c0ce2
Linting
michaelDCurran May 19, 2024
d717f65
Remote ops: convert remoteTypes.py into its own package.
michaelDCurran May 19, 2024
f8eb1d3
Split private enum utils out of remotetypes.py into _enum.py
michaelDCurran May 19, 2024
7720172
Fix imports
michaelDCurran May 19, 2024
734db69
Further split remoteTypes into separate modules.
michaelDCurran May 20, 2024
c4508ce
Linting
michaelDCurran May 20, 2024
bdb62a6
Apply suggestions from code review
seanbudd May 21, 2024
3785c02
Add return type
michaelDCurran May 22, 2024
0de175a
Linting
michaelDCurran May 22, 2024
b2a5e1b
Merge branch 'master' into remoteOpsLowLevel
michaelDCurran May 23, 2024
30710f2
Navigate element instruction: use match-case.
michaelDCurran May 23, 2024
b7ea2a4
Comparison instruction: use match-case.
michaelDCurran May 23, 2024
feff8cb
Move Stringify instruction from general to string.
michaelDCurran May 23, 2024
c17b0a8
RemoteTextRange: add missing return types.
michaelDCurran May 23, 2024
fb41e89
Add an isSupported function on UIAHandler.remote, and use that instea…
michaelDCurran May 24, 2024
f956d59
remote ops testcases: correct copyright years. Shorten some test names.
michaelDCurran May 24, 2024
f26e79c
Remote ops try_with_error testcase: also return and check i
michaelDCurran May 24, 2024
de76ad8
Remote ops testcases: add docstrings to complex instrucitonLimitExcee…
michaelDCurran May 27, 2024
b145782
UIAHandler.remote.terminate: just set _isSupported to false. There is…
michaelDCurran May 27, 2024
40a3af4
Update source/UIAHandler/_remoteOps/readme.md
seanbudd May 28, 2024
0c2efe0
Apply suggestions from code review
seanbudd May 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@
[submodule "include/nsis"]
path = include/nsis
url = https://github.com/nvaccess/nsis-build
[submodule "include/microsoft-ui-uiautomation"]
path = include/microsoft-ui-uiautomation
url = https://github.com/michaeldcurran/microsoft-ui-uiautomation
[submodule "include/wil"]
path = include/wil
url = https://github.com/microsoft/wil
Expand Down
1 change: 0 additions & 1 deletion include/microsoft-ui-uiautomation
Submodule microsoft-ui-uiautomation deleted from 224b22
6 changes: 0 additions & 6 deletions include/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@ Fetch latest from master.
### liblouis
TODO

### microsoft-ui-uiautomation
https://www.github.com/michaeldcurran/microsoft-ui-uiautomation/

We are stuck on our own fork.
The fork specifically adds support for connection bound objects to the high-level API, see PR microsoft/microsoft-ui-uiautomation#95.

### nsis
[NSIS-build readme](https://github.com/nvaccess/NSIS-build)

Expand Down
174 changes: 0 additions & 174 deletions nvdaHelper/UIARemote/UIARemote.cpp

This file was deleted.

192 changes: 192 additions & 0 deletions nvdaHelper/UIARemote/lowLevel.cpp
michaelDCurran marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#include <uiAutomationClient.h>
#include <winrt/windows.foundation.h>
#include <winrt/windows.foundation.collections.h>
#include <winrt/windows.ui.uiautomation.core.h>

using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::UIAutomation;
using namespace winrt::Windows::UI::UIAutomation::Core;

extern "C" __declspec(dllexport) HRESULT __stdcall remoteOp_create(void** ppRemoteOp) {
*ppRemoteOp = winrt::detach_abi(CoreAutomationRemoteOperation());
return S_OK;
}

extern "C" __declspec(dllexport) HRESULT __stdcall remoteOp_free(void* arg_pRemoteOp) {
CoreAutomationRemoteOperation operation {nullptr};
winrt::attach_abi(operation, arg_pRemoteOp);
return S_OK;
}

extern "C" __declspec(dllexport) HRESULT __stdcall remoteOp_importElement(void* arg_pRemoteOp, int arg_registerID, IUIAutomationElement* arg_pElement) {
CoreAutomationRemoteOperation operation {nullptr};
winrt::copy_from_abi(operation, arg_pRemoteOp);
if(!operation) {
return E_FAIL;
}
AutomationElement element {nullptr};
winrt::copy_from_abi(element, arg_pElement);
if(!element) {
return E_FAIL;
}
operation.ImportElement(AutomationRemoteOperationOperandId{arg_registerID}, element);
return S_OK;
}

extern "C" __declspec(dllexport) HRESULT __stdcall remoteOp_importTextRange(void* arg_pRemoteOp, int arg_registerID, IUIAutomationTextRange* arg_pTextRange) {
CoreAutomationRemoteOperation operation {nullptr};
winrt::copy_from_abi(operation, arg_pRemoteOp);
if(!operation) {
return E_FAIL;
}
AutomationTextRange textRange {nullptr};
winrt::copy_from_abi(textRange, arg_pTextRange);
if(!textRange) {
return E_FAIL;
}
operation.ImportTextRange(AutomationRemoteOperationOperandId{arg_registerID}, textRange);
return S_OK;
}

extern "C" __declspec(dllexport) HRESULT __stdcall remoteOp_addToResults(void* arg_pRemoteOp, int arg_registerID) {
CoreAutomationRemoteOperation operation {nullptr};
winrt::copy_from_abi(operation, arg_pRemoteOp);
if(!operation) {
return E_FAIL;
}
operation.AddToResults(AutomationRemoteOperationOperandId{arg_registerID});
return S_OK;
}

extern "C" __declspec(dllexport) HRESULT __stdcall remoteOp_isOpcodeSupported(void* arg_pRemoteOp, uint32_t arg_opcode, bool* pIsSupported) {
CoreAutomationRemoteOperation operation {nullptr};
winrt::copy_from_abi(operation, arg_pRemoteOp);
if(!operation) {
return E_FAIL;
}
*pIsSupported = operation.IsOpcodeSupported(arg_opcode);
return S_OK;
}

extern "C" __declspec(dllexport) HRESULT __stdcall remoteOp_execute(void* arg_pRemoteOp, uint8_t arg_byteCodeBuffer[], int arg_byteCodeBufferLength, void** ppResults) {
CoreAutomationRemoteOperation operation {nullptr};
winrt::copy_from_abi(operation, arg_pRemoteOp);
if(!operation) {
return E_FAIL;
}
auto results = operation.Execute(winrt::array_view<uint8_t>(arg_byteCodeBuffer, arg_byteCodeBufferLength));
*ppResults = winrt::detach_abi(results);
return S_OK;
}

extern "C" __declspec(dllexport) HRESULT __stdcall remoteOpResult_getErrorLocation(void* arg_pResults, int* pErrorLocation) {
AutomationRemoteOperationResult results {nullptr};
winrt::copy_from_abi(results, arg_pResults);
if(!results) {
return E_INVALIDARG;
}
*pErrorLocation = results.ErrorLocation();
return S_OK;
}

extern "C" __declspec(dllexport) HRESULT __stdcall remoteOpResult_getExtendedError(void* arg_pResults, HRESULT* pExtendedError) {
AutomationRemoteOperationResult results {nullptr};
winrt::copy_from_abi(results, arg_pResults);
if(!results) {
return E_INVALIDARG;
}
*pExtendedError = results.ExtendedError();
return S_OK;
}

extern "C" __declspec(dllexport) HRESULT __stdcall remoteOpResult_getStatus(void* arg_pResults, int* pStatus) {
AutomationRemoteOperationResult results {nullptr};
winrt::copy_from_abi(results, arg_pResults);
if(!results) {
return E_INVALIDARG;
}
auto status = results.Status();
*pStatus = static_cast<int>(status);
return S_OK;
}

extern "C" __declspec(dllexport) HRESULT __stdcall remoteOpResult_hasOperand(void* arg_pResults, int arg_registerID, bool* pHasOperand) {
AutomationRemoteOperationResult results {nullptr};
winrt::copy_from_abi(results, arg_pResults);
if(!results) {
return E_INVALIDARG;
}
*pHasOperand = results.HasOperand(AutomationRemoteOperationOperandId{arg_registerID});
return S_OK;
}

extern "C" __declspec(dllexport) HRESULT __stdcall remoteOpResult_free(void* arg_pResults) {
AutomationRemoteOperationResult results {nullptr};
winrt::attach_abi(results, arg_pResults);
return S_OK;
}

HRESULT IInspectableToVariant(winrt::Windows::Foundation::IInspectable result, VARIANT* arg_pVariant) {
if(!result) {
// operand is NULL.
return S_OK;
}
auto propVal = result.try_as<IPropertyValue>();
if(propVal) {
// Unbox property value into VARIANT
auto propType = propVal.Type();
switch(propVal.Type()) {
case PropertyType::Int32:
arg_pVariant->vt = VT_I4;
arg_pVariant->lVal = propVal.GetInt32();
break;
case PropertyType::String:
arg_pVariant->vt = VT_BSTR;
arg_pVariant->bstrVal = SysAllocString(propVal.GetString().c_str());
break;
case PropertyType::Boolean:
arg_pVariant->vt = VT_BOOL;
arg_pVariant->boolVal = propVal.GetBoolean() ? VARIANT_TRUE : VARIANT_FALSE;
break;
case PropertyType::Inspectable:
arg_pVariant->vt = VT_UNKNOWN;
arg_pVariant->punkVal = static_cast<::IUnknown*>(winrt::detach_abi(propVal.as<winrt::Windows::Foundation::IUnknown>()));
break;
default:
return E_NOTIMPL;
}
return S_OK;
}
auto vec = result.try_as<winrt::Windows::Foundation::Collections::IVector<winrt::Windows::Foundation::IInspectable>>();
if(vec) {
// Unbox vector into VARIANT array.
auto vecSize = vec.Size();
arg_pVariant->vt = VT_ARRAY | VT_VARIANT;
arg_pVariant->parray = SafeArrayCreateVector(VT_VARIANT, 0, vecSize);
if(!arg_pVariant->parray) {
return E_OUTOFMEMORY;
}
for(ULONG i = 0; i < vecSize; i++) {
auto vecItem = vec.GetAt(i);
auto hr = IInspectableToVariant(vecItem, &static_cast<VARIANT*>(arg_pVariant->parray->pvData)[i]);
if(FAILED(hr)) {
return hr;
}
}
return S_OK;
}
// Just treat it as an IUnknown.
arg_pVariant->vt = VT_UNKNOWN;
arg_pVariant->punkVal = static_cast<::IUnknown*>(winrt::detach_abi(result.as<winrt::Windows::Foundation::IUnknown>()));
return S_OK;
}

extern "C" __declspec(dllexport) HRESULT __stdcall remoteOpResult_getOperand(void* arg_pResults, int arg_registerID, VARIANT* arg_pVariant) {
AutomationRemoteOperationResult results {nullptr};
winrt::copy_from_abi(results, arg_pResults);
if(!results) {
return E_INVALIDARG;
}
auto result = results.GetOperand(AutomationRemoteOperationOperandId{arg_registerID});
return IInspectableToVariant(result, arg_pVariant);
}
Loading