Skip to content

Commit 1278bec

Browse files
authored
Add basic UIA provider to Fabric's CompositionRootView (#11129)
* commit to diff against main * yarn format * unintentional formatting changes * clean up test code * removing lines from bad merge * Change files * fix build break in desktop
1 parent 984fc4e commit 1278bec

12 files changed

+219
-2
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "Add root UIA provider",
4+
"packageName": "react-native-windows",
5+
"email": "adrum@microsoft.com",
6+
"dependentChangeType": "patch"
7+
}

packages/playground/windows/playground-composition/Playground-Composition.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "../../../../vnext/codegen/NativeDeviceInfoSpec.g.h"
1515

1616
#include <DispatcherQueue.h>
17+
#include <UIAutomation.h>
1718
#include <windows.ui.composition.interop.h>
1819

1920
#include <winrt/Microsoft.ReactNative.Composition.h>
@@ -351,6 +352,19 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
351352
SetProp(hwnd, WindowDataProperty, reinterpret_cast<HANDLE>(windowData));
352353
break;
353354
}
355+
case WM_GETOBJECT: {
356+
if (lparam == UiaRootObjectId) {
357+
auto windowData = WindowData::GetFromWindow(hwnd);
358+
if (!windowData->m_windowInited)
359+
break;
360+
361+
auto hwndHost = windowData->m_CompositionHwndHost;
362+
winrt::com_ptr<IRawElementProviderSimple> spReps;
363+
hwndHost.UiaProvider().as(spReps);
364+
LRESULT lResult = UiaReturnRawElementProvider(hwnd, wparam, lparam, spReps.get());
365+
return lResult;
366+
}
367+
}
354368
}
355369

356370
return DefWindowProc(hwnd, message, wparam, lparam);

vnext/Microsoft.ReactNative/CompositionHwndHost.idl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ namespace Microsoft.ReactNative
2828
IReactViewHost ReactViewHost { get; set; };
2929

3030
Int64 TranslateMessage(UInt32 msg, UInt64 wParam, Int64 lParam);
31+
32+
Object UiaProvider { get; };
3133
}
3234

33-
} // namespace Microsoft.ReactNative
35+
} // namespace Microsoft.ReactNative

vnext/Microsoft.ReactNative/CompositionRootView.idl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ namespace Microsoft.ReactNative
3737

3838
Int64 SendMessage(UInt32 Msg, UInt64 WParam, Int64 LParam);
3939

40+
Object GetUiaProvider(UInt64 hwnd);
41+
4042
//void OnPointerPressed(PointerPressedArgs args);
4143
//void OnMouseUp(Windows.Foundation.Point point);
4244
void OnScrollWheel(Windows.Foundation.Point point, Int32 delta);

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionHwndHost.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include "CompositionContextHelper.h"
1515
#include "ReactNativeHost.h"
1616

17+
#include "CompositionRootView.h"
18+
1719
WINUSERAPI UINT WINAPI GetDpiForWindow(_In_ HWND hwnd);
1820

1921
namespace winrt::Microsoft::ReactNative::implementation {
@@ -142,4 +144,9 @@ winrt::Windows::UI::Composition::Compositor CompositionHwndHost::Compositor() co
142144
compositionContext);
143145
}
144146

