From dac9f65182dd7f760c5ea9e6965d86db0779c59a Mon Sep 17 00:00:00 2001
From: Sharath Manchala <10109130+sharath2727@users.noreply.github.com>
Date: Tue, 2 Sep 2025 20:47:18 +0530
Subject: [PATCH 1/2] Inital validation
---
packages/sample-app-fabric/App.tsx | 5 +-
.../SampleAppFabric/SampleAppFabric.cpp | 4 +
.../SampleAppFabric/SampleAppFabric.vcxproj | 5 +-
vnext/Microsoft.ReactNative/App.xaml | 6 +
.../ContentIslandComponentView.cpp | 4 +-
.../Microsoft.ReactNative.vcxproj | 2 +-
vnext/Microsoft.ReactNative/Pch/pch.h | 17 ++
.../Microsoft.ReactNative/XamlApplication.cpp | 79 +++++++
vnext/Microsoft.ReactNative/XamlApplication.h | 47 ++++
.../Microsoft.ReactNative/XamlApplication.idl | 17 ++
vnext/Microsoft.ReactNative/XamlHost.cpp | 93 ++++++++
vnext/Microsoft.ReactNative/XamlHost.h | 10 +
.../microsoft.reactnative.def | 1 +
vnext/Shared/Shared.vcxitems | 24 +++
vnext/Shared/Shared.vcxitems.filters | 3 +
.../components/rnwcore/ComponentDescriptors.h | 1 +
.../components/rnwcore/EventEmitters.cpp | 1 +
.../react/components/rnwcore/EventEmitters.h | 7 +
.../react/components/rnwcore/Props.cpp | 7 +
.../codegen/react/components/rnwcore/Props.h | 10 +
.../react/components/rnwcore/ShadowNodes.cpp | 1 +
.../react/components/rnwcore/ShadowNodes.h | 11 +
.../codegen/react/components/rnwcore/States.h | 12 ++
.../react/components/rnwcore/XamlHost.g.h | 200 ++++++++++++++++++
vnext/fmt/packages.lock.json | 13 ++
.../Libraries/Components/Xaml/XamlHost.d.ts | 13 ++
.../Components/Xaml/XamlHost.windows.js | 7 +
vnext/src-win/index.windows.js | 4 +
.../Xaml/XamlHostNativeComponent.js | 19 ++
29 files changed, 617 insertions(+), 6 deletions(-)
create mode 100644 vnext/Microsoft.ReactNative/App.xaml
create mode 100644 vnext/Microsoft.ReactNative/XamlApplication.cpp
create mode 100644 vnext/Microsoft.ReactNative/XamlApplication.h
create mode 100644 vnext/Microsoft.ReactNative/XamlApplication.idl
create mode 100644 vnext/Microsoft.ReactNative/XamlHost.cpp
create mode 100644 vnext/Microsoft.ReactNative/XamlHost.h
create mode 100644 vnext/codegen/react/components/rnwcore/XamlHost.g.h
create mode 100644 vnext/fmt/packages.lock.json
create mode 100644 vnext/src-win/Libraries/Components/Xaml/XamlHost.d.ts
create mode 100644 vnext/src-win/Libraries/Components/Xaml/XamlHost.windows.js
create mode 100644 vnext/src-win/src/private/specs_DEPRECATED/components/Xaml/XamlHostNativeComponent.js
diff --git a/packages/sample-app-fabric/App.tsx b/packages/sample-app-fabric/App.tsx
index 3ac27a50420..53748ea279e 100644
--- a/packages/sample-app-fabric/App.tsx
+++ b/packages/sample-app-fabric/App.tsx
@@ -8,6 +8,7 @@
import React from 'react';
import {SafeAreaView, StatusBar, useColorScheme} from 'react-native';
import {NewAppScreen} from '@react-native/new-app-screen';
+import XamlHost from 'react-native-windows/Libraries/Components/Xaml/XamlHost';
function App(): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
@@ -18,7 +19,9 @@ function App(): React.JSX.Element {
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor="transparent"
translucent
- />
+ />
+
+
);
diff --git a/packages/sample-app-fabric/windows/SampleAppFabric/SampleAppFabric.cpp b/packages/sample-app-fabric/windows/SampleAppFabric/SampleAppFabric.cpp
index 2aeaae1397e..548eada80f2 100644
--- a/packages/sample-app-fabric/windows/SampleAppFabric/SampleAppFabric.cpp
+++ b/packages/sample-app-fabric/windows/SampleAppFabric/SampleAppFabric.cpp
@@ -8,12 +8,16 @@
#include "NativeModules.h"
+#include "../../../../vnext/Microsoft.ReactNative/XamlHost.h"
+
+
// A PackageProvider containing any turbo modules you define within this app project
struct CompReactPackageProvider
: winrt::implements {
public: // IReactPackageProvider
void CreatePackage(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept {
AddAttributedModules(packageBuilder, true);
+ RegisterXamlHostComponentView(packageBuilder);
}
};
diff --git a/packages/sample-app-fabric/windows/SampleAppFabric/SampleAppFabric.vcxproj b/packages/sample-app-fabric/windows/SampleAppFabric/SampleAppFabric.vcxproj
index 415ba11b336..3d40a5d0b84 100644
--- a/packages/sample-app-fabric/windows/SampleAppFabric/SampleAppFabric.vcxproj
+++ b/packages/sample-app-fabric/windows/SampleAppFabric/SampleAppFabric.vcxproj
@@ -77,8 +77,9 @@
4453;28204
- shell32.lib;user32.lib;windowsapp.lib;%(AdditionalDependenices)
- Windows
+ shell32.lib;user32.lib;windowsapp.lib;Microsoft.ReactNative.lib;%(AdditionalDependencies)
+ ..\..\..\..\vnext\target\x64\Debug\Microsoft.ReactNative\
+ Windows
true
diff --git a/vnext/Microsoft.ReactNative/App.xaml b/vnext/Microsoft.ReactNative/App.xaml
new file mode 100644
index 00000000000..3354935ec8d
--- /dev/null
+++ b/vnext/Microsoft.ReactNative/App.xaml
@@ -0,0 +1,6 @@
+
+
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp
index 57aa7d14656..37736a34f40 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp
@@ -178,14 +178,14 @@ ContentIslandComponentView::~ContentIslandComponentView() noexcept {
void ContentIslandComponentView::MountChildComponentView(
const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
uint32_t index) noexcept {
- assert(false);
+ //assert(false);
base_type::MountChildComponentView(childComponentView, index);
}
void ContentIslandComponentView::UnmountChildComponentView(
const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
uint32_t index) noexcept {
- assert(false);
+ //assert(false);
base_type::UnmountChildComponentView(childComponentView, index);
}
diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj
index b50fab3cadb..92f3f109ea4 100644
--- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj
+++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj
@@ -68,7 +68,7 @@
-
+
diff --git a/vnext/Microsoft.ReactNative/Pch/pch.h b/vnext/Microsoft.ReactNative/Pch/pch.h
index 34b0b02b9be..9447187a9c1 100644
--- a/vnext/Microsoft.ReactNative/Pch/pch.h
+++ b/vnext/Microsoft.ReactNative/Pch/pch.h
@@ -77,3 +77,20 @@
#include
#include
#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
diff --git a/vnext/Microsoft.ReactNative/XamlApplication.cpp b/vnext/Microsoft.ReactNative/XamlApplication.cpp
new file mode 100644
index 00000000000..4bc71b863ed
--- /dev/null
+++ b/vnext/Microsoft.ReactNative/XamlApplication.cpp
@@ -0,0 +1,79 @@
+#include "pch.h"
+#include "XamlApplication.h"
+#include "XamlApplication.g.cpp"
+
+#include "winrt/Microsoft.UI.Xaml.XamlTypeInfo.h"
+
+namespace winrt::Microsoft::ReactNative::implementation
+{
+using namespace ::winrt::Microsoft::UI::Xaml;
+using namespace ::winrt::Microsoft::UI::Xaml::Markup;
+using namespace ::winrt::Windows::UI::Xaml::Interop;
+ XamlApplication::XamlApplication()
+ {
+ //m_providers.push_back(winrt::make_self().as());
+ s_current = *this;
+
+ // TODO: It's probably not a good idea to only load the controls pri file, there are other ones too.
+ auto resourceManager =
+ winrt::Microsoft::Windows::ApplicationModel::Resources::ResourceManager(L"Microsoft.UI.Xaml.Controls.pri");
+
+ this->ResourceManagerRequested([resourceManager](auto &&, ResourceManagerRequestedEventArgs args) {
+ args.CustomResourceManager(resourceManager);
+ });
+ winrt::Microsoft::UI::Xaml::Hosting::WindowsXamlManager::InitializeForCurrentThread();
+
+ m_providers.push_back(
+ winrt::make_self()
+ .as()); // Default generated provider
+ m_providers.push_back(winrt::Microsoft::UI::Xaml::XamlTypeInfo::XamlControlsXamlMetaDataProvider());
+
+ auto winUIResources = winrt::Microsoft::UI::Xaml::Controls::XamlControlsResources();
+ Resources().MergedDictionaries().Append(winUIResources);
+ }
+
+ XamlApplication::~XamlApplication()
+ {
+ s_current = nullptr;
+ }
+
+ void XamlApplication::AddMetadataProvider(winrt::Microsoft::UI::Xaml::Markup::IXamlMetadataProvider const& otherProvider)
+ {
+ m_providers.push_back(otherProvider);
+ }
+
+ winrt::Microsoft::UI::Xaml::Markup::IXamlType XamlApplication::GetXamlType(winrt::Windows::UI::Xaml::Interop::TypeName const& type)
+ {
+ for (auto &&provider : m_providers) {
+ if (auto result = provider.GetXamlType(type)) {
+ return result;
+ }
+ }
+ return nullptr;
+ }
+
+ winrt::Microsoft::UI::Xaml::Markup::IXamlType XamlApplication::GetXamlType(hstring const& fullName)
+ {
+ for (auto &&provider : m_providers) {
+ if (auto result = provider.GetXamlType(fullName)) {
+ return result;
+ }
+ }
+
+ return nullptr;
+ }
+ com_array XamlApplication::GetXmlnsDefinitions()
+ {
+ std::vector<::winrt::Microsoft::UI::Xaml::Markup::XmlnsDefinition> allDefinitions;
+ for (const auto &provider : m_providers) {
+ auto definitionsCurrentProvider = provider.GetXmlnsDefinitions();
+ for (const auto &definition : definitionsCurrentProvider) {
+ allDefinitions.insert(allDefinitions.begin(), definition);
+ }
+ }
+ return winrt::com_array<::winrt::Microsoft::UI::Xaml::Markup::XmlnsDefinition>(
+ allDefinitions.begin(), allDefinitions.end());
+ }
+
+ winrt::Microsoft::ReactNative::XamlApplication XamlApplication::s_current{nullptr};
+}
diff --git a/vnext/Microsoft.ReactNative/XamlApplication.h b/vnext/Microsoft.ReactNative/XamlApplication.h
new file mode 100644
index 00000000000..6a9e6d3b7a6
--- /dev/null
+++ b/vnext/Microsoft.ReactNative/XamlApplication.h
@@ -0,0 +1,47 @@
+#pragma once
+#include "XamlApplication.g.h"
+
+#include "winrt/Microsoft.UI.Xaml.Hosting.h"
+#include "winrt/Microsoft.UI.Xaml.Interop.h"
+#include "winrt/Microsoft.UI.Xaml.Markup.h"
+#include "winrt/Microsoft.UI.Xaml.h"
+
+#include "winrt/Windows.UI.Xaml.Interop.h"
+
+#include "XamlMetaDataProvider.h"
+
+namespace winrt::Microsoft::ReactNative::implementation
+{
+ struct XamlApplication : XamlApplicationT
+ {
+ XamlApplication();
+ ~XamlApplication();
+
+ static void EnsureCreated()
+ {
+ if (Current() == nullptr) {
+ s_current = winrt::make();
+ }
+ }
+
+ static winrt::Microsoft::ReactNative::XamlApplication Current()
+ {
+ return s_current;
+ }
+
+ void AddMetadataProvider(winrt::Microsoft::UI::Xaml::Markup::IXamlMetadataProvider const& otherProvider);
+ winrt::Microsoft::UI::Xaml::Markup::IXamlType GetXamlType(winrt::Windows::UI::Xaml::Interop::TypeName const& type);
+ winrt::Microsoft::UI::Xaml::Markup::IXamlType GetXamlType(hstring const& fullName);
+ com_array GetXmlnsDefinitions();
+
+ private:
+ static winrt::Microsoft::ReactNative::XamlApplication s_current;
+ std::vector m_providers;
+ };
+}
+namespace winrt::Microsoft::ReactNative::factory_implementation
+{
+ struct XamlApplication : XamlApplicationT
+ {
+ };
+}
diff --git a/vnext/Microsoft.ReactNative/XamlApplication.idl b/vnext/Microsoft.ReactNative/XamlApplication.idl
new file mode 100644
index 00000000000..928ac01617f
--- /dev/null
+++ b/vnext/Microsoft.ReactNative/XamlApplication.idl
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.ReactNative {
+[webhosthidden][default_interface] runtimeclass XamlApplication : Microsoft.UI.Xaml.Application,
+ Microsoft.UI.Xaml.Markup.IXamlMetadataProvider {
+ XamlApplication();
+
+ static void EnsureCreated();
+
+ static XamlApplication Current {
+ get;
+ };
+
+ void AddMetadataProvider(Microsoft.UI.Xaml.Markup.IXamlMetadataProvider otherProvider);
+}
+} // namespace Microsoft.ReactNative. Xaml
diff --git a/vnext/Microsoft.ReactNative/XamlHost.cpp b/vnext/Microsoft.ReactNative/XamlHost.cpp
new file mode 100644
index 00000000000..779dc79688e
--- /dev/null
+++ b/vnext/Microsoft.ReactNative/XamlHost.cpp
@@ -0,0 +1,93 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#include "pch.h"
+
+#include "XamlApplication.h"
+#include "XamlHost.h"
+
+#if defined(RNW_NEW_ARCH)
+
+#include "..\codegen\react\components\rnwcore\XamlHost.g.h"
+
+namespace winrt::Microsoft::ReactNative {
+
+struct XamlHostComponentView : public winrt::implements,
+ ::Microsoft::ReactNativeSpecs::BaseXamlHost {
+ void InitializeContentIsland(
+ const winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView &islandView) noexcept {
+ winrt::Microsoft::ReactNative::implementation::XamlApplication::EnsureCreated();
+
+ m_xamlIsland = winrt::Microsoft::UI::Xaml::XamlIsland{};
+
+ /* auto grid = winrt::Microsoft::UI::Xaml::Controls::Grid{};
+ auto button = winrt::Microsoft::UI::Xaml::Controls::Button{};
+ button.Content(winrt::box_value(L"(This is placeholder content for the XamlHost.)"));
+ grid.Children().Append(button);
+ m_xamlIsland.Content(grid);
+ m_xamlIsland.Content(m_XamlHost);*/
+ // Create a Button control
+
+ winrt::Microsoft::UI::Xaml::Controls::TextBlock textBlock;
+ textBlock.Text(L"Hello from XAML!");
+ textBlock.HorizontalAlignment(winrt::Microsoft::UI::Xaml::HorizontalAlignment::Center);
+ textBlock.VerticalAlignment(winrt::Microsoft::UI::Xaml::VerticalAlignment::Center);
+
+ m_xamlIsland.Content(textBlock);
+
+ islandView.Connect(m_xamlIsland.ContentIsland());
+ }
+
+ void MountChildComponentView(
+ const winrt::Microsoft::ReactNative::ComponentView & /*view*/,
+ const winrt::Microsoft::ReactNative::MountChildComponentViewArgs &args) noexcept override {
+ // Add the xaml child to the m_xamlIsland here.
+ /*
+ auto childXamlControl = args.Child().UserData().as();
+ if (childXamlControl) {
+ auto xamlElement = childXamlControl.GetXamlElement();
+ m_xamlIsland.Content(xamlElement);
+ }
+ */
+
+ // Create a Button control
+ winrt::Microsoft::UI::Xaml::Controls::Button button;
+ button.Content(winrt::box_value(L"Click Me"));
+ button.HorizontalAlignment(winrt::Microsoft::UI::Xaml::HorizontalAlignment::Center);
+ button.VerticalAlignment(winrt::Microsoft::UI::Xaml::VerticalAlignment::Center);
+
+ // Attach Click event handler
+ button.Click([](winrt::IInspectable const &, winrt::Microsoft::UI::Xaml::RoutedEventArgs const &) {
+ OutputDebugString(L"Welcome to XAML world\n");
+ });
+
+ m_xamlIsland.Content(button);
+
+ }
+
+ void UnmountChildComponentView(
+ const winrt::Microsoft::ReactNative::ComponentView & /*view*/,
+ const winrt::Microsoft::ReactNative::UnmountChildComponentViewArgs &) noexcept override {
+ m_xamlIsland.Content(nullptr);
+ }
+
+ private:
+ winrt::Microsoft::UI::Xaml::XamlIsland m_xamlIsland{nullptr};
+};
+
+} // namespace winrt::Microsoft::ReactNative
+
+void RegisterXamlHostComponentView(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) {
+ ::Microsoft::ReactNativeSpecs::RegisterXamlHostNativeComponent(
+ packageBuilder,
+ [](const winrt::Microsoft::ReactNative::Composition::IReactCompositionViewComponentBuilder &builder) {
+ builder.SetContentIslandComponentViewInitializer(
+ [](const winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView &islandView) noexcept {
+ OutputDebugString(L"RegisterXamlHostComponentView called\n");
+ auto userData = winrt::make_self();
+ userData->InitializeContentIsland(islandView);
+ islandView.UserData(*userData);
+ });
+ });
+}
+
+#endif // defined(RNW_NEW_ARCH) && defined(USE_EXPERIMENTAL_WINUI3)
diff --git a/vnext/Microsoft.ReactNative/XamlHost.h b/vnext/Microsoft.ReactNative/XamlHost.h
new file mode 100644
index 00000000000..d64bb111f95
--- /dev/null
+++ b/vnext/Microsoft.ReactNative/XamlHost.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#if defined(RNW_NEW_ARCH)
+
+__declspec(dllexport) void RegisterXamlHostComponentView(
+ winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder);
+
+//void RegisterXamlControl(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder);
+
+#endif // defined(RNW_NEW_ARCH) && defined(USE_EXPERIMENTAL_WINUI3)
diff --git a/vnext/Microsoft.ReactNative/microsoft.reactnative.def b/vnext/Microsoft.ReactNative/microsoft.reactnative.def
index d04a02ea81e..faff126f6c4 100644
--- a/vnext/Microsoft.ReactNative/microsoft.reactnative.def
+++ b/vnext/Microsoft.ReactNative/microsoft.reactnative.def
@@ -1,3 +1,4 @@
EXPORTS
DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE
+RegisterXamlHostComponentView
diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems
index fbfc3740fb4..be03cff75f7 100644
--- a/vnext/Shared/Shared.vcxitems
+++ b/vnext/Shared/Shared.vcxitems
@@ -241,6 +241,16 @@
true
$(ReactNativeWindowsDir)Microsoft.ReactNative\ReactNativeAppBuilder.idl
Code
+
+
+ true
+ $(ReactNativeWindowsDir)Microsoft.ReactNative\XamlApplication.idl
+ Code
+
+
+ true
+ $(ReactNativeWindowsDir)Microsoft.ReactNative\XamlApplication.idl
+ Code
@@ -353,6 +363,16 @@
true
$(MSBuildThisFileDirectory)..\Microsoft.ReactNative\ReactNativeAppBuilder.idl
Code
+
+
+ true
+ $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\XamlApplication.idl
+ Code
+
+
+ true
+ $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\XamlApplication.idl
+ Code
$(MSBuildThisFileDirectory)..\Microsoft.ReactNative\IJSValueReader.idl
@@ -717,6 +737,7 @@
+
@@ -732,4 +753,7 @@
NotUsing
+
+
+
diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters
index 8a8fb167d88..a9fb6dc1699 100644
--- a/vnext/Shared/Shared.vcxitems.filters
+++ b/vnext/Shared/Shared.vcxitems.filters
@@ -858,4 +858,7 @@
+
+
+
\ No newline at end of file
diff --git a/vnext/codegen/react/components/rnwcore/ComponentDescriptors.h b/vnext/codegen/react/components/rnwcore/ComponentDescriptors.h
index 45b7b5ea904..f3d7509b0e9 100644
--- a/vnext/codegen/react/components/rnwcore/ComponentDescriptors.h
+++ b/vnext/codegen/react/components/rnwcore/ComponentDescriptors.h
@@ -23,6 +23,7 @@ using DebuggingOverlayComponentDescriptor = ConcreteComponentDescriptor;
using SwitchComponentDescriptor = ConcreteComponentDescriptor;
using UnimplementedNativeViewComponentDescriptor = ConcreteComponentDescriptor;
+using XamlHostComponentDescriptor = ConcreteComponentDescriptor;
void rnwcore_registerComponentDescriptorsFromCodegen(
std::shared_ptr registry);
diff --git a/vnext/codegen/react/components/rnwcore/EventEmitters.cpp b/vnext/codegen/react/components/rnwcore/EventEmitters.cpp
index b606e786965..4c80fea3870 100644
--- a/vnext/codegen/react/components/rnwcore/EventEmitters.cpp
+++ b/vnext/codegen/react/components/rnwcore/EventEmitters.cpp
@@ -129,4 +129,5 @@ payload.setProperty(runtime, "target", event.target);
}
+
} // namespace facebook::react
diff --git a/vnext/codegen/react/components/rnwcore/EventEmitters.h b/vnext/codegen/react/components/rnwcore/EventEmitters.h
index 0c3bce4a218..ccac8315736 100644
--- a/vnext/codegen/react/components/rnwcore/EventEmitters.h
+++ b/vnext/codegen/react/components/rnwcore/EventEmitters.h
@@ -165,5 +165,12 @@ class UnimplementedNativeViewEventEmitter : public ViewEventEmitter {
+};
+class XamlHostEventEmitter : public ViewEventEmitter {
+ public:
+ using ViewEventEmitter::ViewEventEmitter;
+
+
+
};
} // namespace facebook::react
diff --git a/vnext/codegen/react/components/rnwcore/Props.cpp b/vnext/codegen/react/components/rnwcore/Props.cpp
index 3d33bfb0980..9e1ca41bd42 100644
--- a/vnext/codegen/react/components/rnwcore/Props.cpp
+++ b/vnext/codegen/react/components/rnwcore/Props.cpp
@@ -153,5 +153,12 @@ UnimplementedNativeViewProps::UnimplementedNativeViewProps(
name(convertRawProp(context, rawProps, "name", sourceProps.name, {""}))
{}
+XamlHostProps::XamlHostProps(
+ const PropsParserContext &context,
+ const XamlHostProps &sourceProps,
+ const RawProps &rawProps): ViewProps(context, sourceProps, rawProps),
+
+ label(convertRawProp(context, rawProps, "label", sourceProps.label, {}))
+ {}
} // namespace facebook::react
diff --git a/vnext/codegen/react/components/rnwcore/Props.h b/vnext/codegen/react/components/rnwcore/Props.h
index 397bc1706cb..5805acbd7d5 100644
--- a/vnext/codegen/react/components/rnwcore/Props.h
+++ b/vnext/codegen/react/components/rnwcore/Props.h
@@ -395,4 +395,14 @@ class UnimplementedNativeViewProps final : public ViewProps {
std::string name{""};
};
+class XamlHostProps final : public ViewProps {
+ public:
+ XamlHostProps() = default;
+ XamlHostProps(const PropsParserContext& context, const XamlHostProps &sourceProps, const RawProps &rawProps);
+
+#pragma mark - Props
+
+ std::string label{};
+};
+
} // namespace facebook::react
diff --git a/vnext/codegen/react/components/rnwcore/ShadowNodes.cpp b/vnext/codegen/react/components/rnwcore/ShadowNodes.cpp
index dccefb1d478..627e9a2d3fe 100644
--- a/vnext/codegen/react/components/rnwcore/ShadowNodes.cpp
+++ b/vnext/codegen/react/components/rnwcore/ShadowNodes.cpp
@@ -19,5 +19,6 @@ extern const char DebuggingOverlayComponentName[] = "DebuggingOverlay";
extern const char PullToRefreshViewComponentName[] = "PullToRefreshView";
extern const char SwitchComponentName[] = "Switch";
extern const char UnimplementedNativeViewComponentName[] = "UnimplementedNativeView";
+extern const char XamlHostComponentName[] = "XamlHost";
} // namespace facebook::react
diff --git a/vnext/codegen/react/components/rnwcore/ShadowNodes.h b/vnext/codegen/react/components/rnwcore/ShadowNodes.h
index 2c7c94720cf..9b45530d5ad 100644
--- a/vnext/codegen/react/components/rnwcore/ShadowNodes.h
+++ b/vnext/codegen/react/components/rnwcore/ShadowNodes.h
@@ -95,4 +95,15 @@ using UnimplementedNativeViewShadowNode = ConcreteViewShadowNode<
UnimplementedNativeViewEventEmitter,
UnimplementedNativeViewState>;
+JSI_EXPORT extern const char XamlHostComponentName[];
+
+/*
+ * `ShadowNode` for component.
+ */
+using XamlHostShadowNode = ConcreteViewShadowNode<
+ XamlHostComponentName,
+ XamlHostProps,
+ XamlHostEventEmitter,
+ XamlHostState>;
+
} // namespace facebook::react
diff --git a/vnext/codegen/react/components/rnwcore/States.h b/vnext/codegen/react/components/rnwcore/States.h
index df8b9e54057..efc143b39cc 100644
--- a/vnext/codegen/react/components/rnwcore/States.h
+++ b/vnext/codegen/react/components/rnwcore/States.h
@@ -98,4 +98,16 @@ class UnimplementedNativeViewState {
#endif
};
+class XamlHostState {
+public:
+ XamlHostState() = default;
+
+#ifdef ANDROID
+ XamlHostState(XamlHostState const &previousState, folly::dynamic data){};
+ folly::dynamic getDynamic() const {
+ return {};
+ };
+#endif
+};
+
} // namespace facebook::react
\ No newline at end of file
diff --git a/vnext/codegen/react/components/rnwcore/XamlHost.g.h b/vnext/codegen/react/components/rnwcore/XamlHost.g.h
new file mode 100644
index 00000000000..26bd059c3c3
--- /dev/null
+++ b/vnext/codegen/react/components/rnwcore/XamlHost.g.h
@@ -0,0 +1,200 @@
+
+/*
+ * This file is auto-generated from XamlHostNativeComponent spec file in flow / TypeScript.
+ */
+// clang-format off
+#pragma once
+
+#include
+
+#ifdef RNW_NEW_ARCH
+#include
+
+#include
+#include
+#endif // #ifdef RNW_NEW_ARCH
+
+#ifdef RNW_NEW_ARCH
+
+namespace Microsoft::ReactNativeSpecs {
+
+REACT_STRUCT(XamlHostProps)
+struct XamlHostProps : winrt::implements {
+ XamlHostProps(winrt::Microsoft::ReactNative::ViewProps props, const winrt::Microsoft::ReactNative::IComponentProps& cloneFrom)
+ : ViewProps(props)
+ {
+ if (cloneFrom) {
+ auto cloneFromProps = cloneFrom.as();
+ label = cloneFromProps->label;
+ }
+ }
+
+ void SetProp(uint32_t hash, winrt::hstring propName, winrt::Microsoft::ReactNative::IJSValueReader value) noexcept {
+ winrt::Microsoft::ReactNative::ReadProp(hash, propName, value, *this);
+ }
+
+ REACT_FIELD(label)
+ std::optional label;
+
+ const winrt::Microsoft::ReactNative::ViewProps ViewProps;
+};
+
+struct XamlHostEventEmitter {
+ XamlHostEventEmitter(const winrt::Microsoft::ReactNative::EventEmitter &eventEmitter)
+ : m_eventEmitter(eventEmitter) {}
+
+ private:
+ winrt::Microsoft::ReactNative::EventEmitter m_eventEmitter{nullptr};
+};
+
+template
+struct BaseXamlHost {
+
+ virtual void UpdateProps(
+ const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::com_ptr &newProps,
+ const winrt::com_ptr &/*oldProps*/) noexcept {
+ m_props = newProps;
+ }
+
+ // UpdateLayoutMetrics will only be called if this method is overridden
+ virtual void UpdateLayoutMetrics(
+ const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &/*newLayoutMetrics*/,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &/*oldLayoutMetrics*/) noexcept {
+ }
+
+ // UpdateState will only be called if this method is overridden
+ virtual void UpdateState(
+ const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::IComponentState &/*newState*/) noexcept {
+ }
+
+ virtual void UpdateEventEmitter(const std::shared_ptr &eventEmitter) noexcept {
+ m_eventEmitter = eventEmitter;
+ }
+
+ // MountChildComponentView will only be called if this method is overridden
+ virtual void MountChildComponentView(const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::MountChildComponentViewArgs &/*args*/) noexcept {
+ }
+
+ // UnmountChildComponentView will only be called if this method is overridden
+ virtual void UnmountChildComponentView(const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::UnmountChildComponentViewArgs &/*args*/) noexcept {
+ }
+
+ // Initialize will only be called if this method is overridden
+ virtual void Initialize(const winrt::Microsoft::ReactNative::ComponentView &/*view*/) noexcept {
+ }
+
+ // CreateVisual will only be called if this method is overridden
+ virtual winrt::Microsoft::UI::Composition::Visual CreateVisual(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
+ return view.as().Compositor().CreateSpriteVisual();
+ }
+
+ // FinalizeUpdate will only be called if this method is overridden
+ virtual void FinalizeUpdate(const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ winrt::Microsoft::ReactNative::ComponentViewUpdateMask /*mask*/) noexcept {
+ }
+
+
+
+ const std::shared_ptr& EventEmitter() const { return m_eventEmitter; }
+ const winrt::com_ptr& Props() const { return m_props; }
+
+private:
+ winrt::com_ptr m_props;
+ std::shared_ptr m_eventEmitter;
+};
+
+template
+void RegisterXamlHostNativeComponent(
+ winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder,
+ std::function builderCallback) noexcept {
+ packageBuilder.as().AddViewComponent(
+ L"XamlHost", [builderCallback](winrt::Microsoft::ReactNative::IReactViewComponentBuilder const &builder) noexcept {
+ auto compBuilder = builder.as();
+
+ builder.SetCreateProps([](winrt::Microsoft::ReactNative::ViewProps props,
+ const winrt::Microsoft::ReactNative::IComponentProps& cloneFrom) noexcept {
+ return winrt::make(props, cloneFrom);
+ });
+
+ builder.SetUpdatePropsHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::IComponentProps &newProps,
+ const winrt::Microsoft::ReactNative::IComponentProps &oldProps) noexcept {
+ auto userData = view.UserData().as();
+ userData->UpdateProps(view, newProps ? newProps.as() : nullptr, oldProps ? oldProps.as() : nullptr);
+ });
+
+ compBuilder.SetUpdateLayoutMetricsHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &newLayoutMetrics,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &oldLayoutMetrics) noexcept {
+ auto userData = view.UserData().as();
+ userData->UpdateLayoutMetrics(view, newLayoutMetrics, oldLayoutMetrics);
+ });
+
+ builder.SetUpdateEventEmitterHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::EventEmitter &eventEmitter) noexcept {
+ auto userData = view.UserData().as();
+ userData->UpdateEventEmitter(std::make_shared(eventEmitter));
+ });
+
+ if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::FinalizeUpdate != &BaseXamlHost::FinalizeUpdate) {
+ builder.SetFinalizeUpdateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ winrt::Microsoft::ReactNative::ComponentViewUpdateMask mask) noexcept {
+ auto userData = view.UserData().as();
+ userData->FinalizeUpdate(view, mask);
+ });
+ }
+
+ if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::UpdateState != &BaseXamlHost::UpdateState) {
+ builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept {
+ auto userData = view.UserData().as();
+ userData->UpdateState(view, newState);
+ });
+ }
+
+ if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::MountChildComponentView != &BaseXamlHost::MountChildComponentView) {
+ builder.SetMountChildComponentViewHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::MountChildComponentViewArgs &args) noexcept {
+ auto userData = view.UserData().as();
+ return userData->MountChildComponentView(view, args);
+ });
+ }
+
+ if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::UnmountChildComponentView != &BaseXamlHost::UnmountChildComponentView) {
+ builder.SetUnmountChildComponentViewHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::UnmountChildComponentViewArgs &args) noexcept {
+ auto userData = view.UserData().as();
+ return userData->UnmountChildComponentView(view, args);
+ });
+ }
+
+ compBuilder.SetViewComponentViewInitializer([](const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
+ auto userData = winrt::make_self();
+ if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::Initialize != &BaseXamlHost::Initialize) {
+ userData->Initialize(view);
+ }
+ view.UserData(*userData);
+ });
+
+ if CONSTEXPR_SUPPORTED_ON_VIRTUAL_FN_ADDRESS (&TUserData::CreateVisual != &BaseXamlHost::CreateVisual) {
+ compBuilder.SetCreateVisualHandler([](const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
+ auto userData = view.UserData().as();
+ return userData->CreateVisual(view);
+ });
+ }
+
+ // Allow app to further customize the builder
+ if (builderCallback) {
+ builderCallback(compBuilder);
+ }
+ });
+}
+
+} // namespace Microsoft::ReactNativeSpecs
+
+#endif // #ifdef RNW_NEW_ARCH
diff --git a/vnext/fmt/packages.lock.json b/vnext/fmt/packages.lock.json
new file mode 100644
index 00000000000..a31237b580e
--- /dev/null
+++ b/vnext/fmt/packages.lock.json
@@ -0,0 +1,13 @@
+{
+ "version": 1,
+ "dependencies": {
+ "native,Version=v0.0": {},
+ "native,Version=v0.0/win10-arm": {},
+ "native,Version=v0.0/win10-arm-aot": {},
+ "native,Version=v0.0/win10-arm64-aot": {},
+ "native,Version=v0.0/win10-x64": {},
+ "native,Version=v0.0/win10-x64-aot": {},
+ "native,Version=v0.0/win10-x86": {},
+ "native,Version=v0.0/win10-x86-aot": {}
+ }
+}
\ No newline at end of file
diff --git a/vnext/src-win/Libraries/Components/Xaml/XamlHost.d.ts b/vnext/src-win/Libraries/Components/Xaml/XamlHost.d.ts
new file mode 100644
index 00000000000..42f34d63e58
--- /dev/null
+++ b/vnext/src-win/Libraries/Components/Xaml/XamlHost.d.ts
@@ -0,0 +1,13 @@
+/**
+ * Copyright (c) Microsoft Corporation.
+ * Licensed under the MIT License.
+ * @format
+ * @flow
+ */
+///
+import type {ViewProps} from 'react-native';
+export interface XamlHostProps extends ViewProps {
+ label: string;
+}
+declare const _default: import('react-native/Libraries/Utilities/codegenNativeComponent').NativeComponentType;
+export default _default;
\ No newline at end of file
diff --git a/vnext/src-win/Libraries/Components/Xaml/XamlHost.windows.js b/vnext/src-win/Libraries/Components/Xaml/XamlHost.windows.js
new file mode 100644
index 00000000000..c481f62c8fb
--- /dev/null
+++ b/vnext/src-win/Libraries/Components/Xaml/XamlHost.windows.js
@@ -0,0 +1,7 @@
+/**
+ * @format
+ * @flow
+ */
+
+import XamlHost from '../../../src/private/specs_DEPRECATED/components/Xaml/XamlHostNativeComponent';
+export default XamlHost;
\ No newline at end of file
diff --git a/vnext/src-win/index.windows.js b/vnext/src-win/index.windows.js
index c81a0b7b60a..d3b161b193a 100644
--- a/vnext/src-win/index.windows.js
+++ b/vnext/src-win/index.windows.js
@@ -46,6 +46,7 @@ import typeof SafeAreaView from './Libraries/Components/SafeAreaView/SafeAreaVie
import typeof ScrollView from './Libraries/Components/ScrollView/ScrollView';
import typeof StatusBar from './Libraries/Components/StatusBar/StatusBar';
import typeof Switch from './Libraries/Components/Switch/Switch';
+import typeof XamlHost from './Libraries/Components/Xaml/XamlHost';
import typeof InputAccessoryView from './Libraries/Components/TextInput/InputAccessoryView';
import TextInput from './Libraries/Components/TextInput/TextInput';
import typeof ToastAndroid from './Libraries/Components/ToastAndroid/ToastAndroid';
@@ -183,6 +184,9 @@ module.exports = {
get Switch(): Switch {
return require('./Libraries/Components/Switch/Switch').default;
},
+ get XamlHost(): XamlHost {
+ return require('./Libraries/Components/Xaml/XamlHost').default;
+ },
get Text(): Text {
return require('./Libraries/Text/Text').default;
},
diff --git a/vnext/src-win/src/private/specs_DEPRECATED/components/Xaml/XamlHostNativeComponent.js b/vnext/src-win/src/private/specs_DEPRECATED/components/Xaml/XamlHostNativeComponent.js
new file mode 100644
index 00000000000..4cf39730380
--- /dev/null
+++ b/vnext/src-win/src/private/specs_DEPRECATED/components/Xaml/XamlHostNativeComponent.js
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) Microsoft Corporation.
+ * Licensed under the MIT License.
+ * @format
+ * @flow
+ */
+
+'use strict';
+
+import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
+
+type XamlHostProps = $ReadOnly<{|
+ ...ViewProps,
+
+ // Props
+ label?: string,
+|}>;
+
+export default codegenNativeComponent('XamlHost');
From 1136065fff7da8e99c34ac126f54e81ae452ea6a Mon Sep 17 00:00:00 2001
From: Sharath Manchala <10109130+sharath2727@users.noreply.github.com>
Date: Tue, 16 Sep 2025 15:59:45 +0530
Subject: [PATCH 2/2] Accessibility validation
---
packages/sample-app-fabric/App.tsx | 7 ++-
.../CompositionDynamicAutomationProvider.cpp | 27 ++++++++++
.../CompositionDynamicAutomationProvider.h | 8 +++
.../CompositionRootAutomationProvider.cpp | 3 +-
.../CompositionViewComponentView.cpp | 4 +-
.../ContentIslandComponentView.cpp | 53 ++++++++++++++++++-
.../Composition/ContentIslandComponentView.h | 4 ++
.../Fabric/Composition/RootComponentView.cpp | 36 ++++++++++++-
.../Fabric/Composition/RootComponentView.h | 2 +-
vnext/Microsoft.ReactNative/XamlHost.cpp | 40 ++++++++++++--
10 files changed, 170 insertions(+), 14 deletions(-)
diff --git a/packages/sample-app-fabric/App.tsx b/packages/sample-app-fabric/App.tsx
index 53748ea279e..d0d90d03cde 100644
--- a/packages/sample-app-fabric/App.tsx
+++ b/packages/sample-app-fabric/App.tsx
@@ -6,7 +6,7 @@
*/
import React from 'react';
-import {SafeAreaView, StatusBar, useColorScheme} from 'react-native';
+import {View, SafeAreaView, StatusBar, useColorScheme, ScrollView} from 'react-native';
import {NewAppScreen} from '@react-native/new-app-screen';
import XamlHost from 'react-native-windows/Libraries/Components/Xaml/XamlHost';
@@ -20,9 +20,8 @@ function App(): React.JSX.Element {
backgroundColor="transparent"
translucent
/>
-
-
-
+
+
);
}
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
index d67f6d9ca29..449c608964b 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
@@ -68,11 +68,19 @@ CompositionDynamicAutomationProvider::CompositionDynamicAutomationProvider(
HRESULT __stdcall CompositionDynamicAutomationProvider::Navigate(
NavigateDirection direction,
IRawElementProviderFragment **pRetVal) {
+
+ OutputDebugString(L"[UIA] Navigate returning provider instance: ");
+ OutputDebugString(std::to_wstring(reinterpret_cast(this)).c_str());
+ OutputDebugString(L"\n");
+
if (pRetVal == nullptr)
return E_POINTER;
if (m_childSiteLink) {
if (direction == NavigateDirection_FirstChild || direction == NavigateDirection_LastChild) {
+ OutputDebugString(L"[UIA] Navigate returning provider instance - 2: ");
+ OutputDebugString(std::to_wstring(reinterpret_cast(this)).c_str());
+ OutputDebugString(L"\n");
auto fragment = m_childSiteLink.AutomationProvider().try_as();
*pRetVal = fragment.detach();
return S_OK;
@@ -115,6 +123,18 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::get_BoundingRectangle(Ui
if (pRetVal == nullptr)
return E_POINTER;
+ OutputDebugString(L"[UIA] get_BoundingRectangle called: ");
+
+ if (m_childSiteLink == nullptr) {
+ OutputDebugString(L"[UIA] m_childSiteLink is nullptr — using fallback RNW bounds\n");
+ OutputDebugString(std::to_wstring(reinterpret_cast(this)).c_str());
+ OutputDebugString(L"\n");
+ } else {
+ OutputDebugString(L"[UIA] m_childSiteLink is valid — expected to use XamlIsland bounds\n");
+ OutputDebugString(std::to_wstring(reinterpret_cast(this)).c_str());
+ OutputDebugString(L"\n");
+ }
+
auto hr = UiaGetBoundingRectangleHelper(m_view, *pRetVal);
if (FAILED(hr))
return hr;
@@ -161,6 +181,13 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::SetFocus(void) {
return UiaSetFocusHelper(m_view);
}
+winrt::IUnknown CompositionDynamicAutomationProvider::TryGetChildSiteLinkAutomationProvider() {
+ if (m_childSiteLink) {
+ return m_childSiteLink.AutomationProvider().as();
+ }
+ return nullptr;
+}
+
HRESULT __stdcall CompositionDynamicAutomationProvider::get_FragmentRoot(IRawElementProviderFragmentRoot **pRetVal) {
if (pRetVal == nullptr)
return E_POINTER;
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h
index 7009a6e21b8..5049a52c4b3 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h
@@ -98,6 +98,14 @@ class CompositionDynamicAutomationProvider : public winrt::implements<
void AddToSelectionItems(winrt::com_ptr &item);
void RemoveFromSelectionItems(winrt::com_ptr &item);
+ void SetChildSiteLink(winrt::Microsoft::UI::Content::ChildSiteLink childSiteLink) {
+ m_childSiteLink = childSiteLink;
+ }
+
+ // If this object is for a ChildSiteLink, returns the ChildSiteLink's automation provider.
+ // This will be a provider object from the hosted framework (for example, WinUI).
+ winrt::IUnknown TryGetChildSiteLinkAutomationProvider();
+
private:
::Microsoft::ReactNative::ReactTaggedView m_view;
winrt::com_ptr m_textProvider;
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp
index 6ead642c857..e254eb4c367 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp
@@ -219,7 +219,8 @@ HRESULT __stdcall CompositionRootAutomationProvider::ElementProviderFromPoint(
auto local = rootView->ConvertScreenToLocal({static_cast(x), static_cast(y)});
auto provider = rootView->UiaProviderFromPoint(
{static_cast(local.X * rootView->LayoutMetrics().PointScaleFactor),
- static_cast(local.Y * rootView->LayoutMetrics().PointScaleFactor)});
+ static_cast(local.Y * rootView->LayoutMetrics().PointScaleFactor)},
+ {static_cast(x), static_cast(y)});
auto spFragment = provider.try_as();
if (spFragment) {
*pRetVal = spFragment.detach();
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp
index 681a93c976c..bb7619ebca2 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp
@@ -1003,11 +1003,11 @@ std::string ComponentView::DefaultControlType() const noexcept {
}
std::string ComponentView::DefaultAccessibleName() const noexcept {
- return "";
+ return "DefaultAccessString";
}
std::string ComponentView::DefaultHelpText() const noexcept {
- return "";
+ return "DefaultHelpString";
}
facebook::react::SharedViewProps ViewComponentView::defaultProps() noexcept {
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp
index 37736a34f40..719c735a280 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp
@@ -64,9 +64,11 @@ void ContentIslandComponentView::OnMounted() noexcept {
// We configure automation even if there's no UIA client at this point, because it's possible the first UIA
// request we'll get will be for a child of this island calling upward in the UIA tree.
ConfigureChildSiteLinkAutomation();
+ OutputDebugString(L"Configure childsite Connected");
if (m_islandToConnect) {
m_childSiteLink.Connect(m_islandToConnect);
+ OutputDebugString(L"ChildSiteLinK Connected");
m_islandToConnect = nullptr;
}
@@ -85,6 +87,25 @@ void ContentIslandComponentView::OnMounted() noexcept {
}
}
+facebook::react::Tag ContentIslandComponentView::hitTest(
+ facebook::react::Point pt,
+ facebook::react::Point &localPt,
+ bool ignorePointerEvents) const noexcept {
+ facebook::react::Point ptLocal{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y};
+
+ // This is similar to ViewComponentView::hitTest, but we don't want to hit test the children of this node,
+ // because the child ComponentView is kind of a dummy representation of the XamlElement and doesn't do anything.
+ // So, we just hit test the ContentIsland itself to make the UIA ElementProviderFromPoint call work.
+ // TODO: Will this cause a problem -- does this function need to do something different for non-UIA scenarios?
+ if (ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 &&
+ ptLocal.y <= m_layoutMetrics.frame.size.height) {
+ localPt = ptLocal;
+ return Tag();
+ }
+
+ return -1;
+}
+
void ContentIslandComponentView::OnUnmounted() noexcept {
m_layoutMetricChangedRevokers.clear();
if (m_navigationHostDepartFocusRequestedToken && m_navigationHost) {
@@ -115,6 +136,7 @@ winrt::IInspectable ContentIslandComponentView::EnsureUiaProvider() noexcept {
if (m_uiaProvider == nullptr) {
m_uiaProvider = winrt::make(
*get_strong(), m_childSiteLink);
+ OutputDebugString(L"[EnsureUiaProvider] Created CompositionDynamicAutomationProvider\n");
}
return m_uiaProvider;
}
@@ -196,6 +218,7 @@ void ContentIslandComponentView::updateLayoutMetrics(
m_childSiteLink.ActualSize({layoutMetrics.frame.size.width, layoutMetrics.frame.size.height});
ParentLayoutChanged();
}
+
base_type::updateLayoutMetrics(layoutMetrics, oldLayoutMetrics);
}
@@ -203,8 +226,10 @@ void ContentIslandComponentView::Connect(const winrt::Microsoft::UI::Content::Co
if (m_childSiteLink) {
m_islandToConnect = nullptr;
m_childSiteLink.Connect(contentIsland);
+ OutputDebugString(L"1-ChildSiteLink connected to XamlIsland\n");
} else {
m_islandToConnect = contentIsland;
+ OutputDebugString(L"2-ChildSiteLink connected to XamlIsland\n");
}
}
@@ -217,7 +242,7 @@ void ContentIslandComponentView::ConfigureChildSiteLinkAutomation() noexcept {
// It puts the child content into a mode where it won't own its own framework root. Instead, the child island's
// automation peers will use the same framework root as the automation peer of this ContentIslandComponentView.
m_childSiteLink.AutomationOption(winrt::Microsoft::UI::Content::ContentAutomationOptions::FragmentBased);
-
+ //OutputDebugString(L"[ContentIsland] AutomationOption set to FragmentBased\n");
// These events are raised in response to the child ContentIsland asking for providers.
// For example, the ContentIsland.FragmentRootAutomationProvider property will return
// the provider we provide here in FragmentRootAutomationProviderRequested.
@@ -231,10 +256,20 @@ void ContentIslandComponentView::ConfigureChildSiteLinkAutomation() noexcept {
const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) {
// The child island's fragment tree doesn't have its own fragment root.
// Here's how we can provide the correct fragment root to the child's UIA logic.
+ // OutputDebugString(L"[ContentIsland] FragmentRootAutomationProviderRequested entered\n");
winrt::com_ptr fragmentRoot{nullptr};
auto uiaProvider = this->EnsureUiaProvider();
+ // OutputDebugString(L"[UIA] FragmentRootAutomationProviderRequested uiaprovider: ");
+
+ OutputDebugString(std::to_wstring(reinterpret_cast(winrt::get_abi(uiaProvider))).c_str());
+ OutputDebugString(L"\n");
+
uiaProvider.as()->get_FragmentRoot(fragmentRoot.put());
+ OutputDebugString(L"[UIA] FragmentRoot pointer: ");
+ OutputDebugString(std::to_wstring(reinterpret_cast(fragmentRoot.get())).c_str());
+ OutputDebugString(L"\n");
args.AutomationProvider(fragmentRoot.as());
+ OutputDebugString(L"[ContentIsland] FragmentRootAutomationProviderRequested handled\n");
args.Handled(true);
});
@@ -242,8 +277,14 @@ void ContentIslandComponentView::ConfigureChildSiteLinkAutomation() noexcept {
[this](
const winrt::Microsoft::UI::Content::IContentSiteAutomation &,
const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) {
+ OutputDebugString(L"[ContentIsland] ParentAutomationProviderRequested entered\n");
auto uiaProvider = this->EnsureUiaProvider();
+ OutputDebugString(L"[UIA] ParentAutomationProviderRequested uiaprovider: ");
+
+ OutputDebugString(std::to_wstring(reinterpret_cast(winrt::get_abi(uiaProvider))).c_str());
+ OutputDebugString(L"\n");
args.AutomationProvider(uiaProvider);
+ OutputDebugString(L"[ContentIsland] ParentAutomationProviderRequested handled\n");
args.Handled(true);
});
@@ -251,7 +292,9 @@ void ContentIslandComponentView::ConfigureChildSiteLinkAutomation() noexcept {
[](const winrt::Microsoft::UI::Content::IContentSiteAutomation &,
const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) {
// The ContentIsland will always be the one and only child of this node, so it won't have siblings.
+ OutputDebugString(L"[ContentIsland] NextSiblingAutomationProviderRequested entered\n");
args.AutomationProvider(nullptr);
+ OutputDebugString(L"[ContentIsland] m_nextSiblingAutomationProviderRequestedToken handled\n");
args.Handled(true);
});
@@ -259,9 +302,17 @@ void ContentIslandComponentView::ConfigureChildSiteLinkAutomation() noexcept {
[](const winrt::Microsoft::UI::Content::IContentSiteAutomation &,
const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) {
// The ContentIsland will always be the one and only child of this node, so it won't have siblings.
+ OutputDebugString(L"[ContentIsland] PreviousSiblingAutomationProviderRequested entered\n");
args.AutomationProvider(nullptr);
+ OutputDebugString(L"[ContentIsland] m_previousSiblingAutomationProviderRequestedToken handled\n");
args.Handled(true);
});
+
+ if (m_uiaProvider) {
+ auto providerImpl =
+ m_uiaProvider.as();
+ providerImpl->SetChildSiteLink(m_childSiteLink);
+ }
}
} // namespace winrt::Microsoft::ReactNative::Composition::implementation
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h
index f530baa3400..8f51b76b231 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h
@@ -51,6 +51,10 @@ struct ContentIslandComponentView : ContentIslandComponentViewT
#include
#include "CompositionRootAutomationProvider.h"
+#include "CompositionDynamicAutomationProvider.h"
#include "ReactNativeIsland.h"
#include "Theme.h"
@@ -275,7 +276,7 @@ facebook::react::Point RootComponentView::getClientOffset() const noexcept {
return {};
}
-winrt::IInspectable RootComponentView::UiaProviderFromPoint(const POINT &ptPixels) noexcept {
+winrt::IUnknown RootComponentView::UiaProviderFromPoint(const POINT &ptPixels, const POINT &ptScreen) noexcept {
facebook::react::Point ptDips{
static_cast(ptPixels.x) / m_layoutMetrics.pointScaleFactor,
static_cast(ptPixels.y) / m_layoutMetrics.pointScaleFactor};
@@ -295,7 +296,38 @@ winrt::IInspectable RootComponentView::UiaProviderFromPoint(const POINT &ptPixel
if (view == nullptr)
return nullptr;
- return winrt::get_self(view)->EnsureUiaProvider();
+ // return winrt::get_self(view)->EnsureUiaProvider();
+ auto uiaProvider =
+ winrt::get_self(view)->EnsureUiaProvider();
+
+ // HACKHACK: It's ugly to have the RootComponentView know about the CompositionDynamicAutomationProvider type.
+ // HACKHACK: Can we clean this up?
+ auto dynamicProvider =
+ uiaProvider.try_as();
+ if (dynamicProvider) {
+ if (auto childProvider = dynamicProvider->TryGetChildSiteLinkAutomationProvider()) {
+ // childProvider is the the automation provider from the ChildSiteLink. In the case of WinUI, this
+ // is a pointer to WinUI's internal CUIAHostWindow object.
+ // It seems odd, but even though this node doesn't behave as a fragment root in our case (the real fragment root
+ // is the RootComponentView's UIA provider), we still use its IRawElementProviderFragmentRoot -- just so
+ // we can do the ElementProviderFromPoint call. (this was recommended by the team who did the initial
+ // architecture work).
+ if (auto fragmentRoot = childProvider.try_as()) {
+ com_ptr frag;
+ fragmentRoot->ElementProviderFromPoint(
+ ptScreen.x, // Note since we're going through IRawElementProviderFragment the coordinates are in screen space.
+ ptScreen.y,
+ frag.put());
+ // In the case of WinUI, frag is now the UIA provider for the specific WinUI element that was hit.
+ // (A Microsoft_UI_Xaml!CUIAWrapper object)
+ if (frag) {
+ return frag.as();
+ }
+ }
+ }
+ }
+
+ return uiaProvider;
}
float RootComponentView::FontSizeMultiplier() const noexcept {
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h
index 3a037fc6d61..83f633d212e 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h
@@ -64,7 +64,7 @@ struct RootComponentView : RootComponentViewT
+#include
#if defined(RNW_NEW_ARCH)
@@ -27,12 +29,45 @@ struct XamlHostComponentView : public winrt::implements();
+
+ auto string = buttonPeer.GetHelpText();
+
+ button.IsTabStop(true);
+ button.TabIndex(0);
+
+ m_xamlIsland.Content(button);
+ OutputDebugString(L"[XamlHost] Setting Content\n");
islandView.Connect(m_xamlIsland.ContentIsland());
}
@@ -82,7 +117,6 @@ void RegisterXamlHostComponentView(winrt::Microsoft::ReactNative::IReactPackageB
[](const winrt::Microsoft::ReactNative::Composition::IReactCompositionViewComponentBuilder &builder) {
builder.SetContentIslandComponentViewInitializer(
[](const winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView &islandView) noexcept {
- OutputDebugString(L"RegisterXamlHostComponentView called\n");
auto userData = winrt::make_self();
userData->InitializeContentIsland(islandView);
islandView.UserData(*userData);