11 #include "flutter/shell/platform/embedder/embedder.h"
25 : flutter_engine_(flutter_engine), view_controller_(view_controller) {}
28 ui::AXEventGenerator::TargetedEvent targeted_event) {
29 if (!view_controller_.viewLoaded || !view_controller_.view.window) {
33 ui::AXNode* ax_node = targeted_event.node;
34 std::vector<AccessibilityBridgeMac::NSAccessibilityEvent> events =
35 MacOSEventsFromAXEvent(targeted_event.event_params.event, *ax_node);
36 for (
const AccessibilityBridgeMac::NSAccessibilityEvent& event : events) {
37 if (event.user_info != nil) {
38 DispatchMacOSNotificationWithUserInfo(event.target, event.name, event.user_info);
40 DispatchMacOSNotification(event.target, event.name);
45 std::vector<AccessibilityBridgeMac::NSAccessibilityEvent>
46 AccessibilityBridgeMac::MacOSEventsFromAXEvent(ui::AXEventGenerator::Event event_type,
47 const ui::AXNode& ax_node)
const {
49 NSCAssert(flutter_engine_,
@"Flutter engine should not be deallocated");
51 NSCAssert(platform_node_delegate,
@"Event target must exist in accessibility bridge.");
52 auto mac_platform_node_delegate =
53 std::static_pointer_cast<FlutterPlatformNodeDelegateMac>(platform_node_delegate);
54 gfx::NativeViewAccessible native_node = mac_platform_node_delegate->GetNativeViewAccessible();
56 std::vector<AccessibilityBridgeMac::NSAccessibilityEvent> events;
58 case ui::AXEventGenerator::Event::ACTIVE_DESCENDANT_CHANGED:
59 if (ax_node.data().role == ax::mojom::Role::kTree) {
61 .name = NSAccessibilitySelectedRowsChangedNotification,
62 .target = native_node,
65 }
else if (ax_node.data().role == ax::mojom::Role::kTextFieldWithComboBox) {
70 .name = NSAccessibilitySelectedChildrenChangedNotification,
71 .target = native_node,
79 case ui::AXEventGenerator::Event::LOAD_COMPLETE:
82 .target = native_node,
86 case ui::AXEventGenerator::Event::INVALID_STATUS_CHANGED:
89 .target = native_node,
93 case ui::AXEventGenerator::Event::SELECTED_CHILDREN_CHANGED:
94 if (ui::IsTableLike(ax_node.data().role)) {
96 .name = NSAccessibilitySelectedRowsChangedNotification,
97 .target = native_node,
104 NSAccessibilityElement* native_accessibility_node = (NSAccessibilityElement*)native_node;
105 if (native_accessibility_node.accessibilityFocusedUIElement &&
106 ax_node.data().HasState(ax::mojom::State::kMultiselectable) &&
107 !HasPendingEvent(ui::AXEventGenerator::Event::ACTIVE_DESCENDANT_CHANGED) &&
108 !HasPendingEvent(ui::AXEventGenerator::Event::FOCUS_CHANGED)) {
114 .name = NSAccessibilitySelectedChildrenChangedNotification,
115 .target = native_node,
120 case ui::AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED: {
121 id focused = mac_platform_node_delegate->GetFocus();
127 if (native_text_field == mac_platform_node_delegate->GetFocus()) {
128 [native_text_field startEditing];
134 .name = NSAccessibilitySelectedTextChangedNotification,
135 .target = native_node,
141 int32_t focus = tree_data.focus_id;
142 if (focus == ui::AXNode::kInvalidAXID || focus != tree_data.sel_anchor_object_id) {
150 .name = NSAccessibilitySelectedTextChangedNotification,
151 .target = focus_node->GetNativeViewAccessible(),
156 case ui::AXEventGenerator::Event::CHECKED_STATE_CHANGED:
158 .name = NSAccessibilityValueChangedNotification,
159 .target = native_node,
163 case ui::AXEventGenerator::Event::VALUE_CHANGED: {
164 if (ax_node.data().role == ax::mojom::Role::kTextField) {
170 id focused = mac_platform_node_delegate->GetFocus();
171 if (!focused || native_text_field == focused) {
172 [native_text_field startEditing];
177 .name = NSAccessibilityValueChangedNotification,
178 .target = native_node,
181 if (ax_node.data().HasState(ax::mojom::State::kEditable)) {
183 .name = NSAccessibilityValueChangedNotification,
190 case ui::AXEventGenerator::Event::LIVE_REGION_CREATED:
193 .target = native_node,
197 case ui::AXEventGenerator::Event::ALERT: {
200 .target = native_node,
205 auto live_region_events =
206 MacOSEventsFromAXEvent(ui::AXEventGenerator::Event::LIVE_REGION_CHANGED, ax_node);
207 events.insert(events.end(), live_region_events.begin(), live_region_events.end());
210 case ui::AXEventGenerator::Event::LIVE_REGION_CHANGED: {
214 .target = native_node,
219 case ui::AXEventGenerator::Event::ROW_COUNT_CHANGED:
221 .name = NSAccessibilityRowCountChangedNotification,
222 .target = native_node,
226 case ui::AXEventGenerator::Event::EXPANDED: {
227 NSAccessibilityNotificationName mac_notification;
228 if (ax_node.data().role == ax::mojom::Role::kRow ||
229 ax_node.data().role == ax::mojom::Role::kTreeItem) {
230 mac_notification = NSAccessibilityRowExpandedNotification;
235 .name = mac_notification,
236 .target = native_node,
241 case ui::AXEventGenerator::Event::COLLAPSED: {
242 NSAccessibilityNotificationName mac_notification;
243 if (ax_node.data().role == ax::mojom::Role::kRow ||
244 ax_node.data().role == ax::mojom::Role::kTreeItem) {
245 mac_notification = NSAccessibilityRowCollapsedNotification;
250 .name = mac_notification,
251 .target = native_node,
256 case ui::AXEventGenerator::Event::MENU_ITEM_SELECTED:
259 .target = native_node,
263 case ui::AXEventGenerator::Event::CHILDREN_CHANGED: {
267 .name = NSAccessibilityCreatedNotification,
268 .target = view_controller_.view.window,
273 case ui::AXEventGenerator::Event::SUBTREE_CREATED:
274 case ui::AXEventGenerator::Event::ACCESS_KEY_CHANGED:
275 case ui::AXEventGenerator::Event::ATK_TEXT_OBJECT_ATTRIBUTE_CHANGED:
276 case ui::AXEventGenerator::Event::ATOMIC_CHANGED:
277 case ui::AXEventGenerator::Event::AUTO_COMPLETE_CHANGED:
278 case ui::AXEventGenerator::Event::BUSY_CHANGED:
279 case ui::AXEventGenerator::Event::CONTROLS_CHANGED:
280 case ui::AXEventGenerator::Event::CLASS_NAME_CHANGED:
281 case ui::AXEventGenerator::Event::DESCRIBED_BY_CHANGED:
282 case ui::AXEventGenerator::Event::DESCRIPTION_CHANGED:
283 case ui::AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED:
284 case ui::AXEventGenerator::Event::DROPEFFECT_CHANGED:
285 case ui::AXEventGenerator::Event::ENABLED_CHANGED:
286 case ui::AXEventGenerator::Event::FOCUS_CHANGED:
287 case ui::AXEventGenerator::Event::FLOW_FROM_CHANGED:
288 case ui::AXEventGenerator::Event::FLOW_TO_CHANGED:
289 case ui::AXEventGenerator::Event::GRABBED_CHANGED:
290 case ui::AXEventGenerator::Event::HASPOPUP_CHANGED:
291 case ui::AXEventGenerator::Event::HIERARCHICAL_LEVEL_CHANGED:
292 case ui::AXEventGenerator::Event::IGNORED_CHANGED:
293 case ui::AXEventGenerator::Event::IMAGE_ANNOTATION_CHANGED:
294 case ui::AXEventGenerator::Event::KEY_SHORTCUTS_CHANGED:
295 case ui::AXEventGenerator::Event::LABELED_BY_CHANGED:
296 case ui::AXEventGenerator::Event::LANGUAGE_CHANGED:
297 case ui::AXEventGenerator::Event::LAYOUT_INVALIDATED:
298 case ui::AXEventGenerator::Event::LIVE_REGION_NODE_CHANGED:
299 case ui::AXEventGenerator::Event::LIVE_RELEVANT_CHANGED:
300 case ui::AXEventGenerator::Event::LIVE_STATUS_CHANGED:
301 case ui::AXEventGenerator::Event::LOAD_START:
302 case ui::AXEventGenerator::Event::MULTILINE_STATE_CHANGED:
303 case ui::AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED:
304 case ui::AXEventGenerator::Event::NAME_CHANGED:
305 case ui::AXEventGenerator::Event::OBJECT_ATTRIBUTE_CHANGED:
306 case ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED:
307 case ui::AXEventGenerator::Event::PLACEHOLDER_CHANGED:
308 case ui::AXEventGenerator::Event::PORTAL_ACTIVATED:
309 case ui::AXEventGenerator::Event::POSITION_IN_SET_CHANGED:
310 case ui::AXEventGenerator::Event::READONLY_CHANGED:
311 case ui::AXEventGenerator::Event::RELATED_NODE_CHANGED:
312 case ui::AXEventGenerator::Event::REQUIRED_STATE_CHANGED:
313 case ui::AXEventGenerator::Event::ROLE_CHANGED:
314 case ui::AXEventGenerator::Event::SCROLL_HORIZONTAL_POSITION_CHANGED:
315 case ui::AXEventGenerator::Event::SCROLL_VERTICAL_POSITION_CHANGED:
316 case ui::AXEventGenerator::Event::SELECTED_CHANGED:
317 case ui::AXEventGenerator::Event::SET_SIZE_CHANGED:
318 case ui::AXEventGenerator::Event::SORT_CHANGED:
319 case ui::AXEventGenerator::Event::STATE_CHANGED:
320 case ui::AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED:
321 case ui::AXEventGenerator::Event::VALUE_MAX_CHANGED:
322 case ui::AXEventGenerator::Event::VALUE_MIN_CHANGED:
323 case ui::AXEventGenerator::Event::VALUE_STEP_CHANGED:
324 case ui::AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED:
333 FlutterSemanticsAction action,
334 fml::MallocMapping data) {
335 NSCAssert(flutter_engine_,
@"Flutter engine should not be deallocated");
336 NSCAssert(view_controller_.viewLoaded && view_controller_.view.window,
337 @"The accessibility bridge should not receive accessibility actions if the flutter view"
338 @"is not loaded or attached to a NSWindow.");
339 [flutter_engine_ dispatchSemanticsAction:action toTarget:target withData:std::move(data)];
342 std::shared_ptr<FlutterPlatformNodeDelegate>
344 return std::make_shared<FlutterPlatformNodeDelegateMac>(weak_from_this(), view_controller_);
348 void AccessibilityBridgeMac::DispatchMacOSNotification(
349 gfx::NativeViewAccessible native_node,
350 NSAccessibilityNotificationName mac_notification) {
351 NSCAssert(mac_notification,
@"The notification must not be null.");
352 NSCAssert(native_node,
@"The notification target must not be null.");
353 NSAccessibilityPostNotification(native_node, mac_notification);
356 void AccessibilityBridgeMac::DispatchMacOSNotificationWithUserInfo(
357 gfx::NativeViewAccessible native_node,
358 NSAccessibilityNotificationName mac_notification,
359 NSDictionary* user_info) {
360 NSCAssert(mac_notification,
@"The notification must not be null.");
361 NSCAssert(native_node,
@"The notification target must not be null.");
362 NSCAssert(user_info,
@"The notification data must not be null.");
363 NSAccessibilityPostNotificationWithUserInfo(native_node, mac_notification, user_info);
366 bool AccessibilityBridgeMac::HasPendingEvent(ui::AXEventGenerator::Event event)
const {
367 NSCAssert(flutter_engine_,
@"Flutter engine should not be deallocated");
368 std::vector<ui::AXEventGenerator::TargetedEvent> pending_events =
GetPendingEvents();
370 if (pending_event.event_params.event == event) {