147+
IInspectable CompositionHwndHost::UiaProvider() noexcept {
148+
auto compRootView = winrt::get_self<implementation::CompositionRootView>(m_compRootView);
149+
return compRootView->GetUiaProvider(reinterpret_cast<uint64_t>(m_hwnd));
150+
}
151+
145152
} // namespace winrt::Microsoft::ReactNative::implementation

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionHwndHost.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ struct CompositionHwndHost : CompositionHwndHostT<CompositionHwndHost> {
2020
winrt::Microsoft::ReactNative::IReactViewHost ReactViewHost() const noexcept;
2121
void ReactViewHost(winrt::Microsoft::ReactNative::IReactViewHost const &value) noexcept;
2222

23+
// property UiaProvider
24+
IInspectable UiaProvider() noexcept;
25+
2326
winrt::Windows::UI::Composition::Visual RootVisual() const noexcept;
2427

2528
LRESULT TranslateMessage(int msg, uint64_t wParam, int64_t lParam) noexcept;
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#include "pch.h"
2+
#include "CompositionRootAutomationProvider.h"
3+
4+
#include "Fabric/Composition/CompositionRootView.h"
5+
6+
namespace winrt::Microsoft::ReactNative::implementation {
7+
8+
CompositionRootAutomationProvider::CompositionRootAutomationProvider(
9+
winrt::weak_ref<winrt::Microsoft::ReactNative::implementation::CompositionRootView> &&weakRootControl,
10+
HWND hwnd) noexcept
11+
: m_weakRootControl{std::move(weakRootControl)}, m_hwnd{hwnd} {}
12+
13+
// Implementations should return NULL for a top-level element that is hosted in a window. Other elements should return
14+
// an array that contains UiaAppendRuntimeId (defined in Uiautomationcoreapi.h), followed by a value that is unique
15+
// within an instance of the fragment.
16+
//
17+
// We'll use the View pointer as our identifier for those situations
18+
HRESULT __stdcall CompositionRootAutomationProvider::GetRuntimeId(SAFEARRAY **pRetVal) {
19+
if (pRetVal == nullptr)
20+
return E_POINTER;
21+
22+
*pRetVal = nullptr;
23+
24+
return S_OK;
25+
}
26+
27+
HRESULT __stdcall CompositionRootAutomationProvider::GetEmbeddedFragmentRoots(SAFEARRAY **pRetVal) {
28+
if (pRetVal == nullptr)
29+
return E_POINTER;
30+
31+
*pRetVal = nullptr;
32+
33+
return S_OK;
34+
}
35+
36+
HRESULT __stdcall CompositionRootAutomationProvider::SetFocus(void) {
37+
return S_OK;
38+
}
39+
40+
HRESULT __stdcall CompositionRootAutomationProvider::GetPatternProvider(PATTERNID patternId, IUnknown **pRetVal) {
41+
if (pRetVal == nullptr)
42+
return E_POINTER;
43+
44+
*pRetVal = nullptr;
45+
46+
return S_OK;
47+
}
48+
49+
HRESULT __stdcall CompositionRootAutomationProvider::GetPropertyValue(PROPERTYID propertyId, VARIANT *pRetVal) {
50+
if (pRetVal == nullptr)
51+
return E_POINTER;
52+
53+
return S_OK;
54+
}
55+
56+
HRESULT __stdcall CompositionRootAutomationProvider::get_HostRawElementProvider(IRawElementProviderSimple **pRetVal) {
57+
if (pRetVal == nullptr)
58+
return E_POINTER;
59+
60+
if (!IsWindow(m_hwnd))
61+
return UIA_E_ELEMENTNOTAVAILABLE;
62+
63+
auto hr = UiaHostProviderFromHwnd(m_hwnd, pRetVal);
64+
65+
return S_OK;
66+
}
67+
68+
HRESULT __stdcall CompositionRootAutomationProvider::get_BoundingRectangle(UiaRect *pRetVal) {
69+
if (pRetVal == nullptr)
70+
return E_POINTER;
71+
72+
return S_OK;
73+
}
74+
75+
HRESULT __stdcall CompositionRootAutomationProvider::get_FragmentRoot(IRawElementProviderFragmentRoot **pRetVal) {
76+
if (pRetVal == nullptr)
77+
return E_POINTER;
78+
79+
AddRef();
80+
*pRetVal = this;
81+
82+
return S_OK;
83+
}
84+
85+
HRESULT __stdcall CompositionRootAutomationProvider::get_ProviderOptions(ProviderOptions *pRetVal) {
86+
if (pRetVal == nullptr)
87+
return E_POINTER;
88+
89+
*pRetVal = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading;
90+
return S_OK;
91+
}
92+
93+
HRESULT __stdcall CompositionRootAutomationProvider::ElementProviderFromPoint(
94+
double x,
95+
double y,
96+
IRawElementProviderFragment **pRetVal) {
97+
if (pRetVal == nullptr)
98+
return E_POINTER;
99+
100+
*pRetVal = nullptr;
101+
102+
return S_OK;
103+
}
104+
105+
HRESULT __stdcall CompositionRootAutomationProvider::GetFocus(IRawElementProviderFragment **pRetVal) {
106+
if (pRetVal == nullptr)
107+
return E_POINTER;
108+
109+
*pRetVal = nullptr;
110+
111+
return S_OK;
112+
}
113+
114+
HRESULT __stdcall CompositionRootAutomationProvider::Navigate(
115+
NavigateDirection direction,
116+
IRawElementProviderFragment **pRetVal) {
117+
if (pRetVal == nullptr)
118+
return E_POINTER;
119+
120+
*pRetVal = nullptr;
121+
122+
return S_OK;
123+
}
124+
125+
} // namespace winrt::Microsoft::ReactNative::implementation
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#pragma once
2+
3+
#include <UIAutomation.h>
4+
#include <inspectable.h>
5+
6+
namespace winrt::Microsoft::ReactNative::implementation {
7+
struct CompositionRootView;
8+
9+
class CompositionRootAutomationProvider : public winrt::implements<
10+
CompositionRootAutomationProvider,
11+
IInspectable,
12+
IRawElementProviderFragmentRoot,
13+
IRawElementProviderFragment,
14+
IRawElementProviderSimple> {
15+
public:
16+
// inherited via IRawElementProviderFragmentRoot
17+
virtual HRESULT __stdcall ElementProviderFromPoint(double x, double y, IRawElementProviderFragment **pRetVal)
18+
override;
19+
virtual HRESULT __stdcall GetFocus(IRawElementProviderFragment **pRetVal) override;
20+
21+
// inherited via IRawElementProviderFragment
22+
virtual HRESULT __stdcall Navigate(NavigateDirection direction, IRawElementProviderFragment **pRetVal) override;
23+
virtual HRESULT __stdcall GetRuntimeId(SAFEARRAY **pRetVal) override;
24+
virtual HRESULT __stdcall get_BoundingRectangle(UiaRect *pRetVal) override;
25+
virtual HRESULT __stdcall GetEmbeddedFragmentRoots(SAFEARRAY **pRetVal) override;
26+
virtual HRESULT __stdcall SetFocus(void) override;
27+
virtual HRESULT __stdcall get_FragmentRoot(IRawElementProviderFragmentRoot **pRetVal) override;
28+
29+
// inherited via IRawElementProviderSimple
30+
virtual HRESULT __stdcall get_ProviderOptions(ProviderOptions *pRetVal) override;
31+
virtual HRESULT __stdcall GetPatternProvider(PATTERNID patternId, IUnknown **pRetVal) override;
32+
virtual HRESULT __stdcall GetPropertyValue(PROPERTYID propertyId, VARIANT *pRetVal) override;
33+
virtual HRESULT __stdcall get_HostRawElementProvider(IRawElementProviderSimple **pRetVal) override;
34+
35+
CompositionRootAutomationProvider(
36+
winrt::weak_ref<winrt::Microsoft::ReactNative::implementation::CompositionRootView> &&weakRootControl,
37+
HWND hwnd) noexcept;
38+
39+
private:
40+
winrt::weak_ref<winrt::Microsoft::ReactNative::implementation::CompositionRootView> m_weakRootControl;
41+
HWND m_hwnd;
42+
};
43+
44+
} // namespace winrt::Microsoft::ReactNative::implementation

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,14 @@ void CompositionRootView::ScaleFactor(double value) noexcept {
144144
m_scaleFactor = value;
145145
}
146146

147+
IInspectable CompositionRootView::GetUiaProvider(uint64_t hwnd) noexcept {
148+
if (m_compositionUiaProvider == nullptr) {
149+
m_compositionUiaProvider =
150+
winrt::make<CompositionRootAutomationProvider>(this->get_weak(), reinterpret_cast<HWND>(hwnd));
151+
}
152+
return m_compositionUiaProvider;
153+
}
154+
147155
winrt::Microsoft::ReactNative::Composition::IVisual CompositionRootView::GetVisual() const noexcept {
148156
return m_rootVisual;
149157
}

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <ReactContext.h>
88
#include <winrt/Microsoft.ReactNative.h>
99
#include "CompositionEventHandler.h"
10+
#include "CompositionRootAutomationProvider.h"
1011
#include "ReactHost/React.h"
1112
#include "Views/ICompositionRootView.h"
1213

@@ -34,6 +35,8 @@ struct CompositionRootView : CompositionRootViewT<CompositionRootView>, ::Micros
3435
Windows::Foundation::Size Measure(Windows::Foundation::Size const &availableSize) const;
3536
Windows::Foundation::Size Arrange(Windows::Foundation::Size finalSize) const;
3637

38+
IInspectable GetUiaProvider(uint64_t hWnd) noexcept;
39+
3740
int64_t SendMessage(uint32_t msg, uint64_t wParam, int64_t lParam) noexcept;
3841
void OnScrollWheel(Windows::Foundation::Point point, int32_t delta) noexcept;
3942

@@ -55,6 +58,7 @@ struct CompositionRootView : CompositionRootViewT<CompositionRootView>, ::Micros
5558
void UninitRootView() noexcept;
5659

5760
private:
61+
IInspectable m_compositionUiaProvider{nullptr};
5862
bool m_isInitialized{false};
5963
bool m_isJSViewAttached{false};
6064
IReactDispatcher m_uiDispatcher{nullptr};

0 commit comments

Comments
 (0)