diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index ea0738c72c1b3..77ad3e05aa1a6 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -3277,6 +3277,7 @@ FILE: ../../../flutter/shell/platform/windows/windows_proc_table.cc FILE: ../../../flutter/shell/platform/windows/windows_proc_table.h FILE: ../../../flutter/shell/platform/windows/windows_registry.cc FILE: ../../../flutter/shell/platform/windows/windows_registry.h +FILE: ../../../flutter/shell/platform/windows/windowsx_shim.h FILE: ../../../flutter/shell/profiling/sampling_profiler.cc FILE: ../../../flutter/shell/profiling/sampling_profiler.h FILE: ../../../flutter/shell/profiling/sampling_profiler_unittest.cc diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 53b997f5847c4..42f4c16f73bf8 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -111,6 +111,7 @@ source_set("flutter_windows_source") { "windows_proc_table.h", "windows_registry.cc", "windows_registry.h", + "windowsx_shim.h", ] libs = [ diff --git a/shell/platform/windows/accessibility_bridge_windows.cc b/shell/platform/windows/accessibility_bridge_windows.cc index add1fc7f7c036..83435ce3a41b1 100644 --- a/shell/platform/windows/accessibility_bridge_windows.cc +++ b/shell/platform/windows/accessibility_bridge_windows.cc @@ -31,53 +31,67 @@ void AccessibilityBridgeWindows::OnAccessibilityEvent( switch (event_type) { case ui::AXEventGenerator::Event::ALERT: - DispatchWinAccessibilityEvent(win_delegate, EVENT_SYSTEM_ALERT); + DispatchWinAccessibilityEvent(win_delegate, ax::mojom::Event::kAlert); break; case ui::AXEventGenerator::Event::CHECKED_STATE_CHANGED: - DispatchWinAccessibilityEvent(win_delegate, EVENT_OBJECT_VALUECHANGE); + DispatchWinAccessibilityEvent(win_delegate, + ax::mojom::Event::kValueChanged); break; case ui::AXEventGenerator::Event::CHILDREN_CHANGED: - DispatchWinAccessibilityEvent(win_delegate, EVENT_OBJECT_REORDER); + DispatchWinAccessibilityEvent(win_delegate, + ax::mojom::Event::kChildrenChanged); + break; + case ui::AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED: + DispatchWinAccessibilityEvent( + win_delegate, ax::mojom::Event::kDocumentSelectionChanged); break; case ui::AXEventGenerator::Event::FOCUS_CHANGED: - DispatchWinAccessibilityEvent(win_delegate, EVENT_OBJECT_FOCUS); + DispatchWinAccessibilityEvent(win_delegate, ax::mojom::Event::kFocus); SetFocus(win_delegate); break; case ui::AXEventGenerator::Event::IGNORED_CHANGED: if (ax_node->IsIgnored()) { - DispatchWinAccessibilityEvent(win_delegate, EVENT_OBJECT_HIDE); + DispatchWinAccessibilityEvent(win_delegate, ax::mojom::Event::kHide); } break; case ui::AXEventGenerator::Event::IMAGE_ANNOTATION_CHANGED: - DispatchWinAccessibilityEvent(win_delegate, EVENT_OBJECT_NAMECHANGE); + DispatchWinAccessibilityEvent(win_delegate, + ax::mojom::Event::kTextChanged); break; case ui::AXEventGenerator::Event::LIVE_REGION_CHANGED: DispatchWinAccessibilityEvent(win_delegate, - EVENT_OBJECT_LIVEREGIONCHANGED); + ax::mojom::Event::kLiveRegionChanged); break; case ui::AXEventGenerator::Event::NAME_CHANGED: - DispatchWinAccessibilityEvent(win_delegate, EVENT_OBJECT_NAMECHANGE); + DispatchWinAccessibilityEvent(win_delegate, + ax::mojom::Event::kTextChanged); break; case ui::AXEventGenerator::Event::SCROLL_HORIZONTAL_POSITION_CHANGED: - DispatchWinAccessibilityEvent(win_delegate, EVENT_SYSTEM_SCROLLINGEND); + DispatchWinAccessibilityEvent(win_delegate, + ax::mojom::Event::kScrollPositionChanged); break; case ui::AXEventGenerator::Event::SCROLL_VERTICAL_POSITION_CHANGED: - DispatchWinAccessibilityEvent(win_delegate, EVENT_SYSTEM_SCROLLINGEND); + DispatchWinAccessibilityEvent(win_delegate, + ax::mojom::Event::kScrollPositionChanged); break; case ui::AXEventGenerator::Event::SELECTED_CHANGED: - DispatchWinAccessibilityEvent(win_delegate, EVENT_OBJECT_VALUECHANGE); + DispatchWinAccessibilityEvent(win_delegate, + ax::mojom::Event::kValueChanged); break; case ui::AXEventGenerator::Event::SELECTED_CHILDREN_CHANGED: - DispatchWinAccessibilityEvent(win_delegate, EVENT_OBJECT_SELECTIONWITHIN); + DispatchWinAccessibilityEvent(win_delegate, + ax::mojom::Event::kSelectedChildrenChanged); break; case ui::AXEventGenerator::Event::SUBTREE_CREATED: - DispatchWinAccessibilityEvent(win_delegate, EVENT_OBJECT_SHOW); + DispatchWinAccessibilityEvent(win_delegate, ax::mojom::Event::kShow); break; case ui::AXEventGenerator::Event::VALUE_CHANGED: - DispatchWinAccessibilityEvent(win_delegate, EVENT_OBJECT_VALUECHANGE); + DispatchWinAccessibilityEvent(win_delegate, + ax::mojom::Event::kValueChanged); break; case ui::AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED: - DispatchWinAccessibilityEvent(win_delegate, EVENT_OBJECT_STATECHANGE); + DispatchWinAccessibilityEvent(win_delegate, + ax::mojom::Event::kStateChanged); break; case ui::AXEventGenerator::Event::ACCESS_KEY_CHANGED: case ui::AXEventGenerator::Event::ACTIVE_DESCENDANT_CHANGED: @@ -90,7 +104,6 @@ void AccessibilityBridgeWindows::OnAccessibilityEvent( case ui::AXEventGenerator::Event::CONTROLS_CHANGED: case ui::AXEventGenerator::Event::DESCRIBED_BY_CHANGED: case ui::AXEventGenerator::Event::DESCRIPTION_CHANGED: - case ui::AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED: case ui::AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED: case ui::AXEventGenerator::Event::DROPEFFECT_CHANGED: case ui::AXEventGenerator::Event::ENABLED_CHANGED: @@ -151,7 +164,7 @@ AccessibilityBridgeWindows::CreateFlutterPlatformNodeDelegate() { void AccessibilityBridgeWindows::DispatchWinAccessibilityEvent( std::shared_ptr node_delegate, - DWORD event_type) { + ax::mojom::Event event_type) { node_delegate->DispatchWinAccessibilityEvent(event_type); } @@ -160,4 +173,18 @@ void AccessibilityBridgeWindows::SetFocus( node_delegate->SetFocus(); } +gfx::NativeViewAccessible +AccessibilityBridgeWindows::GetChildOfAXFragmentRoot() { + return view_->GetNativeViewAccessible(); +} + +gfx::NativeViewAccessible +AccessibilityBridgeWindows::GetParentOfAXFragmentRoot() { + return nullptr; +} + +bool AccessibilityBridgeWindows::IsAXFragmentRootAControlElement() { + return true; +} + } // namespace flutter diff --git a/shell/platform/windows/accessibility_bridge_windows.h b/shell/platform/windows/accessibility_bridge_windows.h index 68f75298a83e9..9f32b5ed666fa 100644 --- a/shell/platform/windows/accessibility_bridge_windows.h +++ b/shell/platform/windows/accessibility_bridge_windows.h @@ -6,13 +6,12 @@ #define FLUTTER_SHELL_PLATFORM_WINDOWS_ACCESSIBILITY_BRIDGE_WINDOWS_H_ #include "flutter/shell/platform/common/accessibility_bridge.h" - -#include "flutter/shell/platform/windows/flutter_windows_engine.h" -#include "flutter/shell/platform/windows/flutter_windows_view.h" +#include "flutter/third_party/accessibility/ax/platform/ax_fragment_root_delegate_win.h" namespace flutter { class FlutterWindowsEngine; +class FlutterWindowsView; class FlutterPlatformNodeDelegateWindows; // The Win32 implementation of AccessibilityBridge. @@ -24,7 +23,8 @@ class FlutterPlatformNodeDelegateWindows; /// /// AccessibilityBridgeWindows must be created as a shared_ptr, since some /// methods acquires its weak_ptr. -class AccessibilityBridgeWindows : public AccessibilityBridge { +class AccessibilityBridgeWindows : public AccessibilityBridge, + public ui::AXFragmentRootDelegateWin { public: AccessibilityBridgeWindows(FlutterWindowsEngine* engine, FlutterWindowsView* view); @@ -41,7 +41,7 @@ class AccessibilityBridgeWindows : public AccessibilityBridge { // This is a virtual method for the convenience of unit tests. virtual void DispatchWinAccessibilityEvent( std::shared_ptr node_delegate, - DWORD event_type); + ax::mojom::Event event_type); // Sets the accessibility focus to the accessibility node associated with the // specified semantics node. @@ -50,6 +50,15 @@ class AccessibilityBridgeWindows : public AccessibilityBridge { virtual void SetFocus( std::shared_ptr node_delegate); + // |AXFragmentRootDelegateWin| + gfx::NativeViewAccessible GetChildOfAXFragmentRoot() override; + + // |AXFragmentRootDelegateWin| + gfx::NativeViewAccessible GetParentOfAXFragmentRoot() override; + + // |AXFragmentRootDelegateWin| + bool IsAXFragmentRootAControlElement() override; + protected: // |AccessibilityBridge| void OnAccessibilityEvent( diff --git a/shell/platform/windows/accessibility_bridge_windows_unittests.cc b/shell/platform/windows/accessibility_bridge_windows_unittests.cc index 2dae9d8b41e01..e3cc3b58a0f98 100644 --- a/shell/platform/windows/accessibility_bridge_windows_unittests.cc +++ b/shell/platform/windows/accessibility_bridge_windows_unittests.cc @@ -29,7 +29,7 @@ namespace { // A structure representing a Win32 MSAA event targeting a specified node. struct MsaaEvent { std::shared_ptr node_delegate; - DWORD event_type; + ax::mojom::Event event_type; }; // Accessibility bridge delegate that captures events dispatched to the OS. @@ -43,7 +43,7 @@ class AccessibilityBridgeWindowsSpy : public AccessibilityBridgeWindows { void DispatchWinAccessibilityEvent( std::shared_ptr node_delegate, - DWORD event_type) override { + ax::mojom::Event event_type) override { dispatched_events_.push_back({node_delegate, event_type}); } @@ -76,7 +76,7 @@ class FlutterWindowsEngineSpy : public FlutterWindowsEngine { : FlutterWindowsEngine(project) {} protected: - virtual std::shared_ptr CreateAccessibilityBridge( + virtual std::shared_ptr CreateAccessibilityBridge( FlutterWindowsEngine* engine, FlutterWindowsView* view) override { return std::make_shared(engine, view); @@ -169,7 +169,7 @@ std::shared_ptr GetAccessibilityBridgeSpy( void ExpectWinEventFromAXEvent(int32_t node_id, ui::AXEventGenerator::Event ax_event, - DWORD expected_event) { + ax::mojom::Event expected_event) { auto window_binding_handler = std::make_unique<::testing::NiceMock>(); FlutterWindowsView view(std::move(window_binding_handler)); @@ -246,12 +246,12 @@ TEST(AccessibilityBridgeWindows, DispatchAccessibilityAction) { TEST(AccessibilityBridgeWindows, OnAccessibilityEventAlert) { ExpectWinEventFromAXEvent(0, ui::AXEventGenerator::Event::ALERT, - EVENT_SYSTEM_ALERT); + ax::mojom::Event::kAlert); } TEST(AccessibilityBridgeWindows, OnAccessibilityEventChildrenChanged) { ExpectWinEventFromAXEvent(0, ui::AXEventGenerator::Event::CHILDREN_CHANGED, - EVENT_OBJECT_REORDER); + ax::mojom::Event::kChildrenChanged); } TEST(AccessibilityBridgeWindows, OnAccessibilityEventFocusChanged) { @@ -270,7 +270,8 @@ TEST(AccessibilityBridgeWindows, OnAccessibilityEventFocusChanged) { ax::mojom::EventFrom::kNone, {}}}); ASSERT_EQ(bridge->dispatched_events().size(), 1); - EXPECT_EQ(bridge->dispatched_events()[0].event_type, EVENT_OBJECT_FOCUS); + EXPECT_EQ(bridge->dispatched_events()[0].event_type, + ax::mojom::Event::kFocus); ASSERT_EQ(bridge->focused_nodes().size(), 1); EXPECT_EQ(bridge->focused_nodes()[0], 1); @@ -279,62 +280,62 @@ TEST(AccessibilityBridgeWindows, OnAccessibilityEventFocusChanged) { TEST(AccessibilityBridgeWindows, OnAccessibilityEventIgnoredChanged) { // Static test nodes with no text, hint, or scrollability are ignored. ExpectWinEventFromAXEvent(4, ui::AXEventGenerator::Event::IGNORED_CHANGED, - EVENT_OBJECT_HIDE); + ax::mojom::Event::kHide); } TEST(AccessibilityBridgeWindows, OnAccessibilityImageAnnotationChanged) { ExpectWinEventFromAXEvent( 1, ui::AXEventGenerator::Event::IMAGE_ANNOTATION_CHANGED, - EVENT_OBJECT_NAMECHANGE); + ax::mojom::Event::kTextChanged); } TEST(AccessibilityBridgeWindows, OnAccessibilityLiveRegionChanged) { ExpectWinEventFromAXEvent(1, ui::AXEventGenerator::Event::LIVE_REGION_CHANGED, - EVENT_OBJECT_LIVEREGIONCHANGED); + ax::mojom::Event::kLiveRegionChanged); } TEST(AccessibilityBridgeWindows, OnAccessibilityNameChanged) { ExpectWinEventFromAXEvent(1, ui::AXEventGenerator::Event::NAME_CHANGED, - EVENT_OBJECT_NAMECHANGE); + ax::mojom::Event::kTextChanged); } TEST(AccessibilityBridgeWindows, OnAccessibilityHScrollPosChanged) { ExpectWinEventFromAXEvent( 1, ui::AXEventGenerator::Event::SCROLL_HORIZONTAL_POSITION_CHANGED, - EVENT_SYSTEM_SCROLLINGEND); + ax::mojom::Event::kScrollPositionChanged); } TEST(AccessibilityBridgeWindows, OnAccessibilityVScrollPosChanged) { ExpectWinEventFromAXEvent( 1, ui::AXEventGenerator::Event::SCROLL_VERTICAL_POSITION_CHANGED, - EVENT_SYSTEM_SCROLLINGEND); + ax::mojom::Event::kScrollPositionChanged); } TEST(AccessibilityBridgeWindows, OnAccessibilitySelectedChanged) { ExpectWinEventFromAXEvent(1, ui::AXEventGenerator::Event::SELECTED_CHANGED, - EVENT_OBJECT_VALUECHANGE); + ax::mojom::Event::kValueChanged); } TEST(AccessibilityBridgeWindows, OnAccessibilitySelectedChildrenChanged) { ExpectWinEventFromAXEvent( 2, ui::AXEventGenerator::Event::SELECTED_CHILDREN_CHANGED, - EVENT_OBJECT_SELECTIONWITHIN); + ax::mojom::Event::kSelectedChildrenChanged); } TEST(AccessibilityBridgeWindows, OnAccessibilitySubtreeCreated) { ExpectWinEventFromAXEvent(0, ui::AXEventGenerator::Event::SUBTREE_CREATED, - EVENT_OBJECT_SHOW); + ax::mojom::Event::kShow); } TEST(AccessibilityBridgeWindows, OnAccessibilityValueChanged) { ExpectWinEventFromAXEvent(1, ui::AXEventGenerator::Event::VALUE_CHANGED, - EVENT_OBJECT_VALUECHANGE); + ax::mojom::Event::kValueChanged); } TEST(AccessibilityBridgeWindows, OnAccessibilityStateChanged) { ExpectWinEventFromAXEvent( 1, ui::AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED, - EVENT_OBJECT_STATECHANGE); + ax::mojom::Event::kStateChanged); } } // namespace testing diff --git a/shell/platform/windows/flutter_platform_node_delegate_windows.cc b/shell/platform/windows/flutter_platform_node_delegate_windows.cc index 5f5ad62d4adcd..a7c972be10a62 100644 --- a/shell/platform/windows/flutter_platform_node_delegate_windows.cc +++ b/shell/platform/windows/flutter_platform_node_delegate_windows.cc @@ -10,6 +10,7 @@ #include "flutter/shell/platform/windows/flutter_windows_view.h" #include "flutter/third_party/accessibility/ax/ax_clipping_behavior.h" #include "flutter/third_party/accessibility/ax/ax_coordinate_system.h" +#include "flutter/third_party/accessibility/ax/platform/ax_fragment_root_win.h" namespace flutter { @@ -90,14 +91,8 @@ gfx::Rect FlutterPlatformNodeDelegateWindows::GetBoundsRect( } void FlutterPlatformNodeDelegateWindows::DispatchWinAccessibilityEvent( - DWORD event_type) { - HWND hwnd = view_->GetPlatformWindow(); - if (!hwnd) { - return; - } - assert(ax_platform_node_); - ::NotifyWinEvent(event_type, hwnd, OBJID_CLIENT, - -ax_platform_node_->GetUniqueId()); + ax::mojom::Event event_type) { + ax_platform_node_->NotifyAccessibilityEvent(event_type); } void FlutterPlatformNodeDelegateWindows::SetFocus() { @@ -107,4 +102,9 @@ void FlutterPlatformNodeDelegateWindows::SetFocus() { GetNativeViewAccessible()->accSelect(SELFLAG_TAKEFOCUS, varchild); } +gfx::AcceleratedWidget +FlutterPlatformNodeDelegateWindows::GetTargetForNativeAccessibilityEvent() { + return view_->GetPlatformWindow(); +} + } // namespace flutter diff --git a/shell/platform/windows/flutter_platform_node_delegate_windows.h b/shell/platform/windows/flutter_platform_node_delegate_windows.h index 8bac155b9c14d..364411cfffeab 100644 --- a/shell/platform/windows/flutter_platform_node_delegate_windows.h +++ b/shell/platform/windows/flutter_platform_node_delegate_windows.h @@ -42,12 +42,15 @@ class FlutterPlatformNodeDelegateWindows : public FlutterPlatformNodeDelegate { // Dispatches a Windows accessibility event of the specified type, generated // by the accessibility node associated with this object. This is a // convenience wrapper around |NotifyWinEvent|. - virtual void DispatchWinAccessibilityEvent(DWORD event_type); + virtual void DispatchWinAccessibilityEvent(ax::mojom::Event event_type); // Sets the accessibility focus to the accessibility node associated with // this object. void SetFocus(); + // | AXPlatformNodeDelegate | + gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override; + private: ui::AXPlatformNode* ax_platform_node_; std::weak_ptr bridge_; diff --git a/shell/platform/windows/flutter_window.cc b/shell/platform/windows/flutter_window.cc index 165fd52413a8d..18e7210bedda6 100644 --- a/shell/platform/windows/flutter_window.cc +++ b/shell/platform/windows/flutter_window.cc @@ -303,4 +303,8 @@ AccessibilityRootNode* FlutterWindow::GetAccessibilityRootNode() { return accessibility_root_; } +ui::AXFragmentRootDelegateWin* FlutterWindow::GetAxFragmentRootDelegate() { + return binding_handler_delegate_->GetAxFragmentRootDelegate(); +} + } // namespace flutter diff --git a/shell/platform/windows/flutter_window.h b/shell/platform/windows/flutter_window.h index b8f3183bcbd4f..e80d98b801d6d 100644 --- a/shell/platform/windows/flutter_window.h +++ b/shell/platform/windows/flutter_window.h @@ -5,8 +5,6 @@ #ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOW_H_ #define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOW_H_ -#include - #include #include #include @@ -154,6 +152,9 @@ class FlutterWindow : public Window, public WindowBindingHandler { // |WindowBindingHandler| AccessibilityRootNode* GetAccessibilityRootNode() override; + // |Window| + ui::AXFragmentRootDelegateWin* GetAxFragmentRootDelegate() override; + private: // A pointer to a FlutterWindowsView that can be used to update engine // windowing and input state. diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 795d83efea6fd..5118f1e39d5bb 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -618,7 +618,7 @@ void FlutterWindowsEngine::UpdateSemanticsEnabled(bool enabled) { } } -std::shared_ptr +std::shared_ptr FlutterWindowsEngine::CreateAccessibilityBridge(FlutterWindowsEngine* engine, FlutterWindowsView* view) { return std::make_shared(engine, view); diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index d7f08036c552c..193cc8f03f520 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -19,6 +19,7 @@ #include "flutter/shell/platform/common/client_wrapper/include/flutter/basic_message_channel.h" #include "flutter/shell/platform/common/incoming_message_dispatcher.h" #include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/windows/accessibility_bridge_windows.h" #include "flutter/shell/platform/windows/angle_surface_manager.h" #include "flutter/shell/platform/windows/flutter_desktop_messenger.h" #include "flutter/shell/platform/windows/flutter_project_bundle.h" @@ -142,7 +143,7 @@ class FlutterWindowsEngine { // rendering using software instead of OpenGL. AngleSurfaceManager* surface_manager() { return surface_manager_.get(); } - std::weak_ptr accessibility_bridge() { + std::weak_ptr accessibility_bridge() { return accessibility_bridge_; } @@ -251,7 +252,7 @@ class FlutterWindowsEngine { // // By default this method calls AccessibilityBridge's constructor. Exposing // this method allows unit tests to override in order to capture information. - virtual std::shared_ptr CreateAccessibilityBridge( + virtual std::shared_ptr CreateAccessibilityBridge( FlutterWindowsEngine* engine, FlutterWindowsView* view); @@ -333,7 +334,7 @@ class FlutterWindowsEngine { bool high_contrast_enabled_ = false; - std::shared_ptr accessibility_bridge_; + std::shared_ptr accessibility_bridge_; // The manager for WindowProc delegate registration and callbacks. std::unique_ptr window_proc_delegate_manager_; diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index a7b9a675d6b58..65ae0ef3cef40 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -675,4 +675,8 @@ void FlutterWindowsView::NotifyWinEventWrapper(DWORD event, } } +ui::AXFragmentRootDelegateWin* FlutterWindowsView::GetAxFragmentRootDelegate() { + return engine_->accessibility_bridge().lock().get(); +} + } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 1c7d721e2f5eb..2c0c719953f7c 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -5,8 +5,6 @@ #ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOWS_VIEW_H_ #define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WINDOWS_VIEW_H_ -#include - #include #include #include @@ -197,6 +195,9 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, // |TextInputPluginDelegate| void OnResetImeComposing() override; + // |WindowBindingHandlerDelegate| + virtual ui::AXFragmentRootDelegateWin* GetAxFragmentRootDelegate() override; + protected: // Called to create keyboard key handler. // diff --git a/shell/platform/windows/flutter_windows_view_unittests.cc b/shell/platform/windows/flutter_windows_view_unittests.cc index f2699206f0daf..0c1aa369fbe7d 100644 --- a/shell/platform/windows/flutter_windows_view_unittests.cc +++ b/shell/platform/windows/flutter_windows_view_unittests.cc @@ -4,6 +4,7 @@ #include "flutter/shell/platform/windows/flutter_windows_view.h" +#include #include #include #include @@ -231,6 +232,33 @@ TEST(FlutterWindowsView, AddSemanticsNodeUpdate) { varrole.vt = VT_I4; ASSERT_EQ(native_view->get_accRole(varchild, &varrole), S_OK); EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_STATICTEXT); + + // Get the IRawElementProviderFragment object. + IRawElementProviderSimple* uia_view; + native_view->QueryInterface(IID_PPV_ARGS(&uia_view)); + ASSERT_TRUE(uia_view != nullptr); + + // Verify name property matches our label. + VARIANT varname{}; + ASSERT_EQ(uia_view->GetPropertyValue(UIA_NamePropertyId, &varname), S_OK); + EXPECT_EQ(varname.vt, VT_BSTR); + name = _com_util::ConvertBSTRToString(varname.bstrVal); + EXPECT_EQ(name, "name"); + + // Verify value property matches our label. + VARIANT varvalue{}; + ASSERT_EQ(uia_view->GetPropertyValue(UIA_ValueValuePropertyId, &varvalue), + S_OK); + EXPECT_EQ(varvalue.vt, VT_BSTR); + value = _com_util::ConvertBSTRToString(varvalue.bstrVal); + EXPECT_EQ(value, "value"); + + // Verify node control type is text. + varrole = {}; + ASSERT_EQ(uia_view->GetPropertyValue(UIA_ControlTypePropertyId, &varrole), + S_OK); + EXPECT_EQ(varrole.vt, VT_I4); + EXPECT_EQ(varrole.lVal, UIA_TextControlTypeId); } // Verify the native IAccessible COM object tree is an accurate reflection of @@ -660,6 +688,17 @@ TEST(FlutterWindowsViewTest, CheckboxNativeState) { VARIANT native_state = {}; ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state))); EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_CHECKED); + + // Perform similar tests for UIA value; + IRawElementProviderSimple* uia_node; + native_view->QueryInterface(IID_PPV_ARGS(&uia_node)); + ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue( + UIA_ToggleToggleStatePropertyId, &native_state))); + EXPECT_EQ(native_state.lVal, ToggleState_On); + + ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue( + UIA_AriaPropertiesPropertyId, &native_state))); + EXPECT_NE(std::wcsstr(native_state.bstrVal, L"checked=true"), nullptr); } // Test unchecked too. @@ -690,6 +729,17 @@ TEST(FlutterWindowsViewTest, CheckboxNativeState) { VARIANT native_state = {}; ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state))); EXPECT_FALSE(native_state.lVal & STATE_SYSTEM_CHECKED); + + // Perform similar tests for UIA value; + IRawElementProviderSimple* uia_node; + native_view->QueryInterface(IID_PPV_ARGS(&uia_node)); + ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue( + UIA_ToggleToggleStatePropertyId, &native_state))); + EXPECT_EQ(native_state.lVal, ToggleState_Off); + + ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue( + UIA_AriaPropertiesPropertyId, &native_state))); + EXPECT_NE(std::wcsstr(native_state.bstrVal, L"checked=false"), nullptr); } // Now check mixed state. @@ -721,6 +771,17 @@ TEST(FlutterWindowsViewTest, CheckboxNativeState) { VARIANT native_state = {}; ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state))); EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_MIXED); + + // Perform similar tests for UIA value; + IRawElementProviderSimple* uia_node; + native_view->QueryInterface(IID_PPV_ARGS(&uia_node)); + ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue( + UIA_ToggleToggleStatePropertyId, &native_state))); + EXPECT_EQ(native_state.lVal, ToggleState_Indeterminate); + + ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue( + UIA_AriaPropertiesPropertyId, &native_state))); + EXPECT_NE(std::wcsstr(native_state.bstrVal, L"checked=mixed"), nullptr); } } @@ -788,6 +849,21 @@ TEST(FlutterWindowsViewTest, SwitchNativeState) { VARIANT native_state = {}; ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state))); EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_PRESSED); + + // Test similarly on UIA node. + IRawElementProviderSimple* uia_node; + native_view->QueryInterface(IID_PPV_ARGS(&uia_node)); + ASSERT_EQ(uia_node->GetPropertyValue(UIA_ControlTypePropertyId, &varrole), + S_OK); + EXPECT_EQ(varrole.lVal, UIA_ButtonControlTypeId); + ASSERT_EQ(uia_node->GetPropertyValue(UIA_ToggleToggleStatePropertyId, + &native_state), + S_OK); + EXPECT_EQ(native_state.lVal, ToggleState_On); + ASSERT_EQ( + uia_node->GetPropertyValue(UIA_AriaPropertiesPropertyId, &native_state), + S_OK); + EXPECT_NE(std::wcsstr(native_state.bstrVal, L"pressed=true"), nullptr); } // Test unpressed too. @@ -818,6 +894,18 @@ TEST(FlutterWindowsViewTest, SwitchNativeState) { VARIANT native_state = {}; ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state))); EXPECT_FALSE(native_state.lVal & STATE_SYSTEM_PRESSED); + + // Test similarly on UIA node. + IRawElementProviderSimple* uia_node; + native_view->QueryInterface(IID_PPV_ARGS(&uia_node)); + ASSERT_EQ(uia_node->GetPropertyValue(UIA_ToggleToggleStatePropertyId, + &native_state), + S_OK); + EXPECT_EQ(native_state.lVal, ToggleState_Off); + ASSERT_EQ( + uia_node->GetPropertyValue(UIA_AriaPropertiesPropertyId, &native_state), + S_OK); + EXPECT_NE(std::wcsstr(native_state.bstrVal, L"pressed=false"), nullptr); } } @@ -862,6 +950,25 @@ TEST(FlutterWindowsViewTest, TooltipNodeData) { std::string tooltip = root_node->GetData().GetStringAttribute( ax::mojom::StringAttribute::kTooltip); EXPECT_EQ(tooltip, "tooltip"); + + // Check that MSAA name contains the tooltip. + IAccessible* native_view = bridge + ->GetFlutterPlatformNodeDelegateFromID( + AccessibilityBridge::kRootNodeId) + .lock() + ->GetNativeViewAccessible(); + VARIANT varchild = {.vt = VT_I4, .lVal = CHILDID_SELF}; + BSTR bname; + ASSERT_EQ(native_view->get_accName(varchild, &bname), S_OK); + EXPECT_NE(std::wcsstr(bname, L"tooltip"), nullptr); + + // Check that UIA help text is equal to the tooltip. + IRawElementProviderSimple* uia_node; + native_view->QueryInterface(IID_PPV_ARGS(&uia_node)); + VARIANT varname{}; + ASSERT_EQ(uia_node->GetPropertyValue(UIA_HelpTextPropertyId, &varname), S_OK); + std::string uia_tooltip = _com_util::ConvertBSTRToString(varname.bstrVal); + EXPECT_EQ(uia_tooltip, "tooltip"); } } // namespace testing diff --git a/shell/platform/windows/testing/mock_text_input_manager.h b/shell/platform/windows/testing/mock_text_input_manager.h index 7fefa1ae25170..e51d218273207 100644 --- a/shell/platform/windows/testing/mock_text_input_manager.h +++ b/shell/platform/windows/testing/mock_text_input_manager.h @@ -5,7 +5,6 @@ #ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_MOCK_TEXT_INPUT_MANAGER_WIN32_H_ #define FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_MOCK_TEXT_INPUT_MANAGER_WIN32_H_ -#include #include #include diff --git a/shell/platform/windows/testing/mock_window.h b/shell/platform/windows/testing/mock_window.h index fc50235745e3b..78fabc663c1cf 100644 --- a/shell/platform/windows/testing/mock_window.h +++ b/shell/platform/windows/testing/mock_window.h @@ -5,8 +5,6 @@ #ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_MOCK_WIN32_WINDOW_H_ #define FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_MOCK_WIN32_WINDOW_H_ -#include - #include "flutter/shell/platform/windows/testing/test_keyboard.h" #include "flutter/shell/platform/windows/window.h" #include "gmock/gmock.h" @@ -67,6 +65,8 @@ class MockWindow : public Window { MOCK_METHOD0(OnThemeChange, void()); + MOCK_METHOD0(GetAxFragmentRootDelegate, ui::AXFragmentRootDelegateWin*()); + void CallOnImeComposition(UINT const message, WPARAM const wparam, LPARAM const lparam); diff --git a/shell/platform/windows/testing/mock_window_binding_handler.h b/shell/platform/windows/testing/mock_window_binding_handler.h index dcbbc9e15f2a6..2fc5cbd77e905 100644 --- a/shell/platform/windows/testing/mock_window_binding_handler.h +++ b/shell/platform/windows/testing/mock_window_binding_handler.h @@ -5,8 +5,6 @@ #ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_MOCK_WINDOW_BINDING_HANDLER_H_ #define FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_MOCK_WINDOW_BINDING_HANDLER_H_ -#include - #include "flutter/shell/platform/windows/window_binding_handler.h" #include "gmock/gmock.h" diff --git a/shell/platform/windows/testing/mock_window_binding_handler_delegate.h b/shell/platform/windows/testing/mock_window_binding_handler_delegate.h index 03534b2e5ecfd..2e7df003cc847 100644 --- a/shell/platform/windows/testing/mock_window_binding_handler_delegate.h +++ b/shell/platform/windows/testing/mock_window_binding_handler_delegate.h @@ -63,6 +63,8 @@ class MockWindowBindingHandlerDelegate : public WindowBindingHandlerDelegate { MOCK_METHOD1(OnScrollInertiaCancel, void(int32_t)); MOCK_METHOD0(OnPlatformBrightnessChanged, void()); MOCK_METHOD1(UpdateHighContrastEnabled, void(bool enabled)); + + MOCK_METHOD0(GetAxFragmentRootDelegate, ui::AXFragmentRootDelegateWin*()); }; } // namespace testing diff --git a/shell/platform/windows/testing/test_keyboard.cc b/shell/platform/windows/testing/test_keyboard.cc index 06a5b86c38409..d752f027c80c0 100644 --- a/shell/platform/windows/testing/test_keyboard.cc +++ b/shell/platform/windows/testing/test_keyboard.cc @@ -7,7 +7,6 @@ #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" #include -#include namespace flutter { namespace testing { diff --git a/shell/platform/windows/testing/wm_builders.h b/shell/platform/windows/testing/wm_builders.h index 6f93b13d1c61a..49b78035b7e01 100644 --- a/shell/platform/windows/testing/wm_builders.h +++ b/shell/platform/windows/testing/wm_builders.h @@ -7,7 +7,6 @@ #include #include -#include namespace flutter { namespace testing { diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index a6fe97b0d5383..8f2a3327a97db 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -59,7 +59,8 @@ Window::Window(std::unique_ptr windows_proc_table, : touch_id_generator_(kMinTouchDeviceId, kMaxTouchDeviceId), windows_proc_table_(std::move(windows_proc_table)), text_input_manager_(std::move(text_input_manager)), - accessibility_root_(nullptr) { + accessibility_root_(nullptr), + ax_fragment_root_(nullptr) { // Get the DPI of the primary monitor as the initial DPI. If Per-Monitor V2 is // supported, |current_dpi_| should be updated in the // kWmDpiChangedBeforeParent message. @@ -200,9 +201,27 @@ LRESULT Window::OnGetObject(UINT const message, } gfx::NativeViewAccessible root_view = GetNativeViewAccessible(); + // TODO(schectman): UIA is currently disabled by default. + // https://github.com/flutter/flutter/issues/114547 if (is_uia_request && root_view) { - // TODO(cbracken): https://github.com/flutter/flutter/issues/94782 - // Implement when we adopt UIA support. +#ifdef FLUTTER_ENGINE_USE_UIA + if (!ax_fragment_root_) { + ax_fragment_root_ = std::make_unique( + window_handle_, GetAxFragmentRootDelegate()); + } + + // Retrieve UIA object for the root view. + Microsoft::WRL::ComPtr root; + if (SUCCEEDED(ax_fragment_root_->GetNativeViewAccessible()->QueryInterface( + IID_PPV_ARGS(&root)))) { + // Return the UIA object via UiaReturnRawElementProvider(). See: + // https://docs.microsoft.com/en-us/windows/win32/winauto/wm-getobject + reference_result = UiaReturnRawElementProvider(window_handle_, wparam, + lparam, root.Get()); + } else { + FML_LOG(ERROR) << "Failed to query AX fragment root."; + } +#endif // FLUTTER_ENGINE_USE_UIA } else if (is_msaa_request && root_view) { // Create the accessibility root if it does not already exist. if (!accessibility_root_) { @@ -212,10 +231,9 @@ LRESULT Window::OnGetObject(UINT const message, // Microsoft::WRL::ComPtr root(root_view); accessibility_root_->SetWindow(root_view); Microsoft::WRL::ComPtr root(accessibility_root_); - LRESULT lresult = LresultFromObject(IID_IAccessible, wparam, root.Get()); - return lresult; + reference_result = LresultFromObject(IID_IAccessible, wparam, root.Get()); } - return 0; + return reference_result; } void Window::OnImeSetContext(UINT const message, diff --git a/shell/platform/windows/window.h b/shell/platform/windows/window.h index ee9af55950b59..6c234ce68fc58 100644 --- a/shell/platform/windows/window.h +++ b/shell/platform/windows/window.h @@ -6,7 +6,6 @@ #define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_WIN32_WINDOW_H_ #include -#include #include #include @@ -20,6 +19,9 @@ #include "flutter/shell/platform/windows/sequential_id_generator.h" #include "flutter/shell/platform/windows/text_input_manager.h" #include "flutter/shell/platform/windows/windows_proc_table.h" +#include "flutter/shell/platform/windows/windowsx_shim.h" +#include "flutter/third_party/accessibility/ax/platform/ax_fragment_root_delegate_win.h" +#include "flutter/third_party/accessibility/ax/platform/ax_fragment_root_win.h" #include "flutter/third_party/accessibility/gfx/native_widget_types.h" namespace flutter { @@ -211,6 +213,9 @@ class Window : public KeyboardManager::WindowDelegate { // Check if the high contrast feature is enabled on the OS virtual bool GetHighContrastEnabled(); + // Called to obtain a pointer to the fragment root delegate. + virtual ui::AXFragmentRootDelegateWin* GetAxFragmentRootDelegate() = 0; + protected: // Win32's DefWindowProc. // @@ -299,6 +304,12 @@ class Window : public KeyboardManager::WindowDelegate { // Timer identifier for DirectManipulation gesture polling. const static int kDirectManipulationTimer = 1; + + // Implements IRawElementProviderFragmentRoot when UIA is enabled. + std::unique_ptr ax_fragment_root_; + + // Allow WindowAXFragmentRootDelegate to access protected method. + friend class WindowAXFragmentRootDelegate; }; } // namespace flutter diff --git a/shell/platform/windows/window_binding_handler_delegate.h b/shell/platform/windows/window_binding_handler_delegate.h index 485fe4384f3cc..9d0e0253c447a 100644 --- a/shell/platform/windows/window_binding_handler_delegate.h +++ b/shell/platform/windows/window_binding_handler_delegate.h @@ -9,6 +9,7 @@ #include "flutter/shell/platform/common/geometry.h" #include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/third_party/accessibility/ax/platform/ax_fragment_root_delegate_win.h" #include "flutter/third_party/accessibility/gfx/native_widget_types.h" namespace flutter { @@ -135,6 +136,13 @@ class WindowBindingHandlerDelegate { // Update the status of the high contrast feature virtual void UpdateHighContrastEnabled(bool enabled) = 0; + + // Obtain a pointer to the fragment root delegate. + // This is required by UIA in order to obtain the fragment root that + // contains a fragment obtained by, for example, a hit test. Unlike + // MSAA, UIA elements do not explicitly store or enumerate their + // children and parents, so a method such as this is required. + virtual ui::AXFragmentRootDelegateWin* GetAxFragmentRootDelegate() = 0; }; } // namespace flutter diff --git a/shell/platform/windows/windowsx_shim.h b/shell/platform/windows/windowsx_shim.h new file mode 100644 index 0000000000000..2e3a88fc0522b --- /dev/null +++ b/shell/platform/windows/windowsx_shim.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_WIN_WINDOWSX_SHIM_H_ +#define BASE_WIN_WINDOWSX_SHIM_H_ + +// The Win32 platform header contains some macros for +// common function names. To work around that, windowsx.h is not to be +// included directly, and instead this file should be included. If one +// of the removed Win32 macros is wanted, use the expanded form +// manually instead. + +#ifdef _INC_WINDOWS_X +#error "There is an include of windowsx.h in the code. Use windowsx_shim.h" +#endif // _INC_WINDOWS_X + +#include + +#undef GetNextSibling // Same as GetWindow(hwnd, GW_HWNDNEXT) +#undef GetFirstChild // Same as GetTopWindow(hwnd) +#undef IsMaximized // Defined to IsZoomed, use IsZoomed directly instead +#undef IsMinimized // Defined to IsIconic, use IsIconic directly instead +#undef IsRestored // Macro to check that neither WS_MINIMIZE, nor + // WS_MAXIMIZE is set in the GetWindowStyle return + // value. + +#endif // BASE_WIN_WINDOWSX_SHIM_H_ diff --git a/third_party/accessibility/ax/ax_active_popup.cc b/third_party/accessibility/ax/ax_active_popup.cc index 904de75c4bbb0..d41510bceb157 100644 --- a/third_party/accessibility/ax/ax_active_popup.cc +++ b/third_party/accessibility/ax/ax_active_popup.cc @@ -15,7 +15,7 @@ namespace ui { static std::optional g_active_popup_ax_unique_id; std::optional GetActivePopupAxUniqueId() { - return *g_active_popup_ax_unique_id; + return g_active_popup_ax_unique_id; } void SetActivePopupAxUniqueId(std::optional ax_unique_id) { diff --git a/third_party/accessibility/ax/platform/ax_platform_node_win.cc b/third_party/accessibility/ax/platform/ax_platform_node_win.cc index dad8d2739806a..9de6867ad9eed 100644 --- a/third_party/accessibility/ax/platform/ax_platform_node_win.cc +++ b/third_party/accessibility/ax/platform/ax_platform_node_win.cc @@ -2465,6 +2465,19 @@ HRESULT AXPlatformNodeWin::GetPropertyValueImpl(PROPERTYID property_id, result->intVal = static_cast(ComputeExpandCollapseState()); break; + case UIA_ToggleToggleStatePropertyId: { + ToggleState state; + get_ToggleState(&state); + result->vt = VT_I4; + result->lVal = state; + break; + } + + case UIA_ValueValuePropertyId: + result->vt = VT_BSTR; + result->bstrVal = GetValueAttributeAsBstr(this); + break; + // Not currently implemented. case UIA_AnnotationObjectsPropertyId: case UIA_AnnotationTypesPropertyId: @@ -5205,6 +5218,8 @@ std::optional AXPlatformNodeWin::MojoEventToMSAAEvent( return EVENT_OBJECT_SHOW; case ax::mojom::Event::kValueChanged: return EVENT_OBJECT_VALUECHANGE; + case ax::mojom::Event::kDocumentSelectionChanged: + return EVENT_OBJECT_TEXTSELECTIONCHANGED; default: return std::nullopt; } @@ -5216,6 +5231,8 @@ std::optional AXPlatformNodeWin::MojoEventToUIAEvent( switch (event) { case ax::mojom::Event::kAlert: return UIA_SystemAlertEventId; + case ax::mojom::Event::kDocumentSelectionChanged: + return UIA_Text_TextChangedEventId; case ax::mojom::Event::kFocus: case ax::mojom::Event::kFocusContext: case ax::mojom::Event::kFocusAfterMenuClose: @@ -5582,6 +5599,11 @@ AXPlatformNodeWin::GetPatternProviderFactoryMethod(PATTERNID pattern_id) { } break; + // TODO(schectman): add implementations for ITextProvider and + // ITextRangeProvider interfaces. + // https://github.com/flutter/flutter/issues/114547 and + // https://github.com/flutter/flutter/issues/109804 + case UIA_TogglePatternId: if (SupportsToggle(data.role)) { return &PatternProvider; diff --git a/third_party/accessibility/ax/platform/ax_platform_node_win_unittest.cc b/third_party/accessibility/ax/platform/ax_platform_node_win_unittest.cc index 90d5e40156d0f..bfa064a425f1f 100644 --- a/third_party/accessibility/ax/platform/ax_platform_node_win_unittest.cc +++ b/third_party/accessibility/ax/platform/ax_platform_node_win_unittest.cc @@ -1760,7 +1760,7 @@ TEST_F(AXPlatformNodeWinTest, ITableItemProviderGetRowHeaderItems) { EXPECT_EQ(nullptr, safearray.Get()); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetPropertySimple) { +TEST_F(AXPlatformNodeWinTest, UIAGetPropertySimple) { AXNodeData root; root.role = ax::mojom::Role::kList; root.SetName("fake name"); @@ -1820,7 +1820,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetPropertySimple) { EXPECT_UIA_INT_EQ(child_node1, UIA_PositionInSetPropertyId, 1); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetPropertyValueClickablePoint) { +TEST_F(AXPlatformNodeWinTest, UIAGetPropertyValueClickablePoint) { AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kButton; @@ -1837,7 +1837,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetPropertyValueClickablePoint) { UIA_ClickablePointPropertyId, expected_values); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetPropertyValueIsDialog) { +TEST_F(AXPlatformNodeWinTest, UIAGetPropertyValueIsDialog) { AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kRootWebArea; @@ -1948,7 +1948,7 @@ TEST_F(AXPlatformNodeWinTest, UIA_IsControlElementPropertyId, false); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetControllerForPropertyId) { +TEST_F(AXPlatformNodeWinTest, UIAGetControllerForPropertyId) { AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kRootWebArea; @@ -1996,7 +1996,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetControllerForPropertyId) { expected_names_2); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetDescribedByPropertyId) { +TEST_F(AXPlatformNodeWinTest, UIAGetDescribedByPropertyId) { AXNodeData root; std::vector describedby_ids = {2, 3, 4}; root.AddIntListAttribute(ax::mojom::IntListAttribute::kDescribedbyIds, @@ -2029,7 +2029,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetDescribedByPropertyId) { root_node, UIA_DescribedByPropertyId, UIA_NamePropertyId, expected_names); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAItemStatusPropertyId) { +TEST_F(AXPlatformNodeWinTest, UIAItemStatusPropertyId) { AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kTable; @@ -2097,7 +2097,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAItemStatusPropertyId) { UIA_ItemStatusPropertyId, ScopedVariant::kEmptyVariant); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetFlowsToPropertyId) { +TEST_F(AXPlatformNodeWinTest, UIAGetFlowsToPropertyId) { AXNodeData root; std::vector flowto_ids = {2, 3, 4}; root.AddIntListAttribute(ax::mojom::IntListAttribute::kFlowtoIds, flowto_ids); @@ -2128,7 +2128,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetFlowsToPropertyId) { UIA_NamePropertyId, expected_names); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetPropertyValueFlowsFromNone) { +TEST_F(AXPlatformNodeWinTest, UIAGetPropertyValueFlowsFromNone) { AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kRootWebArea; @@ -2146,7 +2146,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetPropertyValueFlowsFromNone) { EXPECT_EQ(nullptr, V_ARRAY(property_value.ptr())); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetPropertyValueFlowsFromSingle) { +TEST_F(AXPlatformNodeWinTest, UIAGetPropertyValueFlowsFromSingle) { AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kRootWebArea; @@ -2171,7 +2171,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetPropertyValueFlowsFromSingle) { child_node1, UIA_FlowsFromPropertyId, UIA_NamePropertyId, expected_names); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetPropertyValueFlowsFromMultiple) { +TEST_F(AXPlatformNodeWinTest, UIAGetPropertyValueFlowsFromMultiple) { AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kRootWebArea; @@ -2215,7 +2215,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetPropertyValueFlowsFromMultiple) { expected_names_2); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetPropertyValueFrameworkId) { +TEST_F(AXPlatformNodeWinTest, UIAGetPropertyValueFrameworkId) { AXNodeData root_ax_node_data; root_ax_node_data.id = 1; root_ax_node_data.role = ax::mojom::Role::kRootWebArea; @@ -2554,7 +2554,7 @@ TEST_F(AXPlatformNodeWinTest, GetPropertyValue_IsControlElement) { UIA_IsControlElementPropertyId, true); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetProviderOptions) { +TEST_F(AXPlatformNodeWinTest, UIAGetProviderOptions) { AXNodeData root_data; root_data.id = 1; Init(root_data); @@ -2571,7 +2571,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetProviderOptions) { provider_options); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetHostRawElementProvider) { +TEST_F(AXPlatformNodeWinTest, UIAGetHostRawElementProvider) { AXNodeData root_data; root_data.id = 1; Init(root_data); @@ -2585,7 +2585,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetHostRawElementProvider) { EXPECT_EQ(nullptr, host_provider.Get()); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetBoundingRectangle) { +TEST_F(AXPlatformNodeWinTest, UIAGetBoundingRectangle) { AXNodeData root_data; root_data.id = 1; root_data.relative_bounds.bounds = gfx::RectF(10, 20, 30, 50); @@ -2603,7 +2603,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetBoundingRectangle) { EXPECT_EQ(50, bounding_rectangle.height); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetFragmentRoot) { +TEST_F(AXPlatformNodeWinTest, UIAGetFragmentRoot) { // This test needs to be run on a child node since AXPlatformRootNodeWin // overrides the method. AXNodeData root_data; @@ -2643,7 +2643,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetFragmentRoot) { element1_provider->get_FragmentRoot(&actual_fragment_root)); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetEmbeddedFragmentRoots) { +TEST_F(AXPlatformNodeWinTest, UIAGetEmbeddedFragmentRoots) { AXNodeData root_data; root_data.id = 1; Init(root_data); @@ -2657,7 +2657,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetEmbeddedFragmentRoots) { EXPECT_EQ(nullptr, embedded_fragment_roots.Get()); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetRuntimeId) { +TEST_F(AXPlatformNodeWinTest, UIAGetRuntimeId) { AXNodeData root_data; root_data.id = 1; Init(root_data); @@ -2687,7 +2687,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAGetRuntimeId) { EXPECT_HRESULT_SUCCEEDED(::SafeArrayUnaccessData(runtime_id.Get())); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAIWindowProviderGetIsModalUnset) { +TEST_F(AXPlatformNodeWinTest, UIAIWindowProviderGetIsModalUnset) { AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kRootWebArea; @@ -2701,7 +2701,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAIWindowProviderGetIsModalUnset) { ASSERT_EQ(nullptr, window_provider.Get()); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAIWindowProviderGetIsModalFalse) { +TEST_F(AXPlatformNodeWinTest, UIAIWindowProviderGetIsModalFalse) { AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kRootWebArea; @@ -2720,7 +2720,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAIWindowProviderGetIsModalFalse) { ASSERT_FALSE(is_modal); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAIWindowProviderGetIsModalTrue) { +TEST_F(AXPlatformNodeWinTest, UIAIWindowProviderGetIsModalTrue) { AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kRootWebArea; @@ -2739,7 +2739,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAIWindowProviderGetIsModalTrue) { ASSERT_TRUE(is_modal); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAIWindowProviderInvalidArgument) { +TEST_F(AXPlatformNodeWinTest, UIAIWindowProviderInvalidArgument) { AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kRootWebArea; @@ -2762,7 +2762,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAIWindowProviderInvalidArgument) { ASSERT_EQ(E_INVALIDARG, window_provider->get_IsTopmost(nullptr)); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAIWindowProviderNotSupported) { +TEST_F(AXPlatformNodeWinTest, UIAIWindowProviderNotSupported) { AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kRootWebArea; @@ -2800,7 +2800,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIAIWindowProviderNotSupported) { window_provider->get_IsTopmost(&bool_result)); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIANavigate) { +TEST_F(AXPlatformNodeWinTest, UIANavigate) { AXNodeData root_data; root_data.id = 1; @@ -3076,7 +3076,7 @@ TEST_F(AXPlatformNodeWinTest, ComputeUIAControlType) { UIA_ControlTypePropertyId, int{UIA_EditControlTypeId}); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIALandmarkType) { +TEST_F(AXPlatformNodeWinTest, UIALandmarkType) { auto TestLandmarkType = [this](ax::mojom::Role node_role, std::optional expected_landmark_type, const std::string& node_name = {}) { @@ -3120,7 +3120,7 @@ TEST_F(AXPlatformNodeWinTest, DISABLED_UIALandmarkType) { TestLandmarkType(ax::mojom::Role::kTable, {}); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIALocalizedLandmarkType) { +TEST_F(AXPlatformNodeWinTest, UIALocalizedLandmarkType) { auto TestLocalizedLandmarkType = [this](ax::mojom::Role node_role, const std::wstring& expected_localized_landmark, @@ -3196,7 +3196,7 @@ TEST_F(AXPlatformNodeWinTest, IRawElementProviderSimple2ShowContextMenu) { EXPECT_EQ(root_node, TestAXNodeWrapper::GetNodeFromLastShowContextMenu()); } -TEST_F(AXPlatformNodeWinTest, DISABLED_UIAErrorHandling) { +TEST_F(AXPlatformNodeWinTest, UIAErrorHandling) { AXNodeData root; root.id = 1; Init(root); diff --git a/third_party/accessibility/ax/platform/test_ax_node_wrapper.cc b/third_party/accessibility/ax/platform/test_ax_node_wrapper.cc index 7270b83911da4..05a655194948e 100644 --- a/third_party/accessibility/ax/platform/test_ax_node_wrapper.cc +++ b/third_party/accessibility/ax/platform/test_ax_node_wrapper.cc @@ -54,9 +54,14 @@ std::map g_hit_test_result; class TestAXTreeObserver : public AXTreeObserver { private: void OnNodeDeleted(AXTree* tree, int32_t node_id) override { - const auto iter = g_node_id_to_wrapper_map.find(node_id); + const auto& iter = g_node_id_to_wrapper_map.find(node_id); if (iter != g_node_id_to_wrapper_map.end()) { TestAXNodeWrapper* wrapper = iter->second; + const auto& focus_iter = g_focused_node_in_tree.find(tree); + if (focus_iter != g_focused_node_in_tree.end() && + focus_iter->second->id() == node_id) { + g_focused_node_in_tree.erase(tree); + } delete wrapper; g_node_id_to_wrapper_map.erase(node_id); }