Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
ax_platform_node_mac.mm
Go to the documentation of this file.
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#import <Cocoa/Cocoa.h>
8
9#include <cstddef>
10
11#include "ax/ax_action_data.h"
12#include "ax/ax_node_data.h"
14#include "ax_platform_node.h"
16#include "base/no_destructor.h"
18
19namespace {
20
21NSString* const NSAccessibilityScrollToVisibleAction = @"AXScrollToVisible";
22
23// Same length as web content/WebKit.
24static int kLiveRegionDebounceMillis = 20;
25
26using RoleMap = std::map<ax::mojom::Role, NSString*>;
27using EventMap = std::map<ax::mojom::Event, NSString*>;
28using ActionList = std::vector<std::pair<ax::mojom::Action, NSString*>>;
29
30struct AnnouncementSpec {
33 bool is_polite;
34};
35
36RoleMap BuildRoleMap() {
37 const RoleMap::value_type roles[] = {
38 {ax::mojom::Role::kAbbr, NSAccessibilityGroupRole},
39 {ax::mojom::Role::kAlert, NSAccessibilityGroupRole},
40 {ax::mojom::Role::kAlertDialog, NSAccessibilityGroupRole},
41 {ax::mojom::Role::kAnchor, NSAccessibilityGroupRole},
42 {ax::mojom::Role::kApplication, NSAccessibilityGroupRole},
43 {ax::mojom::Role::kArticle, NSAccessibilityGroupRole},
44 {ax::mojom::Role::kAudio, NSAccessibilityGroupRole},
45 {ax::mojom::Role::kBanner, NSAccessibilityGroupRole},
46 {ax::mojom::Role::kBlockquote, NSAccessibilityGroupRole},
47 {ax::mojom::Role::kButton, NSAccessibilityButtonRole},
48 {ax::mojom::Role::kCanvas, NSAccessibilityImageRole},
49 {ax::mojom::Role::kCaption, NSAccessibilityGroupRole},
50 {ax::mojom::Role::kCell, @"AXCell"},
51 {ax::mojom::Role::kCheckBox, NSAccessibilityCheckBoxRole},
52 {ax::mojom::Role::kCode, NSAccessibilityGroupRole},
53 {ax::mojom::Role::kColorWell, NSAccessibilityColorWellRole},
54 {ax::mojom::Role::kColumn, NSAccessibilityColumnRole},
56 {ax::mojom::Role::kComboBoxGrouping, NSAccessibilityGroupRole},
57 {ax::mojom::Role::kComboBoxMenuButton, NSAccessibilityPopUpButtonRole},
58 {ax::mojom::Role::kComment, NSAccessibilityGroupRole},
59 {ax::mojom::Role::kComplementary, NSAccessibilityGroupRole},
60 {ax::mojom::Role::kContentDeletion, NSAccessibilityGroupRole},
61 {ax::mojom::Role::kContentInsertion, NSAccessibilityGroupRole},
62 {ax::mojom::Role::kContentInfo, NSAccessibilityGroupRole},
63 {ax::mojom::Role::kDate, @"AXDateField"},
64 {ax::mojom::Role::kDateTime, @"AXDateField"},
65 {ax::mojom::Role::kDefinition, NSAccessibilityGroupRole},
66 {ax::mojom::Role::kDescriptionListDetail, NSAccessibilityGroupRole},
67 {ax::mojom::Role::kDescriptionList, NSAccessibilityListRole},
68 {ax::mojom::Role::kDescriptionListTerm, NSAccessibilityGroupRole},
69 {ax::mojom::Role::kDialog, NSAccessibilityGroupRole},
70 {ax::mojom::Role::kDetails, NSAccessibilityGroupRole},
71 {ax::mojom::Role::kDirectory, NSAccessibilityListRole},
72 // If Mac supports AXExpandedChanged event with
73 // NSAccessibilityDisclosureTriangleRole, We should update
74 // ax::mojom::Role::kDisclosureTriangle mapping to
75 // NSAccessibilityDisclosureTriangleRole. http://crbug.com/558324
76 {ax::mojom::Role::kDisclosureTriangle, NSAccessibilityButtonRole},
77 {ax::mojom::Role::kDocAbstract, NSAccessibilityGroupRole},
78 {ax::mojom::Role::kDocAcknowledgments, NSAccessibilityGroupRole},
79 {ax::mojom::Role::kDocAfterword, NSAccessibilityGroupRole},
80 {ax::mojom::Role::kDocAppendix, NSAccessibilityGroupRole},
81 {ax::mojom::Role::kDocBackLink, NSAccessibilityLinkRole},
82 {ax::mojom::Role::kDocBiblioEntry, NSAccessibilityGroupRole},
83 {ax::mojom::Role::kDocBibliography, NSAccessibilityGroupRole},
84 {ax::mojom::Role::kDocBiblioRef, NSAccessibilityLinkRole},
85 {ax::mojom::Role::kDocChapter, NSAccessibilityGroupRole},
86 {ax::mojom::Role::kDocColophon, NSAccessibilityGroupRole},
87 {ax::mojom::Role::kDocConclusion, NSAccessibilityGroupRole},
88 {ax::mojom::Role::kDocCover, NSAccessibilityImageRole},
89 {ax::mojom::Role::kDocCredit, NSAccessibilityGroupRole},
90 {ax::mojom::Role::kDocCredits, NSAccessibilityGroupRole},
91 {ax::mojom::Role::kDocDedication, NSAccessibilityGroupRole},
92 {ax::mojom::Role::kDocEndnote, NSAccessibilityGroupRole},
93 {ax::mojom::Role::kDocEndnotes, NSAccessibilityGroupRole},
94 {ax::mojom::Role::kDocEpigraph, NSAccessibilityGroupRole},
95 {ax::mojom::Role::kDocEpilogue, NSAccessibilityGroupRole},
96 {ax::mojom::Role::kDocErrata, NSAccessibilityGroupRole},
97 {ax::mojom::Role::kDocExample, NSAccessibilityGroupRole},
98 {ax::mojom::Role::kDocFootnote, NSAccessibilityGroupRole},
99 {ax::mojom::Role::kDocForeword, NSAccessibilityGroupRole},
100 {ax::mojom::Role::kDocGlossary, NSAccessibilityGroupRole},
101 {ax::mojom::Role::kDocGlossRef, NSAccessibilityLinkRole},
102 {ax::mojom::Role::kDocIndex, NSAccessibilityGroupRole},
103 {ax::mojom::Role::kDocIntroduction, NSAccessibilityGroupRole},
104 {ax::mojom::Role::kDocNoteRef, NSAccessibilityLinkRole},
105 {ax::mojom::Role::kDocNotice, NSAccessibilityGroupRole},
106 {ax::mojom::Role::kDocPageBreak, NSAccessibilitySplitterRole},
107 {ax::mojom::Role::kDocPageList, NSAccessibilityGroupRole},
108 {ax::mojom::Role::kDocPart, NSAccessibilityGroupRole},
109 {ax::mojom::Role::kDocPreface, NSAccessibilityGroupRole},
110 {ax::mojom::Role::kDocPrologue, NSAccessibilityGroupRole},
111 {ax::mojom::Role::kDocPullquote, NSAccessibilityGroupRole},
112 {ax::mojom::Role::kDocQna, NSAccessibilityGroupRole},
113 {ax::mojom::Role::kDocSubtitle, @"AXHeading"},
114 {ax::mojom::Role::kDocTip, NSAccessibilityGroupRole},
115 {ax::mojom::Role::kDocToc, NSAccessibilityGroupRole},
116 {ax::mojom::Role::kDocument, NSAccessibilityGroupRole},
117 {ax::mojom::Role::kEmbeddedObject, NSAccessibilityGroupRole},
118 {ax::mojom::Role::kEmphasis, NSAccessibilityGroupRole},
119 {ax::mojom::Role::kFigcaption, NSAccessibilityGroupRole},
120 {ax::mojom::Role::kFigure, NSAccessibilityGroupRole},
121 {ax::mojom::Role::kFooter, NSAccessibilityGroupRole},
122 {ax::mojom::Role::kFooterAsNonLandmark, NSAccessibilityGroupRole},
123 {ax::mojom::Role::kForm, NSAccessibilityGroupRole},
124 {ax::mojom::Role::kGenericContainer, NSAccessibilityGroupRole},
125 {ax::mojom::Role::kGraphicsDocument, NSAccessibilityGroupRole},
126 {ax::mojom::Role::kGraphicsObject, NSAccessibilityGroupRole},
127 {ax::mojom::Role::kGraphicsSymbol, NSAccessibilityImageRole},
128 // Should be NSAccessibilityGridRole but VoiceOver treating it like
129 // a list as of 10.12.6, so following WebKit and using table role:
130 {ax::mojom::Role::kGrid, NSAccessibilityTableRole}, // crbug.com/753925
131 {ax::mojom::Role::kGroup, NSAccessibilityGroupRole},
132 {ax::mojom::Role::kHeader, NSAccessibilityGroupRole},
133 {ax::mojom::Role::kHeaderAsNonLandmark, NSAccessibilityGroupRole},
134 {ax::mojom::Role::kHeading, @"AXHeading"},
135 {ax::mojom::Role::kIframe, NSAccessibilityGroupRole},
136 {ax::mojom::Role::kIframePresentational, NSAccessibilityGroupRole},
137 {ax::mojom::Role::kIgnored, NSAccessibilityUnknownRole},
138 {ax::mojom::Role::kImage, NSAccessibilityImageRole},
139 {ax::mojom::Role::kImageMap, NSAccessibilityGroupRole},
140 {ax::mojom::Role::kInputTime, @"AXTimeField"},
141 {ax::mojom::Role::kLabelText, NSAccessibilityGroupRole},
142 {ax::mojom::Role::kLayoutTable, NSAccessibilityGroupRole},
143 {ax::mojom::Role::kLayoutTableCell, NSAccessibilityGroupRole},
144 {ax::mojom::Role::kLayoutTableRow, NSAccessibilityGroupRole},
145 {ax::mojom::Role::kLegend, NSAccessibilityGroupRole},
146 {ax::mojom::Role::kLineBreak, NSAccessibilityGroupRole},
147 {ax::mojom::Role::kLink, NSAccessibilityLinkRole},
148 {ax::mojom::Role::kList, NSAccessibilityListRole},
149 {ax::mojom::Role::kListBox, NSAccessibilityListRole},
150 {ax::mojom::Role::kListBoxOption, NSAccessibilityStaticTextRole},
151 {ax::mojom::Role::kListItem, NSAccessibilityGroupRole},
152 {ax::mojom::Role::kListMarker, @"AXListMarker"},
153 {ax::mojom::Role::kLog, NSAccessibilityGroupRole},
154 {ax::mojom::Role::kMain, NSAccessibilityGroupRole},
155 {ax::mojom::Role::kMark, NSAccessibilityGroupRole},
156 {ax::mojom::Role::kMarquee, NSAccessibilityGroupRole},
157 {ax::mojom::Role::kMath, NSAccessibilityGroupRole},
158 {ax::mojom::Role::kMenu, NSAccessibilityMenuRole},
159 {ax::mojom::Role::kMenuBar, NSAccessibilityMenuBarRole},
160 {ax::mojom::Role::kMenuItem, NSAccessibilityMenuItemRole},
161 {ax::mojom::Role::kMenuItemCheckBox, NSAccessibilityMenuItemRole},
162 {ax::mojom::Role::kMenuItemRadio, NSAccessibilityMenuItemRole},
163 {ax::mojom::Role::kMenuListOption, NSAccessibilityMenuItemRole},
164 {ax::mojom::Role::kMenuListPopup, NSAccessibilityMenuRole},
165 {ax::mojom::Role::kMeter, NSAccessibilityLevelIndicatorRole},
166 {ax::mojom::Role::kNavigation, NSAccessibilityGroupRole},
167 {ax::mojom::Role::kNone, NSAccessibilityGroupRole},
168 {ax::mojom::Role::kNote, NSAccessibilityGroupRole},
169 {ax::mojom::Role::kParagraph, NSAccessibilityGroupRole},
170 {ax::mojom::Role::kPdfActionableHighlight, NSAccessibilityButtonRole},
171 {ax::mojom::Role::kPluginObject, NSAccessibilityGroupRole},
172 {ax::mojom::Role::kPopUpButton, NSAccessibilityPopUpButtonRole},
173 {ax::mojom::Role::kPortal, NSAccessibilityButtonRole},
174 {ax::mojom::Role::kPre, NSAccessibilityGroupRole},
175 {ax::mojom::Role::kPresentational, NSAccessibilityGroupRole},
176 {ax::mojom::Role::kProgressIndicator, NSAccessibilityProgressIndicatorRole},
177 {ax::mojom::Role::kRadioButton, NSAccessibilityRadioButtonRole},
178 {ax::mojom::Role::kRadioGroup, NSAccessibilityRadioGroupRole},
179 {ax::mojom::Role::kRegion, NSAccessibilityGroupRole},
180 {ax::mojom::Role::kRootWebArea, @"AXWebArea"},
181 {ax::mojom::Role::kRow, NSAccessibilityRowRole},
182 {ax::mojom::Role::kRowGroup, NSAccessibilityGroupRole},
183 {ax::mojom::Role::kRowHeader, @"AXCell"},
184 // TODO(accessibility) What should kRuby be? It's not listed? Any others
185 // missing? Maybe use switch statement so that compiler doesn't allow us
186 // to miss any.
187 {ax::mojom::Role::kRubyAnnotation, NSAccessibilityUnknownRole},
188 {ax::mojom::Role::kScrollBar, NSAccessibilityScrollBarRole},
189 {ax::mojom::Role::kSearch, NSAccessibilityGroupRole},
190 {ax::mojom::Role::kSearchBox, NSAccessibilityTextFieldRole},
191 {ax::mojom::Role::kSection, NSAccessibilityGroupRole},
192 {ax::mojom::Role::kSlider, NSAccessibilitySliderRole},
193 {ax::mojom::Role::kSliderThumb, NSAccessibilityValueIndicatorRole},
194 {ax::mojom::Role::kSpinButton, NSAccessibilityIncrementorRole},
195 {ax::mojom::Role::kSplitter, NSAccessibilitySplitterRole},
196 {ax::mojom::Role::kStaticText, NSAccessibilityStaticTextRole},
197 {ax::mojom::Role::kStatus, NSAccessibilityGroupRole},
198 {ax::mojom::Role::kSuggestion, NSAccessibilityGroupRole},
199 {ax::mojom::Role::kSvgRoot, NSAccessibilityGroupRole},
200 {ax::mojom::Role::kSwitch, NSAccessibilityCheckBoxRole},
201 {ax::mojom::Role::kStrong, NSAccessibilityGroupRole},
202 {ax::mojom::Role::kTab, NSAccessibilityRadioButtonRole},
203 {ax::mojom::Role::kTable, NSAccessibilityTableRole},
204 {ax::mojom::Role::kTableHeaderContainer, NSAccessibilityGroupRole},
205 {ax::mojom::Role::kTabList, NSAccessibilityTabGroupRole},
206 {ax::mojom::Role::kTabPanel, NSAccessibilityGroupRole},
207 {ax::mojom::Role::kTerm, NSAccessibilityGroupRole},
208 {ax::mojom::Role::kTextField, NSAccessibilityTextFieldRole},
209 {ax::mojom::Role::kTextFieldWithComboBox, NSAccessibilityComboBoxRole},
210 {ax::mojom::Role::kTime, NSAccessibilityGroupRole},
211 {ax::mojom::Role::kTimer, NSAccessibilityGroupRole},
212 {ax::mojom::Role::kTitleBar, NSAccessibilityStaticTextRole},
213 {ax::mojom::Role::kToggleButton, NSAccessibilityCheckBoxRole},
214 {ax::mojom::Role::kToolbar, NSAccessibilityToolbarRole},
215 {ax::mojom::Role::kTooltip, NSAccessibilityGroupRole},
216 {ax::mojom::Role::kTree, NSAccessibilityOutlineRole},
217 {ax::mojom::Role::kTreeGrid, NSAccessibilityTableRole},
218 {ax::mojom::Role::kTreeItem, NSAccessibilityRowRole},
219 {ax::mojom::Role::kVideo, NSAccessibilityGroupRole},
220 {ax::mojom::Role::kWebArea, @"AXWebArea"},
221 // Use the group role as the BrowserNativeWidgetWindow already provides
222 // a kWindow role, and having extra window roles, which are treated
223 // specially by screen readers, can break their ability to find the
224 // content window. See http://crbug.com/875843 for more information.
225 {ax::mojom::Role::kWindow, NSAccessibilityGroupRole},
226 };
227
228 return RoleMap(begin(roles), end(roles));
229}
230
231RoleMap BuildSubroleMap() {
232 const RoleMap::value_type subroles[] = {
233 {ax::mojom::Role::kAlert, @"AXApplicationAlert"},
234 {ax::mojom::Role::kAlertDialog, @"AXApplicationAlertDialog"},
235 {ax::mojom::Role::kApplication, @"AXLandmarkApplication"},
236 {ax::mojom::Role::kArticle, @"AXDocumentArticle"},
237 {ax::mojom::Role::kBanner, @"AXLandmarkBanner"},
238 {ax::mojom::Role::kCode, @"AXCodeStyleGroup"},
239 {ax::mojom::Role::kComplementary, @"AXLandmarkComplementary"},
240 {ax::mojom::Role::kContentDeletion, @"AXDeleteStyleGroup"},
241 {ax::mojom::Role::kContentInsertion, @"AXInsertStyleGroup"},
242 {ax::mojom::Role::kContentInfo, @"AXLandmarkContentInfo"},
243 {ax::mojom::Role::kDefinition, @"AXDefinition"},
246 {ax::mojom::Role::kDialog, @"AXApplicationDialog"},
247 {ax::mojom::Role::kDocument, @"AXDocument"},
248 {ax::mojom::Role::kEmphasis, @"AXEmphasisStyleGroup"},
249 {ax::mojom::Role::kFooter, @"AXLandmarkContentInfo"},
250 {ax::mojom::Role::kForm, @"AXLandmarkForm"},
251 {ax::mojom::Role::kGraphicsDocument, @"AXDocument"},
252 {ax::mojom::Role::kHeader, @"AXLandmarkBanner"},
253 {ax::mojom::Role::kLog, @"AXApplicationLog"},
254 {ax::mojom::Role::kMain, @"AXLandmarkMain"},
255 {ax::mojom::Role::kMarquee, @"AXApplicationMarquee"},
256 {ax::mojom::Role::kMath, @"AXDocumentMath"},
257 {ax::mojom::Role::kNavigation, @"AXLandmarkNavigation"},
258 {ax::mojom::Role::kNote, @"AXDocumentNote"},
259 {ax::mojom::Role::kRegion, @"AXLandmarkRegion"},
260 {ax::mojom::Role::kSearch, @"AXLandmarkSearch"},
261 {ax::mojom::Role::kSearchBox, @"AXSearchField"},
262 {ax::mojom::Role::kSection, @"AXLandmarkRegion"},
263 {ax::mojom::Role::kStatus, @"AXApplicationStatus"},
264 {ax::mojom::Role::kStrong, @"AXStrongStyleGroup"},
265 {ax::mojom::Role::kSwitch, @"AXSwitch"},
266 {ax::mojom::Role::kTabPanel, @"AXTabPanel"},
267 {ax::mojom::Role::kTerm, @"AXTerm"},
268 {ax::mojom::Role::kTime, @"AXTimeGroup"},
269 {ax::mojom::Role::kTimer, @"AXApplicationTimer"},
270 {ax::mojom::Role::kToggleButton, @"AXToggleButton"},
271 {ax::mojom::Role::kTooltip, @"AXUserInterfaceTooltip"},
272 {ax::mojom::Role::kTreeItem, NSAccessibilityOutlineRowSubrole},
273 };
274
275 return RoleMap(begin(subroles), end(subroles));
276}
277
278EventMap BuildEventMap() {
279 const EventMap::value_type events[] = {
280 {ax::mojom::Event::kCheckedStateChanged, NSAccessibilityValueChangedNotification},
281 {ax::mojom::Event::kFocus, NSAccessibilityFocusedUIElementChangedNotification},
282 {ax::mojom::Event::kFocusContext, NSAccessibilityFocusedUIElementChangedNotification},
283 {ax::mojom::Event::kTextChanged, NSAccessibilityTitleChangedNotification},
284 {ax::mojom::Event::kValueChanged, NSAccessibilityValueChangedNotification},
285 {ax::mojom::Event::kTextSelectionChanged, NSAccessibilitySelectedTextChangedNotification},
286 // TODO(patricialor): Add more events.
287 };
288
289 return EventMap(begin(events), end(events));
290}
291
292ActionList BuildActionList() {
293 const ActionList::value_type entries[] = {
294 // NSAccessibilityPressAction must come first in this list.
295 {ax::mojom::Action::kDoDefault, NSAccessibilityPressAction},
296 {ax::mojom::Action::kScrollToMakeVisible, NSAccessibilityScrollToVisibleAction},
297 {ax::mojom::Action::kDecrement, NSAccessibilityDecrementAction},
298 {ax::mojom::Action::kIncrement, NSAccessibilityIncrementAction},
299 {ax::mojom::Action::kShowContextMenu, NSAccessibilityShowMenuAction},
300 };
301 return ActionList(begin(entries), end(entries));
302}
303
304const ActionList& GetActionList() {
305 static const base::NoDestructor<ActionList> action_map(BuildActionList());
306 return *action_map;
307}
308
309void PostAnnouncementNotification(NSString* announcement, NSWindow* window, bool is_polite) {
310 NSAccessibilityPriorityLevel priority =
311 is_polite ? NSAccessibilityPriorityMedium : NSAccessibilityPriorityHigh;
312 NSDictionary* notification_info =
313 @{NSAccessibilityAnnouncementKey : announcement,
314 NSAccessibilityPriorityKey : @(priority)};
315 // On Mojave, announcements from an inactive window aren't spoken.
316 NSAccessibilityPostNotificationWithUserInfo(
317 window, NSAccessibilityAnnouncementRequestedNotification, notification_info);
318}
320 NSString* notification = [AXPlatformNodeCocoa nativeNotificationFromAXEvent:event_type];
321 if (notification)
322 NSAccessibilityPostNotification(target, notification);
323}
324
325// Returns true if |action| should be added implicitly for |data|.
326bool HasImplicitAction(const ui::AXNodeData& data, ax::mojom::Action action) {
327 return action == ax::mojom::Action::kDoDefault && data.IsClickable();
328}
329
330// For roles that show a menu for the default action, ensure "show menu" also
331// appears in available actions, but only if that's not already used for a
332// context menu. It will be mapped back to the default action when performed.
333bool AlsoUseShowMenuActionForDefaultAction(const ui::AXNodeData& data) {
334 return HasImplicitAction(data, ax::mojom::Action::kDoDefault) &&
337}
338
339} // namespace
340
341@interface AXPlatformNodeCocoa (Private)
342// Helper function for string attributes that don't require extra processing.
343- (NSString*)getStringAttribute:(ax::mojom::StringAttribute)attribute;
344// Returns AXValue, or nil if AXValue isn't an NSString.
345- (NSString*)getAXValueAsString;
346// Returns the data necessary to queue an NSAccessibility announcement if
347// |eventType| should be announced, or nullptr otherwise.
348- (std::unique_ptr<AnnouncementSpec>)announcementForEvent:(ax::mojom::Event)eventType;
349// Ask the system to announce |announcementText|. This is debounced to happen
350// at most every |kLiveRegionDebounceMillis| per node, with only the most
351// recent announcement text read, to account for situations with multiple
352// notifications happening one after another (for example, results for
353// find-in-page updating rapidly as they come in from subframes).
354- (void)scheduleLiveRegionAnnouncement:(std::unique_ptr<AnnouncementSpec>)announcement;
355@end
356
357@implementation AXPlatformNodeCocoa {
358 ui::AXPlatformNodeBase* _node; // Weak. Retains us.
359 std::unique_ptr<AnnouncementSpec> _pendingAnnouncement;
360}
361
362@synthesize node = _node;
363
364+ (NSString*)nativeRoleFromAXRole:(ax::mojom::Role)role {
365 static const base::NoDestructor<RoleMap> role_map(BuildRoleMap());
366 RoleMap::const_iterator it = role_map->find(role);
367 return it != role_map->end() ? it->second : NSAccessibilityUnknownRole;
368}
369
370+ (NSString*)nativeSubroleFromAXRole:(ax::mojom::Role)role {
371 static const base::NoDestructor<RoleMap> subrole_map(BuildSubroleMap());
372 RoleMap::const_iterator it = subrole_map->find(role);
373 return it != subrole_map->end() ? it->second : nil;
374}
375
376+ (NSString*)nativeNotificationFromAXEvent:(ax::mojom::Event)event {
377 static const base::NoDestructor<EventMap> event_map(BuildEventMap());
378 EventMap::const_iterator it = event_map->find(event);
379 return it != event_map->end() ? it->second : nil;
380}
381
382- (instancetype)initWithNode:(ui::AXPlatformNodeBase*)node {
383 if ((self = [super init])) {
384 _node = node;
385 }
386 return self;
387}
388
389- (void)detach {
390 if (!_node)
391 return;
392 _node = nil;
393 NSAccessibilityPostNotification(self, NSAccessibilityUIElementDestroyedNotification);
394}
395
396- (NSRect)boundsInScreen {
397 if (!_node || !_node->GetDelegate())
398 return NSZeroRect;
399 return gfx::ScreenRectToNSRect(_node->GetDelegate()->GetBoundsRect(
401}
402
403- (NSString*)getStringAttribute:(ax::mojom::StringAttribute)attribute {
404 std::string attributeValue;
405 if (_node->GetStringAttribute(attribute, &attributeValue))
406 return @(attributeValue.data());
407 return nil;
408}
409
410- (NSString*)getAXValueAsString {
411 id value = [self AXValueInternal];
412 return [value isKindOfClass:[NSString class]] ? value : nil;
413}
414
415- (NSString*)getName {
416 return @(_node->GetName().data());
417}
418
419- (std::unique_ptr<AnnouncementSpec>)announcementForEvent:(ax::mojom::Event)eventType {
420 // Only alerts and live region changes should be announced.
421 BASE_DCHECK(eventType == ax::mojom::Event::kAlert ||
422 eventType == ax::mojom::Event::kLiveRegionChanged);
423 std::string liveStatus = _node->GetStringAttribute(ax::mojom::StringAttribute::kLiveStatus);
424 // If live status is explicitly set to off, don't announce.
425 if (liveStatus == "off")
426 return nullptr;
427
428 NSString* name = [self getName];
429 NSString* announcementText = name;
430 if ([announcementText length] <= 0) {
431 announcementText = @(base::UTF16ToUTF8(_node->GetInnerText()).data());
432 }
433 if ([announcementText length] == 0)
434 return nullptr;
435
436 auto announcement = std::make_unique<AnnouncementSpec>();
437 announcement->announcement = base::scoped_nsobject<NSString>([announcementText retain]);
438 announcement->window = base::scoped_nsobject<NSWindow>([[self AXWindowInternal] retain]);
439 announcement->is_polite = liveStatus != "assertive";
440 return announcement;
441}
442
443- (void)scheduleLiveRegionAnnouncement:(std::unique_ptr<AnnouncementSpec>)announcement {
445 // An announcement is already in flight, so just reset the contents. This is
446 // threadsafe because the dispatch is on the main queue.
447 _pendingAnnouncement = std::move(announcement);
448 return;
449 }
450
451 _pendingAnnouncement = std::move(announcement);
452 dispatch_after(kLiveRegionDebounceMillis * NSEC_PER_MSEC, dispatch_get_main_queue(), ^{
454 return;
455 }
456 PostAnnouncementNotification(_pendingAnnouncement->announcement, _pendingAnnouncement->window,
457 _pendingAnnouncement->is_polite);
458 _pendingAnnouncement.reset();
459 });
460}
461// NSAccessibility informal protocol implementation.
462
463- (BOOL)accessibilityIsIgnored {
464 if (!_node)
465 return YES;
466
467 return [[self AXRoleInternal] isEqualToString:NSAccessibilityUnknownRole] ||
468 _node->GetData().HasState(ax::mojom::State::kInvisible);
469}
470
471- (id)accessibilityHitTest:(NSPoint)point {
472 if (!NSPointInRect(point, [self boundsInScreen]))
473 return nil;
474
475 for (id child in [[self AXChildrenInternal] reverseObjectEnumerator]) {
476 if (!NSPointInRect(point, [child accessibilityFrame]))
477 continue;
478 if (id foundChild = [child accessibilityHitTest:point])
479 return foundChild;
480 }
481
482 // Hit self, but not any child.
483 return NSAccessibilityUnignoredAncestor(self);
484}
485
486- (BOOL)accessibilityNotifiesWhenDestroyed {
487 return YES;
488}
489
490- (id)accessibilityFocusedUIElement {
491 return _node ? _node->GetDelegate()->GetFocus() : nil;
492}
493
494// This function and accessibilityPerformAction:, while deprecated, are a) still
495// called by AppKit internally and b) not implemented by NSAccessibilityElement,
496// so this class needs its own implementations.
497- (NSArray*)accessibilityActionNames {
498 if (!_node)
499 return @[];
500
501 base::scoped_nsobject<NSMutableArray> axActions([[NSMutableArray alloc] init]);
502
503 const ui::AXNodeData& data = _node->GetData();
504 const ActionList& action_list = GetActionList();
505
506 // VoiceOver expects the "press" action to be first. Note that some roles
507 // should be given a press action implicitly.
508 BASE_DCHECK([action_list[0].second isEqualToString:NSAccessibilityPressAction]);
509 for (const auto& item : action_list) {
510 if (data.HasAction(item.first) || HasImplicitAction(data, item.first))
511 [axActions addObject:item.second];
512 }
513
514 if (AlsoUseShowMenuActionForDefaultAction(data))
515 [axActions addObject:NSAccessibilityShowMenuAction];
516
517 return axActions.autorelease();
518}
519
520- (void)accessibilityPerformAction:(NSString*)action {
521 // Actions are performed asynchronously, so it's always possible for an object
522 // to change its mind after previously reporting an action as available.
523 if (![[self accessibilityActionNames] containsObject:action])
524 return;
525
527 if ([action isEqualToString:NSAccessibilityShowMenuAction] &&
528 AlsoUseShowMenuActionForDefaultAction(_node->GetData())) {
530 } else {
531 for (const ActionList::value_type& entry : GetActionList()) {
532 if ([action isEqualToString:entry.second]) {
533 data.action = entry.first;
534 break;
535 }
536 }
537 }
538
539 // Note ui::AX_ACTIONs which are just overwriting an accessibility attribute
540 // are already implemented in -accessibilitySetValue:forAttribute:, so ignore
541 // those here.
542
543 if (data.action != ax::mojom::Action::kNone)
544 _node->GetDelegate()->AccessibilityPerformAction(data);
545}
546
547- (NSString*)AXRoleInternal {
548 if (!_node)
549 return nil;
550
551 return [[self class] nativeRoleFromAXRole:_node->GetData().role];
552}
553
554- (NSString*)AXRoleDescriptionInternal {
555 switch (_node->GetData().role) {
557 // There is no NSAccessibilityTabRole or similar (AXRadioButton is used
558 // instead). Do the same as NSTabView and put "tab" in the description.
559 // return [l10n_util::GetNSStringWithFixup(IDS_ACCNAME_TAB_ROLE_DESCRIPTION)
560 // lowercaseString];
561 return nil;
563 // return [l10n_util::GetNSStringWithFixup(
564 // IDS_ACCNAME_DISCLOSURE_TRIANGLE_ROLE_DESCRIPTION) lowercaseString];
565 return nil;
566 default:
567 break;
568 }
569 return NSAccessibilityRoleDescription([self AXRoleInternal], [self AXSubroleInternal]);
570}
571
572- (NSString*)AXSubroleInternal {
573 ax::mojom::Role role = _node->GetData().role;
574 switch (role) {
576 if (_node->GetData().HasState(ax::mojom::State::kProtected))
577 return NSAccessibilitySecureTextFieldSubrole;
578 break;
579 default:
580 break;
581 }
583}
584
585- (NSString*)AXHelpInternal {
586 // TODO(aleventhal) Key shortcuts attribute should eventually get
587 // its own field. Follow what WebKit does for aria-keyshortcuts, see
588 // https://bugs.webkit.org/show_bug.cgi?id=159215 (WebKit bug).
589 NSString* desc = [self getStringAttribute:ax::mojom::StringAttribute::kDescription];
590 NSString* key = [self getStringAttribute:ax::mojom::StringAttribute::kKeyShortcuts];
591 if (!desc.length)
592 return key.length ? key : @"";
593 if (!key.length)
594 return desc;
595 return [NSString stringWithFormat:@"%@ %@", desc, key];
596}
597
598- (id)AXValueInternal {
599 ax::mojom::Role role = _node->GetData().role;
600 if (role == ax::mojom::Role::kTab)
601 return [self AXSelectedInternal];
602
604 if (role == ax::mojom::Role::kStaticText) {
605 // Static texts may store their texts in the value attributes. For
606 // example, the selectable text stores its text in value instead of
607 // name.
608 NSString* value = [self getName];
609 if (value.length == 0) {
610 value = [self getStringAttribute:ax::mojom::StringAttribute::kValue];
611 }
612 return value;
613 }
614 return [self getName];
615 }
616
617 if (_node->IsPlatformCheckable()) {
618 // Mixed checkbox state not currently supported in views, but could be.
619 // See browser_accessibility_cocoa.mm for details.
620 const auto checkedState = static_cast<ax::mojom::CheckedState>(
621 _node->GetIntAttribute(ax::mojom::IntAttribute::kCheckedState));
622 return checkedState == ax::mojom::CheckedState::kTrue ? @1 : @0;
623 }
624 return [self getStringAttribute:ax::mojom::StringAttribute::kValue];
625}
626
627- (NSNumber*)AXEnabledInternal {
628 return @(_node->GetData().GetRestriction() != ax::mojom::Restriction::kDisabled);
629}
630
631- (NSNumber*)AXFocusedInternal {
632 if (_node->GetData().HasState(ax::mojom::State::kFocusable))
633 return @(_node->GetDelegate()->GetFocus() == _node->GetNativeViewAccessible());
634 return @NO;
635}
636
637- (id)AXParentInternal {
638 if (!_node)
639 return nil;
640 return NSAccessibilityUnignoredAncestor(_node->GetParent());
641}
642
643- (NSArray*)AXChildrenInternal {
644 if (!_node)
645 return @[];
646
647 int count = _node->GetChildCount();
648 NSMutableArray* children = [NSMutableArray arrayWithCapacity:count];
649 for (auto child_iterator_ptr = _node->GetDelegate()->ChildrenBegin();
650 *child_iterator_ptr != *_node->GetDelegate()->ChildrenEnd(); ++(*child_iterator_ptr)) {
651 [children addObject:child_iterator_ptr->GetNativeViewAccessible()];
652 }
653 return NSAccessibilityUnignoredChildren(children);
654}
655
656- (id)AXWindowInternal {
657 return _node->GetDelegate()->GetNSWindow();
658}
659
660- (id)AXTopLevelUIElementInternal {
661 return [self AXWindowInternal];
662}
663
664- (NSValue*)AXPositionInternal {
665 return [NSValue valueWithPoint:self.boundsInScreen.origin];
666}
667
668- (NSValue*)AXSizeInternal {
669 return [NSValue valueWithSize:self.boundsInScreen.size];
670}
671
672- (NSString*)AXTitleInternal {
673 if (ui::IsNameExposedInAXValueForRole(_node->GetData().role))
674 return @"";
675
676 return [self getName];
677}
678
679- (NSNumber*)AXSelectedInternal {
680 return @(_node->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected));
681}
682
683- (NSString*)AXPlaceholderValueInternal {
684 return [self getStringAttribute:ax::mojom::StringAttribute::kPlaceholder];
685}
686
687- (NSString*)AXMenuItemMarkChar {
688 if (!ui::IsMenuItem(_node->GetData().role))
689 return nil;
690
691 const auto checkedState = static_cast<ax::mojom::CheckedState>(
692 _node->GetIntAttribute(ax::mojom::IntAttribute::kCheckedState));
693 if (checkedState == ax::mojom::CheckedState::kTrue) {
694 return @"\xE2\x9C\x93"; // UTF-8 for unicode 0x2713, "check mark"
695 }
696
697 return @"";
698}
699
700- (NSString*)AXSelectedTextInternal {
701 NSRange selectedTextRange;
702 [[self AXSelectedTextRangeInternal] getValue:&selectedTextRange];
703 return [[self getAXValueAsString] substringWithRange:selectedTextRange];
704}
705
706- (NSValue*)AXSelectedTextRangeInternal {
707 int start = _node->GetIntAttribute(ax::mojom::IntAttribute::kTextSelStart);
708 int end = _node->GetIntAttribute(ax::mojom::IntAttribute::kTextSelEnd);
709 NSAssert((start >= 0 && end >= 0) || (start == -1 && end == -1), @"selection is invalid");
710
711 if (start == -1 && end == -1) {
712 return [NSValue valueWithRange:{NSNotFound, 0}];
713 }
714 // NSRange cannot represent the direction the text was selected in.
715 return [NSValue valueWithRange:{static_cast<NSUInteger>(std::min(start, end)),
716 static_cast<NSUInteger>(abs(end - start))}];
717}
718
719- (NSNumber*)AXNumberOfCharactersInternal {
720 return @([[self getAXValueAsString] length]);
721}
722
723- (NSValue*)AXVisibleCharacterRangeInternal {
724 return [NSValue valueWithRange:{0, [[self getAXValueAsString] length]}];
725}
726
727- (NSNumber*)AXInsertionPointLineNumberInternal {
728 // Multiline is not supported on views.
729 return @0;
730}
731
732// Method based accessibility APIs.
733
734- (NSString*)description {
735 return [NSString stringWithFormat:@"%@ - %@ (%@)", [super description], [self AXTitleInternal],
736 [self AXRoleInternal]];
737}
738
739// The methods below implement the NSAccessibility protocol. These methods
740// appear to be the minimum needed to avoid AppKit refusing to handle the
741// element or crashing internally. Most of the remaining old API methods (the
742// ones from NSObject) are implemented in terms of the new NSAccessibility
743// methods.
744//
745// TODO(https://crbug.com/386671): Does this class need to implement the various
746// accessibilityPerformFoo methods, or are the stub implementations from
747// NSAccessibilityElement sufficient?
748- (NSArray*)accessibilityChildren {
749 return [self AXChildrenInternal];
750}
751
752- (BOOL)isAccessibilityElement {
753 if (!_node)
754 return NO;
755 return (![[self AXRoleInternal] isEqualToString:NSAccessibilityUnknownRole] &&
756 !_node->GetData().HasState(ax::mojom::State::kInvisible));
757}
758- (BOOL)isAccessibilityEnabled {
759 return [[self AXEnabledInternal] boolValue];
760}
761- (NSRect)accessibilityFrame {
762 return [self boundsInScreen];
763}
764
765- (NSString*)accessibilityLabel {
766 // accessibilityLabel is "a short description of the accessibility element",
767 // and accessibilityTitle is "the title of the accessibility element"; at
768 // least in Chromium, the title usually is a short description of the element,
769 // so it also functions as a label.
770 return [self AXTitleInternal];
771}
772
773- (NSString*)accessibilityTitle {
774 return [self AXTitleInternal];
775}
776
777- (id)accessibilityValue {
778 return [self AXValueInternal];
779}
780
781- (NSAccessibilityRole)accessibilityRole {
782 return [self AXRoleInternal];
783}
784
785- (NSString*)accessibilityRoleDescription {
786 return [self AXRoleDescriptionInternal];
787}
788
789- (NSAccessibilitySubrole)accessibilitySubrole {
790 return [self AXSubroleInternal];
791}
792
793- (NSString*)accessibilityHelp {
794 return [self AXHelpInternal];
795}
796
797- (id)accessibilityParent {
798 return [self AXParentInternal];
799}
800
801- (id)accessibilityWindow {
802 return [self AXWindowInternal];
803}
804
805- (id)accessibilityTopLevelUIElement {
806 return [self AXTopLevelUIElementInternal];
807}
808
809- (BOOL)accessibilitySelected {
810 return [[self AXSelectedInternal] boolValue];
811}
812
813- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector {
814 if (!_node)
815 return NO;
816
817 const ax::mojom::Restriction restriction = _node->GetData().GetRestriction();
818 if (restriction == ax::mojom::Restriction::kDisabled)
819 return NO;
820
821 if (selector == @selector(setAccessibilityValue:)) {
822 // Tabs use the radio button role on Mac, so they are selected by calling
823 // setSelected on an individual tab, rather than by setting the selected
824 // element on the tabstrip as a whole.
825 if (_node->GetData().role == ax::mojom::Role::kTab) {
826 return !_node->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected);
827 }
828 return restriction != ax::mojom::Restriction::kReadOnly;
829 }
830
831 // TODO(https://crbug.com/692362): Once the underlying bug in
832 // views::Textfield::SetSelectionRange() described in that bug is fixed,
833 // remove the check here; right now, this check serves to prevent
834 // accessibility clients from trying to set the selection range, which won't
835 // work because of 692362.
836 if (selector == @selector(setAccessibilitySelectedText:) ||
837 selector == @selector(setAccessibilitySelectedTextRange:) ||
838 selector == @selector(setAccessibilitySelectedTextMarkerRange:)) {
839 return restriction != ax::mojom::Restriction::kReadOnly;
840 }
841
842 if (selector == @selector(setAccessibilityFocused:))
843 return _node->GetData().HasState(ax::mojom::State::kFocusable);
844
845 // TODO(https://crbug.com/386671): What about role-specific selectors?
846 return [super isAccessibilitySelectorAllowed:selector];
847}
848
849- (void)setAccessibilityValue:(id)value {
850 if (!_node)
851 return;
852
854 data.action = _node->GetData().role == ax::mojom::Role::kTab ? ax::mojom::Action::kSetSelection
856 if ([value isKindOfClass:[NSString class]]) {
857 data.value = std::string([value UTF8String]);
858 } else if ([value isKindOfClass:[NSValue class]]) {
859 // TODO(https://crbug.com/386671): Is this case actually needed? The
860 // NSObject accessibility implementation supported this, but can it actually
861 // occur?
862 NSRange range = [value rangeValue];
863 data.anchor_offset = range.location;
864 data.focus_offset = NSMaxRange(range);
865 }
866 _node->GetDelegate()->AccessibilityPerformAction(data);
867}
868
869- (void)setAccessibilityFocused:(BOOL)isFocused {
870 if (!_node)
871 return;
874 _node->GetDelegate()->AccessibilityPerformAction(data);
875}
876
877- (void)setAccessibilitySelectedText:(NSString*)text {
878 if (!_node)
879 return;
882 data.value = std::string([text UTF8String]);
883
884 _node->GetDelegate()->AccessibilityPerformAction(data);
885}
886
887- (void)setAccessibilitySelectedTextRange:(NSRange)range {
888 if (!_node)
889 return;
892 data.anchor_offset = range.location;
893 data.focus_offset = NSMaxRange(range);
894 _node->GetDelegate()->AccessibilityPerformAction(data);
895}
896
897// "Configuring Text Elements" section of the NSAccessibility formal protocol.
898// These are all "required" methods, although in practice the ones that are left
899// BASE_UNREACHABLE() seem to not be called anywhere (and were BASE_DCHECK false in
900// the old API as well).
901
902- (NSInteger)accessibilityInsertionPointLineNumber {
903 return [[self AXInsertionPointLineNumberInternal] integerValue];
904}
905
906- (NSInteger)accessibilityNumberOfCharacters {
907 if (!_node)
908 return 0;
909 return [[self AXNumberOfCharactersInternal] integerValue];
910}
911
912- (NSString*)accessibilityPlaceholderValue {
913 if (!_node)
914 return nil;
915
916 return [self AXPlaceholderValueInternal];
917}
918
919- (NSString*)accessibilitySelectedText {
920 if (!_node)
921 return nil;
922
923 return [self AXSelectedTextInternal];
924}
925
926- (NSRange)accessibilitySelectedTextRange {
927 if (!_node)
928 return NSMakeRange(0, 0);
929
930 NSRange r;
931 [[self AXSelectedTextRangeInternal] getValue:&r];
932 return r;
933}
934
935- (NSArray*)accessibilitySelectedTextRanges {
936 if (!_node)
937 return nil;
938
939 return @[ [self AXSelectedTextRangeInternal] ];
940}
941
942- (NSRange)accessibilitySharedCharacterRange {
943 if (!_node)
944 return NSMakeRange(0, 0);
945
946 NSRange r;
947 [[self AXSelectedTextRangeInternal] getValue:&r];
948 return r;
949}
950
951- (NSArray*)accessibilitySharedTextUIElements {
952 if (!_node)
953 return nil;
954
955 return @[ self ];
956}
957
958- (NSRange)accessibilityVisibleCharacterRange {
959 if (!_node)
960 return NSMakeRange(0, 0);
961
962 return [[self AXVisibleCharacterRangeInternal] rangeValue];
963}
964
965- (NSString*)accessibilityStringForRange:(NSRange)range {
966 if (!_node)
967 return nil;
968
969 return [[self getAXValueAsString] substringWithRange:range];
970}
971
972- (NSAttributedString*)accessibilityAttributedStringForRange:(NSRange)range {
973 if (!_node)
974 return nil;
975 // TODO(https://crbug.com/958811): Implement this for real.
977 [[NSAttributedString alloc] initWithString:[self accessibilityStringForRange:range]]);
978 return attributedString.autorelease();
979}
980
981- (NSData*)accessibilityRTFForRange:(NSRange)range {
982 return nil;
983}
984
985- (NSRect)accessibilityFrameForRange:(NSRange)range {
986 return NSZeroRect;
987}
988
989- (NSInteger)accessibilityLineForIndex:(NSInteger)index {
990 // Views textfields are single-line.
991 return 0;
992}
993
994- (NSRange)accessibilityRangeForIndex:(NSInteger)index {
996 return NSMakeRange(0, 0);
997}
998
999- (NSRange)accessibilityStyleRangeForIndex:(NSInteger)index {
1000 if (!_node)
1001 return NSMakeRange(0, 0);
1002
1003 // TODO(https://crbug.com/958811): Implement this for real.
1004 return NSMakeRange(0, [self accessibilityNumberOfCharacters]);
1005}
1006
1007- (NSRange)accessibilityRangeForLine:(NSInteger)line {
1008 if (!_node)
1009 return NSMakeRange(0, 0);
1010
1011 if (line != 0) {
1012 BASE_LOG() << "Views textfields are single-line.";
1014 }
1015 return NSMakeRange(0, [self accessibilityNumberOfCharacters]);
1016}
1017
1018- (NSRange)accessibilityRangeForPosition:(NSPoint)point {
1019 // TODO(a-wallen): Framework needs to send Text Metrics
1020 // to the AXTree in order for a NSPoint (x, y) to be
1021 // translated to the appropriate range of UTF-16 chars.
1022 return NSMakeRange(0, 0);
1023}
1024
1025// "Setting the Focus" section of the NSAccessibility formal protocol.
1026// These are all "required" methods.
1027
1028- (NSArray*)accessibilitySharedFocusElements {
1029 if (![[self AXFocusedInternal] boolValue])
1030 return nil;
1031 return @[ self ];
1032}
1033- (id)accessibilityFocusedWindow {
1034 if (![[self AXFocusedInternal] boolValue])
1035 return nil;
1036 return self;
1037}
1038- (id)accessibilityApplicationFocusedUIElement {
1039 if (![[self AXFocusedInternal] boolValue])
1040 return nil;
1041 return self;
1042}
1043- (BOOL)isAccessibilityFocused {
1044 return [[self AXFocusedInternal] boolValue];
1045}
1046
1047@end
1048
1049namespace ui {
1050
1051// static
1054 node->Init(delegate);
1055 return node;
1056}
1057
1058// static
1060 if ([accessible isKindOfClass:[AXPlatformNodeCocoa class]])
1061 return [accessible node];
1062 return nullptr;
1063}
1064
1066
1068
1070 if (native_node_)
1071 [native_node_ detach];
1073}
1074
1075// On Mac, the checked state is mapped to AXValue.
1077 if (GetData().role == ax::mojom::Role::kTab) {
1078 // On Mac, tabs are exposed as radio buttons, and are treated as checkable.
1079 // Also, the internal State::kSelected is be mapped to checked via AXValue.
1080 return true;
1081 }
1082
1084}
1085
1087 if (!native_node_)
1088 native_node_.reset([[AXPlatformNodeCocoa alloc] initWithNode:this]);
1089 return native_node_.get();
1090}
1091
1095 // Handle special cases.
1096
1097 // Alerts and live regions go through the announcement API instead of the
1098 // regular NSAccessibility notification system.
1099 if (event_type == ax::mojom::Event::kAlert ||
1100 event_type == ax::mojom::Event::kLiveRegionChanged) {
1101 if (auto announcement = [native_node_ announcementForEvent:event_type]) {
1102 [native_node_ scheduleLiveRegionAnnouncement:std::move(announcement)];
1103 }
1104 return;
1105 }
1106 if (event_type == ax::mojom::Event::kSelection) {
1107 ax::mojom::Role role = GetData().role;
1108 if (ui::IsMenuItem(role)) {
1109 // On Mac, map menu item selection to a focus event.
1110 NotifyMacEvent(native_node_, ax::mojom::Event::kFocus);
1111 return;
1112 } else if (ui::IsListItem(role)) {
1113 if (AXPlatformNodeBase* container = GetSelectionContainer()) {
1114 const ui::AXNodeData& data = container->GetData();
1115 if (data.role == ax::mojom::Role::kListBox &&
1116 !data.HasState(ax::mojom::State::kMultiselectable) &&
1118 NotifyMacEvent(native_node_, ax::mojom::Event::kFocus);
1119 return;
1120 }
1121 }
1122 }
1123 }
1124 // Otherwise, use mappings between ax::mojom::Event and NSAccessibility
1125 // notifications from the EventMap above.
1126 NotifyMacEvent(native_node_, event_type);
1127}
1128
1129void AXPlatformNodeMac::AnnounceText(const std::u16string& text) {
1130 PostAnnouncementNotification(@(base::UTF16ToUTF8(text).data()), [native_node_ AXWindowInternal],
1131 false);
1132}
1133
1135 switch (role) {
1141 return true;
1142 default:
1143 return false;
1144 }
1145}
1146
1148 const char* value,
1149 PlatformAttributeList* attributes) {
1151}
1152
1153} // namespace ui
int count
ax::mojom::Event event_type
std::unique_ptr< AnnouncementSpec > _pendingAnnouncement
void reset(NST object=nil)
AXPlatformNodeBase * GetSelectionContainer() const
void NotifyAccessibilityEvent(ax::mojom::Event event_type) override
AXPlatformNodeDelegate * GetDelegate() const override
gfx::NativeViewAccessible GetFocus()
virtual bool IsPlatformCheckable() const
virtual void Init(AXPlatformNodeDelegate *delegate)
std::vector< std::u16string > PlatformAttributeList
const AXNodeData & GetData() const
gfx::NativeViewAccessible GetNativeViewAccessible() override
void NotifyAccessibilityEvent(ax::mojom::Event event_type) override
bool IsPlatformCheckable() const override
void AnnounceText(const std::u16string &text) override
void AddAttributeToList(const char *name, const char *value, PlatformAttributeList *attributes) override
static AXPlatformNode * FromNativeViewAccessible(gfx::NativeViewAccessible accessible)
static AXPlatformNode * Create(AXPlatformNodeDelegate *delegate)
static const char * begin(const StringSlice &s)
Definition editor.cpp:252
GLFWwindow * window
Definition main.cc:45
glong glong end
FlKeyEvent * event
uint8_t value
uint32_t * target
const char * name
Definition fuchsia.cc:50
NSString * nativeSubroleFromAXRole:(ax::mojom::Role role)
API_AVAILABLE(ios(13.0)) @interface FlutterTextPlaceholder UITextRange * selectedTextRange
size_t length
std::u16string text
std::string UTF16ToUTF8(std::u16string src)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
GFX_EXPORT NSRect ScreenRectToNSRect(const Rect &rect)
UnimplementedNativeViewAccessible * NativeViewAccessible
Definition ref_ptr.h:256
AX_EXPORT bool IsNameExposedInAXValueForRole(ax::mojom::Role role)
bool IsMenuItem(ax::mojom::Role role)
bool IsListItem(const ax::mojom::Role role)
ax::mojom::Role role
const uintptr_t id
#define BASE_DCHECK(condition)
Definition logging.h:63
#define BASE_UNREACHABLE()
Definition logging.h:69
#define BASE_LOG()
Definition logging.h:54
int BOOL