diff --git a/shell/platform/common/accessibility_bridge.cc b/shell/platform/common/accessibility_bridge.cc index d0f19089dd86f..b382a8d50f4f0 100644 --- a/shell/platform/common/accessibility_bridge.cc +++ b/shell/platform/common/accessibility_bridge.cc @@ -19,13 +19,17 @@ constexpr int kHasScrollingAction = FlutterSemanticsAction::kFlutterSemanticsActionScrollDown; // AccessibilityBridge -AccessibilityBridge::AccessibilityBridge( - std::unique_ptr delegate) - : delegate_(std::move(delegate)) { +AccessibilityBridge::AccessibilityBridge() { event_generator_.SetTree(&tree_); tree_.AddObserver(static_cast(this)); } +AccessibilityBridge::AccessibilityBridge( + std::unique_ptr delegate) + : AccessibilityBridge() { + UpdateDelegate(std::move(delegate)); +} + AccessibilityBridge::~AccessibilityBridge() { event_generator_.ReleaseTree(); tree_.RemoveObserver(static_cast(this)); diff --git a/shell/platform/common/accessibility_bridge.h b/shell/platform/common/accessibility_bridge.h index 70fcd20c3d2f7..b5e7d08777495 100644 --- a/shell/platform/common/accessibility_bridge.h +++ b/shell/platform/common/accessibility_bridge.h @@ -20,9 +20,9 @@ namespace flutter { //------------------------------------------------------------------------------ -/// Use this class to maintain an accessibility tree. This class consumes -/// semantics updates from the embedder API and produces an accessibility tree -/// in the native format. +/// Use this class to maintain the accessibility tree for a view. This class +/// consumes semantics updates from the embedder API and produces an +/// accessibility tree in the native format. /// /// The bridge creates an AXTree to hold the semantics data that comes from /// Flutter semantics updates. The tree holds AXNode[s] which contain the @@ -63,6 +63,7 @@ class AccessibilityBridge class AccessibilityBridgeDelegate { public: virtual ~AccessibilityBridgeDelegate() = default; + //--------------------------------------------------------------------------- /// @brief Handle accessibility events generated due to accessibility /// tree changes. These events are generated in accessibility @@ -101,12 +102,19 @@ class AccessibilityBridge CreateFlutterPlatformNodeDelegate() = 0; }; + //----------------------------------------------------------------------------- + /// @brief Creates a new instance of a accessibility bridge without a + /// delegate. + /// + /// The bridge is not ready for use until a delegate has been + /// assigned. See AccessibilityBridge::UpdateDelegate. + AccessibilityBridge(); + //----------------------------------------------------------------------------- /// @brief Creates a new instance of a accessibility bridge. /// - /// @param[in] user_data A custom pointer to the data of your - /// choice. This pointer can be retrieve later - /// through GetUserData(). + /// This is effectively identical to the default constructor + /// followed by AccessibilityBridge::UpdateDelegate. explicit AccessibilityBridge( std::unique_ptr delegate); ~AccessibilityBridge(); diff --git a/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.h b/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.h index 772234b5b606c..7bd7dff056ac2 100644 --- a/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.h +++ b/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.h @@ -24,7 +24,8 @@ class AccessibilityBridgeMacDelegate : public AccessibilityBridge::Accessibility /// @param[in] flutter_engine The weak reference to the FlutterEngine. /// @param[in] view_controller The weak reference to the FlutterViewController. explicit AccessibilityBridgeMacDelegate(__weak FlutterEngine* flutter_engine, - __weak FlutterViewController* view_controller); + __weak FlutterViewController* view_controller, + std::weak_ptr bridge); virtual ~AccessibilityBridgeMacDelegate() = default; // |AccessibilityBridge::AccessibilityBridgeDelegate| @@ -80,6 +81,7 @@ class AccessibilityBridgeMacDelegate : public AccessibilityBridge::Accessibility __weak FlutterEngine* flutter_engine_; __weak FlutterViewController* view_controller_; + std::weak_ptr accessibility_bridge_; }; } // namespace flutter diff --git a/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.mm b/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.mm index 21917a72ed1dd..b32bfc55a2918 100644 --- a/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.mm +++ b/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.mm @@ -22,12 +22,15 @@ AccessibilityBridgeMacDelegate::AccessibilityBridgeMacDelegate( __weak FlutterEngine* flutter_engine, - __weak FlutterViewController* view_controller) - : flutter_engine_(flutter_engine), view_controller_(view_controller) {} + __weak FlutterViewController* view_controller, + std::weak_ptr bridge) + : flutter_engine_(flutter_engine), + view_controller_(view_controller), + accessibility_bridge_(bridge) {} void AccessibilityBridgeMacDelegate::OnAccessibilityEvent( ui::AXEventGenerator::TargetedEvent targeted_event) { - if (!flutter_engine_.viewController.viewLoaded || !flutter_engine_.viewController.view.window) { + if (!view_controller_.viewLoaded || !view_controller_.view.window) { // Don't need to send accessibility events if the there is no view or window. return; } @@ -47,9 +50,8 @@ AccessibilityBridgeMacDelegate::MacOSEventsFromAXEvent(ui::AXEventGenerator::Event event_type, const ui::AXNode& ax_node) const { // Gets the native_node with the node_id. - NSCAssert(flutter_engine_, @"Flutter engine should not be deallocated"); - auto bridge = flutter_engine_.accessibilityBridge.lock(); - NSCAssert(bridge, @"Accessibility bridge in flutter engine must not be null."); + auto bridge = accessibility_bridge_.lock(); + NSCAssert(bridge, @"Accessibility bridge should not be expired"); auto platform_node_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(ax_node.id()).lock(); NSCAssert(platform_node_delegate, @"Event target must exist in accessibility bridge."); auto mac_platform_node_delegate = @@ -292,10 +294,10 @@ case ui::AXEventGenerator::Event::CHILDREN_CHANGED: { // NSAccessibilityCreatedNotification seems to be the only way to let // Voiceover pick up layout changes. - NSCAssert(flutter_engine_.viewController, @"The viewController must not be nil"); + NSCAssert(view_controller_, @"The viewController must not be nil"); events.push_back({ .name = NSAccessibilityCreatedNotification, - .target = flutter_engine_.viewController.view.window, + .target = view_controller_.view.window, .user_info = nil, }); break; @@ -363,7 +365,7 @@ FlutterSemanticsAction action, fml::MallocMapping data) { NSCAssert(flutter_engine_, @"Flutter engine should not be deallocated"); - NSCAssert(flutter_engine_.viewController.viewLoaded && flutter_engine_.viewController.view.window, + NSCAssert(view_controller_.viewLoaded && view_controller_.view.window, @"The accessibility bridge should not receive accessibility actions if the flutter view" @"is not loaded or attached to a NSWindow."); [flutter_engine_ dispatchSemanticsAction:action toTarget:target withData:std::move(data)]; @@ -371,7 +373,7 @@ std::shared_ptr AccessibilityBridgeMacDelegate::CreateFlutterPlatformNodeDelegate() { - return std::make_shared(flutter_engine_, view_controller_); + return std::make_shared(view_controller_, accessibility_bridge_); } // Private method @@ -394,9 +396,8 @@ } bool AccessibilityBridgeMacDelegate::HasPendingEvent(ui::AXEventGenerator::Event event) const { - NSCAssert(flutter_engine_, @"Flutter engine should not be deallocated"); - auto bridge = flutter_engine_.accessibilityBridge.lock(); - NSCAssert(bridge, @"Accessibility bridge in flutter engine must not be null."); + auto bridge = accessibility_bridge_.lock(); + NSCAssert(bridge, @"Accessibility bridge should not be expired"); std::vector pending_events = bridge->GetPendingEvents(); for (const auto& pending_event : bridge->GetPendingEvents()) { if (pending_event.event_params.event == event) { diff --git a/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegateTest.mm b/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegateTest.mm index 5f4a6d6a8d125..a70ebd76eec75 100644 --- a/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegateTest.mm +++ b/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegateTest.mm @@ -14,8 +14,9 @@ class AccessibilityBridgeMacDelegateSpy : public AccessibilityBridgeMacDelegate { public: AccessibilityBridgeMacDelegateSpy(__weak FlutterEngine* flutter_engine, - __weak FlutterViewController* view_controller) - : AccessibilityBridgeMacDelegate(flutter_engine, view_controller) {} + __weak FlutterViewController* view_controller, + std::weak_ptr bridge) + : AccessibilityBridgeMacDelegate(flutter_engine, view_controller, bridge) {} std::unordered_map actual_notifications; @@ -55,7 +56,12 @@ void DispatchMacOSNotification(gfx::NativeViewAccessible native_node, // Setting up bridge so that the AccessibilityBridgeMacDelegateSpy // can query semantics information from. engine.semanticsEnabled = YES; + + AccessibilityBridgeMacDelegateSpy* spy = + new AccessibilityBridgeMacDelegateSpy(engine, viewController, engine.accessibilityBridge); auto bridge = engine.accessibilityBridge.lock(); + bridge->UpdateDelegate(std::unique_ptr(spy)); + FlutterSemanticsNode root; root.id = 0; root.flags = static_cast(0); @@ -74,8 +80,6 @@ void DispatchMacOSNotification(gfx::NativeViewAccessible native_node, bridge->CommitUpdates(); auto platform_node_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); - AccessibilityBridgeMacDelegateSpy spy(engine, viewController); - // Creates a targeted event. ui::AXTree tree; ui::AXNode ax_node(&tree, nullptr, 0, 0); @@ -87,10 +91,10 @@ void DispatchMacOSNotification(gfx::NativeViewAccessible native_node, ax::mojom::EventFrom::kNone, intent); ui::AXEventGenerator::TargetedEvent targeted_event(&ax_node, event_params); - spy.OnAccessibilityEvent(targeted_event); + spy->OnAccessibilityEvent(targeted_event); - EXPECT_EQ(spy.actual_notifications.size(), 1u); - EXPECT_EQ(spy.actual_notifications.find([NSAccessibilityCreatedNotification UTF8String])->second, + EXPECT_EQ(spy->actual_notifications.size(), 1u); + EXPECT_EQ(spy->actual_notifications.find([NSAccessibilityCreatedNotification UTF8String])->second, expectedTarget); [engine shutDownEngine]; } @@ -107,7 +111,12 @@ void DispatchMacOSNotification(gfx::NativeViewAccessible native_node, // Setting up bridge so that the AccessibilityBridgeMacDelegateSpy // can query semantics information from. engine.semanticsEnabled = YES; + + AccessibilityBridgeMacDelegateSpy* spy = + new AccessibilityBridgeMacDelegateSpy(engine, viewController, engine.accessibilityBridge); auto bridge = engine.accessibilityBridge.lock(); + bridge->UpdateDelegate(std::unique_ptr(spy)); + FlutterSemanticsNode root; root.id = 0; root.flags = static_cast(0); @@ -126,8 +135,6 @@ void DispatchMacOSNotification(gfx::NativeViewAccessible native_node, bridge->CommitUpdates(); auto platform_node_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); - AccessibilityBridgeMacDelegateSpy spy(engine, viewController); - // Creates a targeted event. ui::AXTree tree; ui::AXNode ax_node(&tree, nullptr, 0, 0); @@ -139,10 +146,10 @@ void DispatchMacOSNotification(gfx::NativeViewAccessible native_node, ax::mojom::EventFrom::kNone, intent); ui::AXEventGenerator::TargetedEvent targeted_event(&ax_node, event_params); - spy.OnAccessibilityEvent(targeted_event); + spy->OnAccessibilityEvent(targeted_event); // Does not send any notification if the engine is headless. - EXPECT_EQ(spy.actual_notifications.size(), 0u); + EXPECT_EQ(spy->actual_notifications.size(), 0u); [engine shutDownEngine]; } @@ -160,7 +167,12 @@ void DispatchMacOSNotification(gfx::NativeViewAccessible native_node, // Setting up bridge so that the AccessibilityBridgeMacDelegateSpy // can query semantics information from. engine.semanticsEnabled = YES; + + AccessibilityBridgeMacDelegateSpy* spy = + new AccessibilityBridgeMacDelegateSpy(engine, viewController, engine.accessibilityBridge); auto bridge = engine.accessibilityBridge.lock(); + bridge->UpdateDelegate(std::unique_ptr(spy)); + FlutterSemanticsNode root; root.id = 0; root.flags = static_cast(0); @@ -179,8 +191,6 @@ void DispatchMacOSNotification(gfx::NativeViewAccessible native_node, bridge->CommitUpdates(); auto platform_node_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); - AccessibilityBridgeMacDelegateSpy spy(engine, viewController); - // Creates a targeted event. ui::AXTree tree; ui::AXNode ax_node(&tree, nullptr, 0, 0); @@ -192,10 +202,10 @@ void DispatchMacOSNotification(gfx::NativeViewAccessible native_node, ax::mojom::EventFrom::kNone, intent); ui::AXEventGenerator::TargetedEvent targeted_event(&ax_node, event_params); - spy.OnAccessibilityEvent(targeted_event); + spy->OnAccessibilityEvent(targeted_event); // Does not send any notification if the flutter view is not attached to a NSWindow. - EXPECT_EQ(spy.actual_notifications.size(), 0u); + EXPECT_EQ(spy->actual_notifications.size(), 0u); [engine shutDownEngine]; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 9c15b52f7d8ff..5bcc9152cf276 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -414,8 +414,8 @@ - (void)setViewController:(FlutterViewController*)controller { [_renderer setFlutterView:controller.flutterView]; if (_semanticsEnabled && _bridge) { - _bridge->UpdateDelegate( - std::make_unique(self, _viewController)); + _bridge->UpdateDelegate(std::make_unique( + self, _viewController, _bridge)); } if (!controller && !_allowHeadlessExecution) { @@ -597,8 +597,9 @@ - (void)setSemanticsEnabled:(BOOL)enabled { if (!_semanticsEnabled && _bridge) { _bridge.reset(); } else if (_semanticsEnabled && !_bridge) { - _bridge = std::make_shared( - std::make_unique(self, self.viewController)); + _bridge = std::make_shared(); + _bridge->UpdateDelegate(std::make_unique( + self, self.viewController, _bridge)); } _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled); } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.h b/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.h index 3077ab2fef6d2..82d78fa4fb890 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.h @@ -9,6 +9,7 @@ #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" +#include "flutter/shell/platform/common/accessibility_bridge.h" #include "flutter/shell/platform/common/flutter_platform_node_delegate.h" #include "flutter/shell/platform/embedder/embedder.h" @@ -20,8 +21,8 @@ namespace flutter { class FlutterPlatformNodeDelegateMac : public FlutterPlatformNodeDelegate { public: explicit FlutterPlatformNodeDelegateMac( - __weak FlutterEngine* engine, - __weak FlutterViewController* view_controller); + __weak FlutterViewController* view_controller, + std::weak_ptr accessibility_bridge); virtual ~FlutterPlatformNodeDelegateMac(); void Init(std::weak_ptr bridge, ui::AXNode* node) override; @@ -49,8 +50,8 @@ class FlutterPlatformNodeDelegateMac : public FlutterPlatformNodeDelegate { private: ui::AXPlatformNode* ax_platform_node_; - __weak FlutterEngine* engine_; __weak FlutterViewController* view_controller_; + std::weak_ptr accessibility_bridge_; gfx::RectF ConvertBoundsFromLocalToScreen( const gfx::RectF& local_bounds) const; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.mm b/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.mm index ab95291d504c9..ac17653fe013c 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.mm @@ -5,7 +5,6 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObject.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" @@ -21,9 +20,9 @@ namespace flutter { // namespace FlutterPlatformNodeDelegateMac::FlutterPlatformNodeDelegateMac( - __weak FlutterEngine* engine, - __weak FlutterViewController* view_controller) - : engine_(engine), view_controller_(view_controller) {} + __weak FlutterViewController* view_controller, + std::weak_ptr accessibility_bridge) + : view_controller_(view_controller), accessibility_bridge_(accessibility_bridge) {} void FlutterPlatformNodeDelegateMac::Init(std::weak_ptr bridge, ui::AXNode* node) { FlutterPlatformNodeDelegate::Init(bridge, node); @@ -48,9 +47,9 @@ gfx::NativeViewAccessible FlutterPlatformNodeDelegateMac::GetParent() { gfx::NativeViewAccessible parent = FlutterPlatformNodeDelegate::GetParent(); if (!parent) { - NSCAssert(engine_, @"Flutter engine should not be deallocated"); - NSCAssert(engine_.viewController.viewLoaded, @"Flutter view must be loaded"); - return engine_.viewController.flutterView; + NSCAssert(!accessibility_bridge_.expired(), @"Accessibility bridge should not be expired"); + NSCAssert(view_controller_.viewLoaded, @"Flutter view must be loaded"); + return view_controller_.flutterView; } return parent; } @@ -80,9 +79,8 @@ if (!text.empty()) { return text; }; - NSCAssert(engine_, @"Flutter engine should not be deallocated"); - auto bridge_ptr = engine_.accessibilityBridge.lock(); - NSCAssert(bridge_ptr, @"Accessibility bridge in flutter engine must not be null."); + auto bridge_ptr = accessibility_bridge_.lock(); + NSCAssert(bridge_ptr, @"Accessibility bridge should not be expired"); for (int32_t child : GetData().child_ids) { auto delegate_child = bridge_ptr->GetFlutterPlatformNodeDelegateFromID(child).lock(); if (!delegate_child) { @@ -105,14 +103,11 @@ // it converts the bounds from flutter coordinates to macOS coordinates. ns_local_bounds.origin.y = -ns_local_bounds.origin.y - ns_local_bounds.size.height; - NSCAssert(engine_, @"Flutter engine should not be deallocated"); - NSCAssert(engine_.viewController.viewLoaded, @"Flutter view must be loaded."); - NSRect ns_view_bounds = - [engine_.viewController.flutterView convertRectFromBacking:ns_local_bounds]; - NSRect ns_window_bounds = [engine_.viewController.flutterView convertRect:ns_view_bounds - toView:nil]; + NSCAssert(view_controller_.viewLoaded, @"Flutter view must be loaded."); + NSRect ns_view_bounds = [view_controller_.flutterView convertRectFromBacking:ns_local_bounds]; + NSRect ns_window_bounds = [view_controller_.flutterView convertRect:ns_view_bounds toView:nil]; NSRect ns_screen_bounds = - [[engine_.viewController.flutterView window] convertRectToScreen:ns_window_bounds]; + [[view_controller_.flutterView window] convertRectToScreen:ns_window_bounds]; gfx::RectF screen_bounds(ns_screen_bounds.origin.x, ns_screen_bounds.origin.y, ns_screen_bounds.size.width, ns_screen_bounds.size.height); return screen_bounds; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm index 4efb4d3e30bf5..8b743d1b358eb 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm @@ -1427,7 +1427,7 @@ - (bool)testSelectorsAreForwardedToFramework { engine.semanticsEnabled = YES; auto bridge = engine.accessibilityBridge.lock(); - FlutterPlatformNodeDelegateMac delegate(engine, viewController); + FlutterPlatformNodeDelegateMac delegate(viewController, engine.accessibilityBridge); ui::AXTree tree; ui::AXNode ax_node(&tree, nullptr, 0, 0); ui::AXNodeData node_data; @@ -1486,7 +1486,7 @@ - (bool)testSelectorsAreForwardedToFramework { engine.semanticsEnabled = YES; auto bridge = engine.accessibilityBridge.lock(); - FlutterPlatformNodeDelegateMac delegate(engine, viewController); + FlutterPlatformNodeDelegateMac delegate(viewController, engine.accessibilityBridge); ui::AXTree tree; ui::AXNode ax_node(&tree, nullptr, 0, 0); ui::AXNodeData node_data; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObjectTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObjectTest.mm index 2080cdbdb8fce..91c4ff8da0292 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObjectTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObjectTest.mm @@ -44,7 +44,7 @@ engine.semanticsEnabled = YES; auto bridge = engine.accessibilityBridge.lock(); - FlutterPlatformNodeDelegateMac delegate(engine, viewController); + FlutterPlatformNodeDelegateMac delegate(viewController, bridge); ui::AXTree tree; ui::AXNode ax_node(&tree, nullptr, 0, 0); ui::AXNodeData node_data; diff --git a/shell/platform/windows/accessibility_bridge_delegate_windows.cc b/shell/platform/windows/accessibility_bridge_delegate_windows.cc index dd2a4b0592154..bdca167b7d564 100644 --- a/shell/platform/windows/accessibility_bridge_delegate_windows.cc +++ b/shell/platform/windows/accessibility_bridge_delegate_windows.cc @@ -11,8 +11,10 @@ namespace flutter { AccessibilityBridgeDelegateWindows::AccessibilityBridgeDelegateWindows( - FlutterWindowsEngine* engine) - : engine_(engine) { + FlutterWindowsEngine* engine, + FlutterWindowsView* view, + std::weak_ptr bridge) + : engine_(engine), view_(view), accessibility_bridge_(bridge) { assert(engine_); } @@ -22,7 +24,7 @@ void AccessibilityBridgeDelegateWindows::OnAccessibilityEvent( ui::AXEventGenerator::Event event_type = targeted_event.event_params.event; // Look up the flutter platform node delegate. - auto bridge = engine_->accessibility_bridge().lock(); + auto bridge = accessibility_bridge_.lock(); assert(bridge); auto node_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(ax_node->id()).lock(); @@ -147,7 +149,8 @@ void AccessibilityBridgeDelegateWindows::DispatchAccessibilityAction( std::shared_ptr AccessibilityBridgeDelegateWindows::CreateFlutterPlatformNodeDelegate() { - return std::make_shared(engine_); + return std::make_shared( + view_, accessibility_bridge_); } void AccessibilityBridgeDelegateWindows::DispatchWinAccessibilityEvent( diff --git a/shell/platform/windows/accessibility_bridge_delegate_windows.h b/shell/platform/windows/accessibility_bridge_delegate_windows.h index d9c2060867876..ee188294219a7 100644 --- a/shell/platform/windows/accessibility_bridge_delegate_windows.h +++ b/shell/platform/windows/accessibility_bridge_delegate_windows.h @@ -24,7 +24,9 @@ class FlutterWindowsEngine; class AccessibilityBridgeDelegateWindows : public AccessibilityBridge::AccessibilityBridgeDelegate { public: - explicit AccessibilityBridgeDelegateWindows(FlutterWindowsEngine* engine); + AccessibilityBridgeDelegateWindows(FlutterWindowsEngine* engine, + FlutterWindowsView* view, + std::weak_ptr bridge); virtual ~AccessibilityBridgeDelegateWindows() = default; // |AccessibilityBridge::AccessibilityBridgeDelegate| @@ -53,6 +55,8 @@ class AccessibilityBridgeDelegateWindows private: FlutterWindowsEngine* engine_; + FlutterWindowsView* view_; + std::weak_ptr accessibility_bridge_; }; } // namespace flutter diff --git a/shell/platform/windows/accessibility_bridge_delegate_windows_unittests.cc b/shell/platform/windows/accessibility_bridge_delegate_windows_unittests.cc index 5db7fc4c7de38..1fdc136b55443 100644 --- a/shell/platform/windows/accessibility_bridge_delegate_windows_unittests.cc +++ b/shell/platform/windows/accessibility_bridge_delegate_windows_unittests.cc @@ -36,8 +36,11 @@ struct MsaaEvent { class AccessibilityBridgeDelegateWindowsSpy : public AccessibilityBridgeDelegateWindows { public: - explicit AccessibilityBridgeDelegateWindowsSpy(FlutterWindowsEngine* engine) - : AccessibilityBridgeDelegateWindows(engine) {} + explicit AccessibilityBridgeDelegateWindowsSpy( + FlutterWindowsEngine* engine, + FlutterWindowsView* view, + std::weak_ptr bridge) + : AccessibilityBridgeDelegateWindows(engine, view, bridge) {} void DispatchWinAccessibilityEvent( std::shared_ptr node_delegate, @@ -154,11 +157,14 @@ void ExpectWinEventFromAXEvent(int32_t node_id, auto bridge = view.GetEngine()->accessibility_bridge().lock(); PopulateAXTree(bridge); - AccessibilityBridgeDelegateWindowsSpy spy(view.GetEngine()); - spy.OnAccessibilityEvent({AXNodeFromID(bridge, node_id), - {ax_event, ax::mojom::EventFrom::kNone, {}}}); - ASSERT_EQ(spy.dispatched_events().size(), 1); - EXPECT_EQ(spy.dispatched_events()[0].event_type, expected_event); + auto spy = new AccessibilityBridgeDelegateWindowsSpy( + view.GetEngine(), &view, view.GetEngine()->accessibility_bridge()); + bridge->UpdateDelegate( + std::unique_ptr(spy)); + spy->OnAccessibilityEvent({AXNodeFromID(bridge, node_id), + {ax_event, ax::mojom::EventFrom::kNone, {}}}); + ASSERT_EQ(spy->dispatched_events().size(), 1); + EXPECT_EQ(spy->dispatched_events()[0].event_type, expected_event); } } // namespace @@ -171,6 +177,10 @@ TEST(AccessibilityBridgeDelegateWindows, GetParent) { view.OnUpdateSemanticsEnabled(true); auto bridge = view.GetEngine()->accessibility_bridge().lock(); + auto spy = new AccessibilityBridgeDelegateWindowsSpy( + view.GetEngine(), &view, view.GetEngine()->accessibility_bridge()); + bridge->UpdateDelegate( + std::unique_ptr(spy)); PopulateAXTree(bridge); auto node0_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); @@ -187,6 +197,9 @@ TEST(AccessibilityBridgeDelegateWindows, GetParentOnRootRetunsNullptr) { view.OnUpdateSemanticsEnabled(true); auto bridge = view.GetEngine()->accessibility_bridge().lock(); + bridge->UpdateDelegate( + std::make_unique( + view.GetEngine(), &view, view.GetEngine()->accessibility_bridge())); PopulateAXTree(bridge); auto node0_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); @@ -201,6 +214,10 @@ TEST(AccessibilityBridgeDelegateWindows, DispatchAccessibilityAction) { view.OnUpdateSemanticsEnabled(true); auto bridge = view.GetEngine()->accessibility_bridge().lock(); + auto spy = new AccessibilityBridgeDelegateWindowsSpy( + view.GetEngine(), &view, view.GetEngine()->accessibility_bridge()); + bridge->UpdateDelegate( + std::unique_ptr(spy)); PopulateAXTree(bridge); FlutterSemanticsAction actual_action = kFlutterSemanticsActionTap; @@ -214,8 +231,7 @@ TEST(AccessibilityBridgeDelegateWindows, DispatchAccessibilityAction) { return kSuccess; })); - AccessibilityBridgeDelegateWindows delegate(view.GetEngine()); - delegate.DispatchAccessibilityAction(1, kFlutterSemanticsActionCopy, {}); + spy->DispatchAccessibilityAction(1, kFlutterSemanticsActionCopy, {}); EXPECT_EQ(actual_action, kFlutterSemanticsActionCopy); } @@ -239,16 +255,20 @@ TEST(AccessibilityBridgeDelegateWindows, OnAccessibilityEventFocusChanged) { auto bridge = view.GetEngine()->accessibility_bridge().lock(); PopulateAXTree(bridge); - AccessibilityBridgeDelegateWindowsSpy 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); + auto spy = new AccessibilityBridgeDelegateWindowsSpy( + view.GetEngine(), &view, view.GetEngine()->accessibility_bridge()); + bridge->UpdateDelegate( + std::unique_ptr(spy)); - ASSERT_EQ(spy.focused_nodes().size(), 1); - EXPECT_EQ(spy.focused_nodes()[0], 1); + 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(AccessibilityBridgeDelegateWindows, OnAccessibilityEventIgnoredChanged) { diff --git a/shell/platform/windows/flutter_platform_node_delegate_windows.cc b/shell/platform/windows/flutter_platform_node_delegate_windows.cc index a2c95daa817bd..fbcc2f16c8174 100644 --- a/shell/platform/windows/flutter_platform_node_delegate_windows.cc +++ b/shell/platform/windows/flutter_platform_node_delegate_windows.cc @@ -13,10 +13,9 @@ namespace flutter { FlutterPlatformNodeDelegateWindows::FlutterPlatformNodeDelegateWindows( - FlutterWindowsEngine* engine) - : engine_(engine) { - assert(engine_); -} + FlutterWindowsView* view, + std::weak_ptr bridge) + : view_(view), bridge_(bridge) {} FlutterPlatformNodeDelegateWindows::~FlutterPlatformNodeDelegateWindows() { if (ax_platform_node_) { @@ -53,8 +52,10 @@ gfx::NativeViewAccessible FlutterPlatformNodeDelegateWindows::HitTestSync( } // If any child in this node's subtree contains the point, return that child. - auto bridge = engine_->accessibility_bridge().lock(); - assert(bridge); + auto bridge = bridge_.lock(); + if (!bridge) { + return nullptr; + } for (const ui::AXNode* child : GetAXNode()->children()) { std::shared_ptr win_delegate = std::static_pointer_cast( @@ -80,19 +81,18 @@ gfx::Rect FlutterPlatformNodeDelegateWindows::GetBoundsRect( coordinate_system, clipping_behavior, offscreen_result); POINT origin{bounds.x(), bounds.y()}; POINT extent{bounds.x() + bounds.width(), bounds.y() + bounds.height()}; - ClientToScreen(engine_->view()->GetPlatformWindow(), &origin); - ClientToScreen(engine_->view()->GetPlatformWindow(), &extent); + ClientToScreen(view_->GetPlatformWindow(), &origin); + ClientToScreen(view_->GetPlatformWindow(), &extent); return gfx::Rect(origin.x, origin.y, extent.x - origin.x, extent.y - origin.y); } void FlutterPlatformNodeDelegateWindows::DispatchWinAccessibilityEvent( DWORD event_type) { - FlutterWindowsView* view = engine_->view(); - if (!view) { + if (!view_) { return; } - HWND hwnd = view->GetPlatformWindow(); + HWND hwnd = view_->GetPlatformWindow(); if (!hwnd) { return; } diff --git a/shell/platform/windows/flutter_platform_node_delegate_windows.h b/shell/platform/windows/flutter_platform_node_delegate_windows.h index b77cc0ca7e107..c89638f8aee45 100644 --- a/shell/platform/windows/flutter_platform_node_delegate_windows.h +++ b/shell/platform/windows/flutter_platform_node_delegate_windows.h @@ -5,6 +5,8 @@ #ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_PLATFORM_NODE_DELEGATE_H_ #define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_PLATFORM_NODE_DELEGATE_H_ +#include + #include "flutter/shell/platform/common/flutter_platform_node_delegate.h" #include "flutter/shell/platform/windows/flutter_windows_engine.h" #include "flutter/third_party/accessibility/ax/platform/ax_platform_node.h" @@ -20,7 +22,8 @@ class FlutterWindowsEngine; // that compose the accessibility tree. class FlutterPlatformNodeDelegateWindows : public FlutterPlatformNodeDelegate { public: - explicit FlutterPlatformNodeDelegateWindows(FlutterWindowsEngine* engine); + FlutterPlatformNodeDelegateWindows(FlutterWindowsView* view, + std::weak_ptr bridge); virtual ~FlutterPlatformNodeDelegateWindows(); // |ui::AXPlatformNodeDelegate| @@ -51,7 +54,8 @@ class FlutterPlatformNodeDelegateWindows : public FlutterPlatformNodeDelegate { private: ui::AXPlatformNode* ax_platform_node_; - FlutterWindowsEngine* engine_; + FlutterWindowsView* view_; + std::weak_ptr bridge_; }; } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index e862a94029b11..9f5d017b015ee 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -594,8 +594,10 @@ void FlutterWindowsEngine::UpdateSemanticsEnabled(bool enabled) { if (!semantics_enabled_ && accessibility_bridge_) { accessibility_bridge_.reset(); } else if (semantics_enabled_ && !accessibility_bridge_) { - accessibility_bridge_ = std::make_shared( - std::make_unique(this)); + accessibility_bridge_ = std::make_shared(); + accessibility_bridge_->UpdateDelegate( + std::make_unique( + this, view_, accessibility_bridge_)); } } }