diff --git a/shell/platform/windows/accessibility_bridge_delegate_win32.cc b/shell/platform/windows/accessibility_bridge_delegate_win32.cc index 048d335ba4009..5e8be78da4396 100644 --- a/shell/platform/windows/accessibility_bridge_delegate_win32.cc +++ b/shell/platform/windows/accessibility_bridge_delegate_win32.cc @@ -39,6 +39,7 @@ void AccessibilityBridgeDelegateWin32::OnAccessibilityEvent( break; case ui::AXEventGenerator::Event::FOCUS_CHANGED: DispatchWinAccessibilityEvent(win_delegate, EVENT_OBJECT_FOCUS); + SetFocus(win_delegate); break; case ui::AXEventGenerator::Event::IGNORED_CHANGED: if (ax_node->IsIgnored()) { @@ -152,4 +153,9 @@ void AccessibilityBridgeDelegateWin32::DispatchWinAccessibilityEvent( node_delegate->DispatchWinAccessibilityEvent(event_type); } +void AccessibilityBridgeDelegateWin32::SetFocus( + std::shared_ptr node_delegate) { + node_delegate->SetFocus(); +} + } // namespace flutter diff --git a/shell/platform/windows/accessibility_bridge_delegate_win32.h b/shell/platform/windows/accessibility_bridge_delegate_win32.h index b5393ddf4498b..c5d4ceebe7717 100644 --- a/shell/platform/windows/accessibility_bridge_delegate_win32.h +++ b/shell/platform/windows/accessibility_bridge_delegate_win32.h @@ -46,6 +46,11 @@ class AccessibilityBridgeDelegateWin32 std::shared_ptr node_delegate, DWORD event_type); + // Sets the accessibility focus to the accessibility node associated with the + // specified semantics node. + virtual void SetFocus( + std::shared_ptr node_delegate); + private: FlutterWindowsEngine* engine_; }; diff --git a/shell/platform/windows/accessibility_bridge_delegate_win32_unittests.cc b/shell/platform/windows/accessibility_bridge_delegate_win32_unittests.cc index 26bae838d79bf..67baa9a74a059 100644 --- a/shell/platform/windows/accessibility_bridge_delegate_win32_unittests.cc +++ b/shell/platform/windows/accessibility_bridge_delegate_win32_unittests.cc @@ -45,13 +45,25 @@ class AccessibilityBridgeDelegateWin32Spy dispatched_events_.push_back({node_delegate, event_type}); } - void Reset() { dispatched_events_.clear(); } + void SetFocus(std::shared_ptr node_delegate) + override { + focused_nodes_.push_back(node_delegate->GetAXNode()->id()); + } + + void Reset() { + dispatched_events_.clear(); + focused_nodes_.clear(); + } + const std::vector& dispatched_events() const { return dispatched_events_; - }; + } + + const std::vector focused_nodes() const { return focused_nodes_; } private: std::vector dispatched_events_; + std::vector focused_nodes_; }; // Returns an engine instance configured with dummy project path values, and @@ -203,8 +215,25 @@ TEST(AccessibilityBridgeDelegateWin32, OnAccessibilityEventChildrenChanged) { } TEST(AccessibilityBridgeDelegateWin32, OnAccessibilityEventFocusChanged) { - ExpectWinEventFromAXEvent(1, ui::AXEventGenerator::Event::FOCUS_CHANGED, - EVENT_OBJECT_FOCUS); + auto window_binding_handler = + std::make_unique<::testing::NiceMock>(); + FlutterWindowsView view(std::move(window_binding_handler)); + view.SetEngine(GetTestEngine()); + view.OnUpdateSemanticsEnabled(true); + + auto bridge = view.GetEngine()->accessibility_bridge().lock(); + PopulateAXTree(bridge); + + AccessibilityBridgeDelegateWin32Spy spy(view.GetEngine()); + spy.OnAccessibilityEvent({AXNodeFromID(bridge, 1), + {ui::AXEventGenerator::Event::FOCUS_CHANGED, + ax::mojom::EventFrom::kNone, + {}}}); + ASSERT_EQ(spy.dispatched_events().size(), 1); + EXPECT_EQ(spy.dispatched_events()[0].event_type, EVENT_OBJECT_FOCUS); + + ASSERT_EQ(spy.focused_nodes().size(), 1); + EXPECT_EQ(spy.focused_nodes()[0], 1); } TEST(AccessibilityBridgeDelegateWin32, OnAccessibilityEventIgnoredChanged) { diff --git a/shell/platform/windows/flutter_platform_node_delegate_win32.cc b/shell/platform/windows/flutter_platform_node_delegate_win32.cc index 81a44430e5f44..75d904fb0ae62 100644 --- a/shell/platform/windows/flutter_platform_node_delegate_win32.cc +++ b/shell/platform/windows/flutter_platform_node_delegate_win32.cc @@ -92,4 +92,11 @@ void FlutterPlatformNodeDelegateWin32::DispatchWinAccessibilityEvent( -ax_platform_node_->GetUniqueId()); } +void FlutterPlatformNodeDelegateWin32::SetFocus() { + VARIANT varchild{}; + varchild.vt = VT_I4; + varchild.lVal = CHILDID_SELF; + GetNativeViewAccessible()->accSelect(SELFLAG_TAKEFOCUS, varchild); +} + } // namespace flutter diff --git a/shell/platform/windows/flutter_platform_node_delegate_win32.h b/shell/platform/windows/flutter_platform_node_delegate_win32.h index 65358a99f05a1..f42c2baa4c87d 100644 --- a/shell/platform/windows/flutter_platform_node_delegate_win32.h +++ b/shell/platform/windows/flutter_platform_node_delegate_win32.h @@ -45,6 +45,10 @@ class FlutterPlatformNodeDelegateWin32 : public FlutterPlatformNodeDelegate { // convenience wrapper around |NotifyWinEvent|. virtual void DispatchWinAccessibilityEvent(DWORD event_type); + // Sets the accessibility focus to the accessibility node associated with + // this object. + void SetFocus(); + private: ui::AXPlatformNode* ax_platform_node_; FlutterWindowsEngine* engine_;