Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1243)

Side by Side Diff: third_party/WebKit/Source/modules/accessibility/AXObjectImpl.cpp

Issue 2883203002: Revert of Rename AXObject to AXObjectImpl in modules/ and web/ (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2008, 2009, 2011 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "modules/accessibility/AXObjectImpl.h"
30
31 #include "SkMatrix44.h"
32 #include "core/InputTypeNames.h"
33 #include "core/css/resolver/StyleResolver.h"
34 #include "core/dom/AccessibleNode.h"
35 #include "core/dom/DocumentUserGestureToken.h"
36 #include "core/editing/EditingUtilities.h"
37 #include "core/editing/VisibleUnits.h"
38 #include "core/frame/FrameView.h"
39 #include "core/frame/LocalFrame.h"
40 #include "core/frame/Settings.h"
41 #include "core/html/HTMLDialogElement.h"
42 #include "core/html/HTMLFrameOwnerElement.h"
43 #include "core/html/HTMLInputElement.h"
44 #include "core/html/parser/HTMLParserIdioms.h"
45 #include "core/layout/LayoutBoxModelObject.h"
46 #include "modules/accessibility/AXObjectCacheImpl.h"
47 #include "platform/UserGestureIndicator.h"
48 #include "platform/text/PlatformLocale.h"
49 #include "platform/wtf/HashSet.h"
50 #include "platform/wtf/StdLibExtras.h"
51 #include "platform/wtf/text/WTFString.h"
52
53 using blink::WebLocalizedString;
54
55 namespace blink {
56
57 using namespace HTMLNames;
58
59 namespace {
60 typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;
61 typedef HashSet<String, CaseFoldingHash> ARIAWidgetSet;
62
63 struct RoleEntry {
64 const char* aria_role;
65 AccessibilityRole webcore_role;
66 };
67
68 const RoleEntry kRoles[] = {{"alert", kAlertRole},
69 {"alertdialog", kAlertDialogRole},
70 {"application", kApplicationRole},
71 {"article", kArticleRole},
72 {"banner", kBannerRole},
73 {"button", kButtonRole},
74 {"cell", kCellRole},
75 {"checkbox", kCheckBoxRole},
76 {"columnheader", kColumnHeaderRole},
77 {"combobox", kComboBoxRole},
78 {"complementary", kComplementaryRole},
79 {"contentinfo", kContentInfoRole},
80 {"definition", kDefinitionRole},
81 {"dialog", kDialogRole},
82 {"directory", kDirectoryRole},
83 {"document", kDocumentRole},
84 {"feed", kFeedRole},
85 {"figure", kFigureRole},
86 {"form", kFormRole},
87 {"grid", kGridRole},
88 {"gridcell", kCellRole},
89 {"group", kGroupRole},
90 {"heading", kHeadingRole},
91 {"img", kImageRole},
92 {"link", kLinkRole},
93 {"list", kListRole},
94 {"listbox", kListBoxRole},
95 {"listitem", kListItemRole},
96 {"log", kLogRole},
97 {"main", kMainRole},
98 {"marquee", kMarqueeRole},
99 {"math", kMathRole},
100 {"menu", kMenuRole},
101 {"menubar", kMenuBarRole},
102 {"menuitem", kMenuItemRole},
103 {"menuitemcheckbox", kMenuItemCheckBoxRole},
104 {"menuitemradio", kMenuItemRadioRole},
105 {"navigation", kNavigationRole},
106 {"none", kNoneRole},
107 {"note", kNoteRole},
108 {"option", kListBoxOptionRole},
109 {"presentation", kPresentationalRole},
110 {"progressbar", kProgressIndicatorRole},
111 {"radio", kRadioButtonRole},
112 {"radiogroup", kRadioGroupRole},
113 {"region", kRegionRole},
114 {"row", kRowRole},
115 {"rowheader", kRowHeaderRole},
116 {"scrollbar", kScrollBarRole},
117 {"search", kSearchRole},
118 {"searchbox", kSearchBoxRole},
119 {"separator", kSplitterRole},
120 {"slider", kSliderRole},
121 {"spinbutton", kSpinButtonRole},
122 {"status", kStatusRole},
123 {"switch", kSwitchRole},
124 {"tab", kTabRole},
125 {"table", kTableRole},
126 {"tablist", kTabListRole},
127 {"tabpanel", kTabPanelRole},
128 {"term", kTermRole},
129 {"text", kStaticTextRole},
130 {"textbox", kTextFieldRole},
131 {"timer", kTimerRole},
132 {"toolbar", kToolbarRole},
133 {"tooltip", kUserInterfaceTooltipRole},
134 {"tree", kTreeRole},
135 {"treegrid", kTreeGridRole},
136 {"treeitem", kTreeItemRole}};
137
138 struct InternalRoleEntry {
139 AccessibilityRole webcore_role;
140 const char* internal_role_name;
141 };
142
143 const InternalRoleEntry kInternalRoles[] = {
144 {kUnknownRole, "Unknown"},
145 {kAbbrRole, "Abbr"},
146 {kAlertDialogRole, "AlertDialog"},
147 {kAlertRole, "Alert"},
148 {kAnchorRole, "Anchor"},
149 {kAnnotationRole, "Annotation"},
150 {kApplicationRole, "Application"},
151 {kArticleRole, "Article"},
152 {kAudioRole, "Audio"},
153 {kBannerRole, "Banner"},
154 {kBlockquoteRole, "Blockquote"},
155 // TODO(nektar): Delete busy_indicator role. It's used nowhere.
156 {kBusyIndicatorRole, "BusyIndicator"},
157 {kButtonRole, "Button"},
158 {kCanvasRole, "Canvas"},
159 {kCaptionRole, "Caption"},
160 {kCellRole, "Cell"},
161 {kCheckBoxRole, "CheckBox"},
162 {kColorWellRole, "ColorWell"},
163 {kColumnHeaderRole, "ColumnHeader"},
164 {kColumnRole, "Column"},
165 {kComboBoxRole, "ComboBox"},
166 {kComplementaryRole, "Complementary"},
167 {kContentInfoRole, "ContentInfo"},
168 {kDateRole, "Date"},
169 {kDateTimeRole, "DateTime"},
170 {kDefinitionRole, "Definition"},
171 {kDescriptionListDetailRole, "DescriptionListDetail"},
172 {kDescriptionListRole, "DescriptionList"},
173 {kDescriptionListTermRole, "DescriptionListTerm"},
174 {kDetailsRole, "Details"},
175 {kDialogRole, "Dialog"},
176 {kDirectoryRole, "Directory"},
177 {kDisclosureTriangleRole, "DisclosureTriangle"},
178 {kDivRole, "Div"},
179 {kDocumentRole, "Document"},
180 {kEmbeddedObjectRole, "EmbeddedObject"},
181 {kFeedRole, "feed"},
182 {kFigcaptionRole, "Figcaption"},
183 {kFigureRole, "Figure"},
184 {kFooterRole, "Footer"},
185 {kFormRole, "Form"},
186 {kGridRole, "Grid"},
187 {kGroupRole, "Group"},
188 {kHeadingRole, "Heading"},
189 {kIframePresentationalRole, "IframePresentational"},
190 {kIframeRole, "Iframe"},
191 {kIgnoredRole, "Ignored"},
192 {kImageMapLinkRole, "ImageMapLink"},
193 {kImageMapRole, "ImageMap"},
194 {kImageRole, "Image"},
195 {kInlineTextBoxRole, "InlineTextBox"},
196 {kInputTimeRole, "InputTime"},
197 {kLabelRole, "Label"},
198 {kLegendRole, "Legend"},
199 {kLinkRole, "Link"},
200 {kLineBreakRole, "LineBreak"},
201 {kListBoxOptionRole, "ListBoxOption"},
202 {kListBoxRole, "ListBox"},
203 {kListItemRole, "ListItem"},
204 {kListMarkerRole, "ListMarker"},
205 {kListRole, "List"},
206 {kLogRole, "Log"},
207 {kMainRole, "Main"},
208 {kMarkRole, "Mark"},
209 {kMarqueeRole, "Marquee"},
210 {kMathRole, "Math"},
211 {kMenuBarRole, "MenuBar"},
212 {kMenuButtonRole, "MenuButton"},
213 {kMenuItemRole, "MenuItem"},
214 {kMenuItemCheckBoxRole, "MenuItemCheckBox"},
215 {kMenuItemRadioRole, "MenuItemRadio"},
216 {kMenuListOptionRole, "MenuListOption"},
217 {kMenuListPopupRole, "MenuListPopup"},
218 {kMenuRole, "Menu"},
219 {kMeterRole, "Meter"},
220 {kNavigationRole, "Navigation"},
221 {kNoneRole, "None"},
222 {kNoteRole, "Note"},
223 {kOutlineRole, "Outline"},
224 {kParagraphRole, "Paragraph"},
225 {kPopUpButtonRole, "PopUpButton"},
226 {kPreRole, "Pre"},
227 {kPresentationalRole, "Presentational"},
228 {kProgressIndicatorRole, "ProgressIndicator"},
229 {kRadioButtonRole, "RadioButton"},
230 {kRadioGroupRole, "RadioGroup"},
231 {kRegionRole, "Region"},
232 {kRootWebAreaRole, "RootWebArea"},
233 {kRowHeaderRole, "RowHeader"},
234 {kRowRole, "Row"},
235 {kRubyRole, "Ruby"},
236 {kRulerRole, "Ruler"},
237 {kSVGRootRole, "SVGRoot"},
238 {kScrollAreaRole, "ScrollArea"},
239 {kScrollBarRole, "ScrollBar"},
240 {kSeamlessWebAreaRole, "SeamlessWebArea"},
241 {kSearchRole, "Search"},
242 {kSearchBoxRole, "SearchBox"},
243 {kSliderRole, "Slider"},
244 {kSliderThumbRole, "SliderThumb"},
245 {kSpinButtonPartRole, "SpinButtonPart"},
246 {kSpinButtonRole, "SpinButton"},
247 {kSplitterRole, "Splitter"},
248 {kStaticTextRole, "StaticText"},
249 {kStatusRole, "Status"},
250 {kSwitchRole, "Switch"},
251 {kTabGroupRole, "TabGroup"},
252 {kTabListRole, "TabList"},
253 {kTabPanelRole, "TabPanel"},
254 {kTabRole, "Tab"},
255 {kTableHeaderContainerRole, "TableHeaderContainer"},
256 {kTableRole, "Table"},
257 {kTermRole, "Term"},
258 {kTextFieldRole, "TextField"},
259 {kTimeRole, "Time"},
260 {kTimerRole, "Timer"},
261 {kToggleButtonRole, "ToggleButton"},
262 {kToolbarRole, "Toolbar"},
263 {kTreeGridRole, "TreeGrid"},
264 {kTreeItemRole, "TreeItem"},
265 {kTreeRole, "Tree"},
266 {kUserInterfaceTooltipRole, "UserInterfaceTooltip"},
267 {kVideoRole, "Video"},
268 {kWebAreaRole, "WebArea"},
269 {kWindowRole, "Window"}};
270
271 static_assert(WTF_ARRAY_LENGTH(kInternalRoles) == kNumRoles,
272 "Not all internal roles have an entry in internalRoles array");
273
274 // Roles which we need to map in the other direction
275 const RoleEntry kReverseRoles[] = {
276 {"button", kToggleButtonRole}, {"combobox", kPopUpButtonRole},
277 {"contentinfo", kFooterRole}, {"menuitem", kMenuButtonRole},
278 {"menuitem", kMenuListOptionRole}, {"progressbar", kMeterRole},
279 {"textbox", kTextFieldRole}};
280
281 static ARIARoleMap* CreateARIARoleMap() {
282 ARIARoleMap* role_map = new ARIARoleMap;
283
284 for (size_t i = 0; i < WTF_ARRAY_LENGTH(kRoles); ++i)
285 role_map->Set(String(kRoles[i].aria_role), kRoles[i].webcore_role);
286 return role_map;
287 }
288
289 static Vector<AtomicString>* CreateRoleNameVector() {
290 Vector<AtomicString>* role_name_vector = new Vector<AtomicString>(kNumRoles);
291 for (int i = 0; i < kNumRoles; i++)
292 (*role_name_vector)[i] = g_null_atom;
293
294 for (size_t i = 0; i < WTF_ARRAY_LENGTH(kRoles); ++i) {
295 (*role_name_vector)[kRoles[i].webcore_role] =
296 AtomicString(kRoles[i].aria_role);
297 }
298
299 for (size_t i = 0; i < WTF_ARRAY_LENGTH(kReverseRoles); ++i) {
300 (*role_name_vector)[kReverseRoles[i].webcore_role] =
301 AtomicString(kReverseRoles[i].aria_role);
302 }
303
304 return role_name_vector;
305 }
306
307 static Vector<AtomicString>* CreateInternalRoleNameVector() {
308 Vector<AtomicString>* internal_role_name_vector =
309 new Vector<AtomicString>(kNumRoles);
310 for (size_t i = 0; i < WTF_ARRAY_LENGTH(kInternalRoles); i++) {
311 (*internal_role_name_vector)[kInternalRoles[i].webcore_role] =
312 AtomicString(kInternalRoles[i].internal_role_name);
313 }
314
315 return internal_role_name_vector;
316 }
317
318 const char* g_aria_widgets[] = {
319 // From http://www.w3.org/TR/wai-aria/roles#widget_roles
320 "alert", "alertdialog", "button", "checkbox", "dialog", "gridcell", "link",
321 "log", "marquee", "menuitem", "menuitemcheckbox", "menuitemradio", "option",
322 "progressbar", "radio", "scrollbar", "slider", "spinbutton", "status",
323 "tab", "tabpanel", "textbox", "timer", "tooltip", "treeitem",
324 // Composite user interface widgets.
325 // This list is also from the w3.org site referenced above.
326 "combobox", "grid", "listbox", "menu", "menubar", "radiogroup", "tablist",
327 "tree", "treegrid"};
328
329 static ARIAWidgetSet* CreateARIARoleWidgetSet() {
330 ARIAWidgetSet* widget_set = new HashSet<String, CaseFoldingHash>();
331 for (size_t i = 0; i < WTF_ARRAY_LENGTH(g_aria_widgets); ++i)
332 widget_set->insert(String(g_aria_widgets[i]));
333 return widget_set;
334 }
335
336 const char* g_aria_interactive_widget_attributes[] = {
337 // These attributes implicitly indicate the given widget is interactive.
338 // From http://www.w3.org/TR/wai-aria/states_and_properties#attrs_widgets
339 "aria-activedescendant", "aria-checked", "aria-controls",
340 "aria-disabled", // If it's disabled, it can be made interactive.
341 "aria-expanded", "aria-haspopup", "aria-multiselectable",
342 "aria-pressed", "aria-required", "aria-selected"};
343
344 HTMLDialogElement* GetActiveDialogElement(Node* node) {
345 return node->GetDocument().ActiveModalDialog();
346 }
347
348 } // namespace
349
350 unsigned AXObjectImpl::number_of_live_ax_objects_ = 0;
351
352 AXObjectImpl::AXObjectImpl(AXObjectCacheImpl& ax_object_cache)
353 : id_(0),
354 have_children_(false),
355 role_(kUnknownRole),
356 last_known_is_ignored_value_(kDefaultBehavior),
357 explicit_container_id_(0),
358 parent_(nullptr),
359 last_modification_count_(-1),
360 cached_is_ignored_(false),
361 cached_is_inert_or_aria_hidden_(false),
362 cached_is_descendant_of_leaf_node_(false),
363 cached_is_descendant_of_disabled_node_(false),
364 cached_has_inherited_presentational_role_(false),
365 cached_is_presentational_child_(false),
366 cached_ancestor_exposes_active_descendant_(false),
367 cached_live_region_root_(nullptr),
368 ax_object_cache_(&ax_object_cache) {
369 ++number_of_live_ax_objects_;
370 }
371
372 AXObjectImpl::~AXObjectImpl() {
373 DCHECK(IsDetached());
374 --number_of_live_ax_objects_;
375 }
376
377 void AXObjectImpl::Detach() {
378 // Clear any children and call detachFromParent on them so that
379 // no children are left with dangling pointers to their parent.
380 ClearChildren();
381
382 ax_object_cache_ = nullptr;
383 }
384
385 bool AXObjectImpl::IsDetached() const {
386 return !ax_object_cache_;
387 }
388
389 const AtomicString& AXObjectImpl::GetAOMPropertyOrARIAAttribute(
390 AOMStringProperty property) const {
391 Node* node = this->GetNode();
392 if (!node || !node->IsElementNode())
393 return g_null_atom;
394
395 return AccessibleNode::GetPropertyOrARIAAttribute(ToElement(node), property);
396 }
397
398 bool AXObjectImpl::IsARIATextControl() const {
399 return AriaRoleAttribute() == kTextFieldRole ||
400 AriaRoleAttribute() == kSearchBoxRole ||
401 AriaRoleAttribute() == kComboBoxRole;
402 }
403
404 bool AXObjectImpl::IsButton() const {
405 AccessibilityRole role = RoleValue();
406
407 return role == kButtonRole || role == kPopUpButtonRole ||
408 role == kToggleButtonRole;
409 }
410
411 bool AXObjectImpl::IsCheckable() const {
412 switch (RoleValue()) {
413 case kCheckBoxRole:
414 case kMenuItemCheckBoxRole:
415 case kMenuItemRadioRole:
416 case kRadioButtonRole:
417 case kSwitchRole:
418 return true;
419 default:
420 return false;
421 }
422 }
423
424 // Why this is here instead of AXNodeObject:
425 // Because an AXMenuListOption (<option>) can
426 // have an ARIA role of menuitemcheckbox/menuitemradio
427 // yet does not inherit from AXNodeObject
428 AccessibilityButtonState AXObjectImpl::CheckedState() const {
429 if (!IsCheckable())
430 return kButtonStateOff;
431
432 const AtomicString& checkedAttribute =
433 GetAOMPropertyOrARIAAttribute(AOMStringProperty::kChecked);
434 if (checkedAttribute) {
435 if (EqualIgnoringASCIICase(checkedAttribute, "true"))
436 return kButtonStateOn;
437
438 if (EqualIgnoringASCIICase(checkedAttribute, "mixed")) {
439 // Only checkboxes and radios should support the mixed state.
440 const AccessibilityRole role = RoleValue();
441 if (role == kCheckBoxRole || role == kMenuItemCheckBoxRole ||
442 role == kRadioButtonRole || role == kMenuItemRadioRole)
443 return kButtonStateMixed;
444 }
445
446 return kButtonStateOff;
447 }
448
449 const Node* node = this->GetNode();
450 if (!node)
451 return kButtonStateOff;
452
453 if (IsNativeInputInMixedState(node))
454 return kButtonStateMixed;
455
456 if (isHTMLInputElement(*node) &&
457 toHTMLInputElement(*node).ShouldAppearChecked()) {
458 return kButtonStateOn;
459 }
460
461 return kButtonStateOff;
462 }
463
464 bool AXObjectImpl::IsNativeInputInMixedState(const Node* node) {
465 if (!isHTMLInputElement(node))
466 return false;
467
468 const HTMLInputElement* input = toHTMLInputElement(node);
469 const auto inputType = input->type();
470 if (inputType != InputTypeNames::checkbox &&
471 inputType != InputTypeNames::radio)
472 return false;
473 return input->ShouldAppearIndeterminate();
474 }
475
476 bool AXObjectImpl::IsLandmarkRelated() const {
477 switch (RoleValue()) {
478 case kApplicationRole:
479 case kArticleRole:
480 case kBannerRole:
481 case kComplementaryRole:
482 case kContentInfoRole:
483 case kFooterRole:
484 case kFormRole:
485 case kMainRole:
486 case kNavigationRole:
487 case kRegionRole:
488 case kSearchRole:
489 return true;
490 default:
491 return false;
492 }
493 }
494
495 bool AXObjectImpl::IsMenuRelated() const {
496 switch (RoleValue()) {
497 case kMenuRole:
498 case kMenuBarRole:
499 case kMenuButtonRole:
500 case kMenuItemRole:
501 case kMenuItemCheckBoxRole:
502 case kMenuItemRadioRole:
503 return true;
504 default:
505 return false;
506 }
507 }
508
509 bool AXObjectImpl::IsPasswordFieldAndShouldHideValue() const {
510 Settings* settings = GetDocument()->GetSettings();
511 if (!settings || settings->GetAccessibilityPasswordValuesEnabled())
512 return false;
513
514 return IsPasswordField();
515 }
516
517 bool AXObjectImpl::IsClickable() const {
518 switch (RoleValue()) {
519 case kButtonRole:
520 case kCheckBoxRole:
521 case kColorWellRole:
522 case kComboBoxRole:
523 case kImageMapLinkRole:
524 case kLinkRole:
525 case kListBoxOptionRole:
526 case kMenuButtonRole:
527 case kPopUpButtonRole:
528 case kRadioButtonRole:
529 case kSpinButtonRole:
530 case kTabRole:
531 case kTextFieldRole:
532 case kToggleButtonRole:
533 return true;
534 default:
535 return false;
536 }
537 }
538
539 bool AXObjectImpl::AccessibilityIsIgnored() const {
540 UpdateCachedAttributeValuesIfNeeded();
541 return cached_is_ignored_;
542 }
543
544 void AXObjectImpl::UpdateCachedAttributeValuesIfNeeded() const {
545 if (IsDetached())
546 return;
547
548 AXObjectCacheImpl& cache = AxObjectCache();
549
550 if (cache.ModificationCount() == last_modification_count_)
551 return;
552
553 last_modification_count_ = cache.ModificationCount();
554 cached_background_color_ = ComputeBackgroundColor();
555 cached_is_inert_or_aria_hidden_ = ComputeIsInertOrAriaHidden();
556 cached_is_descendant_of_leaf_node_ = (LeafNodeAncestor() != 0);
557 cached_is_descendant_of_disabled_node_ = (DisabledAncestor() != 0);
558 cached_has_inherited_presentational_role_ =
559 (InheritsPresentationalRoleFrom() != 0);
560 cached_is_presentational_child_ =
561 (AncestorForWhichThisIsAPresentationalChild() != 0);
562 cached_is_ignored_ = ComputeAccessibilityIsIgnored();
563 cached_live_region_root_ =
564 IsLiveRegion()
565 ? const_cast<AXObjectImpl*>(this)
566 : (ParentObjectIfExists() ? ParentObjectIfExists()->LiveRegionRoot()
567 : 0);
568 cached_ancestor_exposes_active_descendant_ =
569 ComputeAncestorExposesActiveDescendant();
570 }
571
572 bool AXObjectImpl::AccessibilityIsIgnoredByDefault(
573 IgnoredReasons* ignored_reasons) const {
574 return DefaultObjectInclusion(ignored_reasons) == kIgnoreObject;
575 }
576
577 AXObjectInclusion AXObjectImpl::AccessibilityPlatformIncludesObject() const {
578 if (IsMenuListPopup() || IsMenuListOption())
579 return kIncludeObject;
580
581 return kDefaultBehavior;
582 }
583
584 AXObjectInclusion AXObjectImpl::DefaultObjectInclusion(
585 IgnoredReasons* ignored_reasons) const {
586 if (IsInertOrAriaHidden()) {
587 if (ignored_reasons)
588 ComputeIsInertOrAriaHidden(ignored_reasons);
589 return kIgnoreObject;
590 }
591
592 if (IsPresentationalChild()) {
593 if (ignored_reasons) {
594 AXObjectImpl* ancestor = AncestorForWhichThisIsAPresentationalChild();
595 ignored_reasons->push_back(
596 IgnoredReason(kAXAncestorDisallowsChild, ancestor));
597 }
598 return kIgnoreObject;
599 }
600
601 return AccessibilityPlatformIncludesObject();
602 }
603
604 bool AXObjectImpl::IsInertOrAriaHidden() const {
605 UpdateCachedAttributeValuesIfNeeded();
606 return cached_is_inert_or_aria_hidden_;
607 }
608
609 bool AXObjectImpl::ComputeIsInertOrAriaHidden(
610 IgnoredReasons* ignored_reasons) const {
611 if (GetNode()) {
612 if (GetNode()->IsInert()) {
613 if (ignored_reasons) {
614 HTMLDialogElement* dialog = GetActiveDialogElement(GetNode());
615 if (dialog) {
616 AXObjectImpl* dialog_object = AxObjectCache().GetOrCreate(dialog);
617 if (dialog_object) {
618 ignored_reasons->push_back(
619 IgnoredReason(kAXActiveModalDialog, dialog_object));
620 } else {
621 ignored_reasons->push_back(IgnoredReason(kAXInert));
622 }
623 } else {
624 // TODO(aboxhall): handle inert attribute if it eventuates
625 ignored_reasons->push_back(IgnoredReason(kAXInert));
626 }
627 }
628 return true;
629 }
630 } else {
631 AXObjectImpl* parent = ParentObject();
632 if (parent && parent->IsInertOrAriaHidden()) {
633 if (ignored_reasons)
634 parent->ComputeIsInertOrAriaHidden(ignored_reasons);
635 return true;
636 }
637 }
638
639 const AXObjectImpl* hidden_root = AriaHiddenRoot();
640 if (hidden_root) {
641 if (ignored_reasons) {
642 if (hidden_root == this) {
643 ignored_reasons->push_back(IgnoredReason(kAXAriaHidden));
644 } else {
645 ignored_reasons->push_back(
646 IgnoredReason(kAXAriaHiddenRoot, hidden_root));
647 }
648 }
649 return true;
650 }
651
652 return false;
653 }
654
655 bool AXObjectImpl::IsDescendantOfLeafNode() const {
656 UpdateCachedAttributeValuesIfNeeded();
657 return cached_is_descendant_of_leaf_node_;
658 }
659
660 AXObjectImpl* AXObjectImpl::LeafNodeAncestor() const {
661 if (AXObjectImpl* parent = ParentObject()) {
662 if (!parent->CanHaveChildren())
663 return parent;
664
665 return parent->LeafNodeAncestor();
666 }
667
668 return 0;
669 }
670
671 const AXObjectImpl* AXObjectImpl::AriaHiddenRoot() const {
672 for (const AXObjectImpl* object = this; object;
673 object = object->ParentObject()) {
674 if (EqualIgnoringASCIICase(object->GetAttribute(aria_hiddenAttr), "true"))
675 return object;
676 }
677
678 return 0;
679 }
680
681 bool AXObjectImpl::IsDescendantOfDisabledNode() const {
682 UpdateCachedAttributeValuesIfNeeded();
683 return cached_is_descendant_of_disabled_node_;
684 }
685
686 const AXObjectImpl* AXObjectImpl::DisabledAncestor() const {
687 const AtomicString& disabled = GetAttribute(aria_disabledAttr);
688 if (EqualIgnoringASCIICase(disabled, "true"))
689 return this;
690 if (EqualIgnoringASCIICase(disabled, "false"))
691 return 0;
692
693 if (AXObjectImpl* parent = ParentObject())
694 return parent->DisabledAncestor();
695
696 return 0;
697 }
698
699 bool AXObjectImpl::LastKnownIsIgnoredValue() {
700 if (last_known_is_ignored_value_ == kDefaultBehavior) {
701 last_known_is_ignored_value_ =
702 AccessibilityIsIgnored() ? kIgnoreObject : kIncludeObject;
703 }
704
705 return last_known_is_ignored_value_ == kIgnoreObject;
706 }
707
708 void AXObjectImpl::SetLastKnownIsIgnoredValue(bool is_ignored) {
709 last_known_is_ignored_value_ = is_ignored ? kIgnoreObject : kIncludeObject;
710 }
711
712 bool AXObjectImpl::HasInheritedPresentationalRole() const {
713 UpdateCachedAttributeValuesIfNeeded();
714 return cached_has_inherited_presentational_role_;
715 }
716
717 bool AXObjectImpl::IsPresentationalChild() const {
718 UpdateCachedAttributeValuesIfNeeded();
719 return cached_is_presentational_child_;
720 }
721
722 bool AXObjectImpl::AncestorExposesActiveDescendant() const {
723 UpdateCachedAttributeValuesIfNeeded();
724 return cached_ancestor_exposes_active_descendant_;
725 }
726
727 bool AXObjectImpl::ComputeAncestorExposesActiveDescendant() const {
728 const AXObjectImpl* parent = ParentObjectUnignored();
729 if (!parent)
730 return false;
731
732 if (parent->SupportsActiveDescendant() &&
733 !parent->GetAttribute(aria_activedescendantAttr).IsEmpty()) {
734 return true;
735 }
736
737 return parent->AncestorExposesActiveDescendant();
738 }
739
740 // Simplify whitespace, but preserve a single leading and trailing whitespace
741 // character if it's present.
742 // static
743 String AXObjectImpl::CollapseWhitespace(const String& str) {
744 StringBuilder result;
745 if (!str.IsEmpty() && IsHTMLSpace<UChar>(str[0]))
746 result.Append(' ');
747 result.Append(str.SimplifyWhiteSpace(IsHTMLSpace<UChar>));
748 if (!str.IsEmpty() && IsHTMLSpace<UChar>(str[str.length() - 1]))
749 result.Append(' ');
750 return result.ToString();
751 }
752
753 String AXObjectImpl::ComputedName() const {
754 AXNameFrom name_from;
755 AXObjectImpl::AXObjectVector name_objects;
756 return GetName(name_from, &name_objects);
757 }
758
759 String AXObjectImpl::GetName(AXNameFrom& name_from,
760 AXObjectImpl::AXObjectVector* name_objects) const {
761 HeapHashSet<Member<const AXObjectImpl>> visited;
762 AXRelatedObjectVector related_objects;
763 String text = TextAlternative(false, false, visited, name_from,
764 &related_objects, nullptr);
765
766 AccessibilityRole role = RoleValue();
767 if (!GetNode() || (!isHTMLBRElement(GetNode()) && role != kStaticTextRole &&
768 role != kInlineTextBoxRole))
769 text = CollapseWhitespace(text);
770
771 if (name_objects) {
772 name_objects->clear();
773 for (size_t i = 0; i < related_objects.size(); i++)
774 name_objects->push_back(related_objects[i]->object);
775 }
776
777 return text;
778 }
779
780 String AXObjectImpl::GetName(NameSources* name_sources) const {
781 AXObjectSet visited;
782 AXNameFrom tmp_name_from;
783 AXRelatedObjectVector tmp_related_objects;
784 String text = TextAlternative(false, false, visited, tmp_name_from,
785 &tmp_related_objects, name_sources);
786 text = text.SimplifyWhiteSpace(IsHTMLSpace<UChar>);
787 return text;
788 }
789
790 String AXObjectImpl::RecursiveTextAlternative(
791 const AXObjectImpl& ax_obj,
792 bool in_aria_labelled_by_traversal,
793 AXObjectSet& visited) {
794 if (visited.Contains(&ax_obj) && !in_aria_labelled_by_traversal)
795 return String();
796
797 AXNameFrom tmp_name_from;
798 return ax_obj.TextAlternative(true, in_aria_labelled_by_traversal, visited,
799 tmp_name_from, nullptr, nullptr);
800 }
801
802 bool AXObjectImpl::IsHiddenForTextAlternativeCalculation() const {
803 if (EqualIgnoringASCIICase(GetAttribute(aria_hiddenAttr), "false"))
804 return false;
805
806 if (GetLayoutObject())
807 return GetLayoutObject()->Style()->Visibility() != EVisibility::kVisible;
808
809 // This is an obscure corner case: if a node has no LayoutObject, that means
810 // it's not rendered, but we still may be exploring it as part of a text
811 // alternative calculation, for example if it was explicitly referenced by
812 // aria-labelledby. So we need to explicitly call the style resolver to check
813 // whether it's invisible or display:none, rather than relying on the style
814 // cached in the LayoutObject.
815 Document* document = GetDocument();
816 if (!document || !document->GetFrame())
817 return false;
818 if (Node* node = GetNode()) {
819 if (node->isConnected() && node->IsElementNode()) {
820 RefPtr<ComputedStyle> style =
821 document->EnsureStyleResolver().StyleForElement(ToElement(node));
822 return style->Display() == EDisplay::kNone ||
823 style->Visibility() != EVisibility::kVisible;
824 }
825 }
826 return false;
827 }
828
829 String AXObjectImpl::AriaTextAlternative(bool recursive,
830 bool in_aria_labelled_by_traversal,
831 AXObjectSet& visited,
832 AXNameFrom& name_from,
833 AXRelatedObjectVector* related_objects,
834 NameSources* name_sources,
835 bool* found_text_alternative) const {
836 String text_alternative;
837 bool already_visited = visited.Contains(this);
838 visited.insert(this);
839
840 // Step 2A from: http://www.w3.org/TR/accname-aam-1.1
841 // If you change this logic, update AXNodeObject::nameFromLabelElement, too.
842 if (!in_aria_labelled_by_traversal &&
843 IsHiddenForTextAlternativeCalculation()) {
844 *found_text_alternative = true;
845 return String();
846 }
847
848 // Step 2B from: http://www.w3.org/TR/accname-aam-1.1
849 // If you change this logic, update AXNodeObject::nameFromLabelElement, too.
850 if (!in_aria_labelled_by_traversal && !already_visited) {
851 const QualifiedName& attr =
852 HasAttribute(aria_labeledbyAttr) && !HasAttribute(aria_labelledbyAttr)
853 ? aria_labeledbyAttr
854 : aria_labelledbyAttr;
855 name_from = kAXNameFromRelatedElement;
856 if (name_sources) {
857 name_sources->push_back(NameSource(*found_text_alternative, attr));
858 name_sources->back().type = name_from;
859 }
860
861 const AtomicString& aria_labelledby = GetAttribute(attr);
862 if (!aria_labelledby.IsNull()) {
863 if (name_sources)
864 name_sources->back().attribute_value = aria_labelledby;
865
866 // Operate on a copy of |visited| so that if |nameSources| is not null,
867 // the set of visited objects is preserved unmodified for future
868 // calculations.
869 AXObjectSet visited_copy = visited;
870 text_alternative = TextFromAriaLabelledby(visited_copy, related_objects);
871 if (!text_alternative.IsNull()) {
872 if (name_sources) {
873 NameSource& source = name_sources->back();
874 source.type = name_from;
875 source.related_objects = *related_objects;
876 source.text = text_alternative;
877 *found_text_alternative = true;
878 } else {
879 *found_text_alternative = true;
880 return text_alternative;
881 }
882 } else if (name_sources) {
883 name_sources->back().invalid = true;
884 }
885 }
886 }
887
888 // Step 2C from: http://www.w3.org/TR/accname-aam-1.1
889 // If you change this logic, update AXNodeObject::nameFromLabelElement, too.
890 name_from = kAXNameFromAttribute;
891 if (name_sources) {
892 name_sources->push_back(
893 NameSource(*found_text_alternative, aria_labelAttr));
894 name_sources->back().type = name_from;
895 }
896 const AtomicString& aria_label =
897 GetAOMPropertyOrARIAAttribute(AOMStringProperty::kLabel);
898 if (!aria_label.IsEmpty()) {
899 text_alternative = aria_label;
900
901 if (name_sources) {
902 NameSource& source = name_sources->back();
903 source.text = text_alternative;
904 source.attribute_value = aria_label;
905 *found_text_alternative = true;
906 } else {
907 *found_text_alternative = true;
908 return text_alternative;
909 }
910 }
911
912 return text_alternative;
913 }
914
915 String AXObjectImpl::TextFromElements(
916 bool in_aria_labelledby_traversal,
917 AXObjectSet& visited,
918 HeapVector<Member<Element>>& elements,
919 AXRelatedObjectVector* related_objects) const {
920 StringBuilder accumulated_text;
921 bool found_valid_element = false;
922 AXRelatedObjectVector local_related_objects;
923
924 for (const auto& element : elements) {
925 AXObjectImpl* ax_element = AxObjectCache().GetOrCreate(element);
926 if (ax_element) {
927 found_valid_element = true;
928
929 String result = RecursiveTextAlternative(
930 *ax_element, in_aria_labelledby_traversal, visited);
931 local_related_objects.push_back(
932 new NameSourceRelatedObject(ax_element, result));
933 if (!result.IsEmpty()) {
934 if (!accumulated_text.IsEmpty())
935 accumulated_text.Append(' ');
936 accumulated_text.Append(result);
937 }
938 }
939 }
940 if (!found_valid_element)
941 return String();
942 if (related_objects)
943 *related_objects = local_related_objects;
944 return accumulated_text.ToString();
945 }
946
947 void AXObjectImpl::TokenVectorFromAttribute(
948 Vector<String>& tokens,
949 const QualifiedName& attribute) const {
950 Node* node = this->GetNode();
951 if (!node || !node->IsElementNode())
952 return;
953
954 String attribute_value = GetAttribute(attribute).GetString();
955 if (attribute_value.IsEmpty())
956 return;
957
958 attribute_value.SimplifyWhiteSpace();
959 attribute_value.Split(' ', tokens);
960 }
961
962 void AXObjectImpl::ElementsFromAttribute(HeapVector<Member<Element>>& elements,
963 const QualifiedName& attribute) const {
964 Vector<String> ids;
965 TokenVectorFromAttribute(ids, attribute);
966 if (ids.IsEmpty())
967 return;
968
969 TreeScope& scope = GetNode()->GetTreeScope();
970 for (const auto& id : ids) {
971 if (Element* id_element = scope.getElementById(AtomicString(id)))
972 elements.push_back(id_element);
973 }
974 }
975
976 void AXObjectImpl::AriaLabelledbyElementVector(
977 HeapVector<Member<Element>>& elements) const {
978 // Try both spellings, but prefer aria-labelledby, which is the official spec.
979 ElementsFromAttribute(elements, aria_labelledbyAttr);
980 if (!elements.size())
981 ElementsFromAttribute(elements, aria_labeledbyAttr);
982 }
983
984 String AXObjectImpl::TextFromAriaLabelledby(
985 AXObjectSet& visited,
986 AXRelatedObjectVector* related_objects) const {
987 HeapVector<Member<Element>> elements;
988 AriaLabelledbyElementVector(elements);
989 return TextFromElements(true, visited, elements, related_objects);
990 }
991
992 String AXObjectImpl::TextFromAriaDescribedby(
993 AXRelatedObjectVector* related_objects) const {
994 AXObjectSet visited;
995 HeapVector<Member<Element>> elements;
996 ElementsFromAttribute(elements, aria_describedbyAttr);
997 return TextFromElements(true, visited, elements, related_objects);
998 }
999
1000 RGBA32 AXObjectImpl::BackgroundColor() const {
1001 UpdateCachedAttributeValuesIfNeeded();
1002 return cached_background_color_;
1003 }
1004
1005 AccessibilityOrientation AXObjectImpl::Orientation() const {
1006 // In ARIA 1.1, the default value for aria-orientation changed from
1007 // horizontal to undefined.
1008 return kAccessibilityOrientationUndefined;
1009 }
1010
1011 AXSupportedAction AXObjectImpl::Action() const {
1012 if (!ActionElement())
1013 return AXSupportedAction::kNone;
1014
1015 switch (RoleValue()) {
1016 case kButtonRole:
1017 case kToggleButtonRole:
1018 return AXSupportedAction::kPress;
1019 case kTextFieldRole:
1020 return AXSupportedAction::kActivate;
1021 case kRadioButtonRole:
1022 return AXSupportedAction::kSelect;
1023 case kCheckBoxRole:
1024 case kSwitchRole:
1025 return CheckedState() == kButtonStateOff ? AXSupportedAction::kCheck
1026 : AXSupportedAction::kUncheck;
1027 case kLinkRole:
1028 return AXSupportedAction::kJump;
1029 case kPopUpButtonRole:
1030 return AXSupportedAction::kOpen;
1031 default:
1032 return AXSupportedAction::kClick;
1033 }
1034 }
1035
1036 bool AXObjectImpl::IsMultiline() const {
1037 Node* node = this->GetNode();
1038 if (!node)
1039 return false;
1040
1041 if (isHTMLTextAreaElement(*node))
1042 return true;
1043
1044 if (HasEditableStyle(*node))
1045 return true;
1046
1047 if (!IsNativeTextControl() && !IsNonNativeTextControl())
1048 return false;
1049
1050 return EqualIgnoringASCIICase(GetAttribute(aria_multilineAttr), "true");
1051 }
1052
1053 bool AXObjectImpl::AriaPressedIsPresent() const {
1054 return !GetAttribute(aria_pressedAttr).IsEmpty();
1055 }
1056
1057 bool AXObjectImpl::SupportsActiveDescendant() const {
1058 // According to the ARIA Spec, all ARIA composite widgets, ARIA text boxes
1059 // and ARIA groups should be able to expose an active descendant.
1060 // Implicitly, <input> and <textarea> elements should also have this ability.
1061 switch (AriaRoleAttribute()) {
1062 case kComboBoxRole:
1063 case kGridRole:
1064 case kGroupRole:
1065 case kListBoxRole:
1066 case kMenuRole:
1067 case kMenuBarRole:
1068 case kRadioGroupRole:
1069 case kRowRole:
1070 case kSearchBoxRole:
1071 case kTabListRole:
1072 case kTextFieldRole:
1073 case kToolbarRole:
1074 case kTreeRole:
1075 case kTreeGridRole:
1076 return true;
1077 default:
1078 return false;
1079 }
1080 }
1081
1082 bool AXObjectImpl::SupportsARIAAttributes() const {
1083 return IsLiveRegion() || SupportsARIADragging() || SupportsARIADropping() ||
1084 SupportsARIAFlowTo() || SupportsARIAOwns() ||
1085 HasAttribute(aria_labelAttr);
1086 }
1087
1088 bool AXObjectImpl::SupportsRangeValue() const {
1089 return IsProgressIndicator() || IsMeter() || IsSlider() || IsScrollbar() ||
1090 IsSpinButton();
1091 }
1092
1093 bool AXObjectImpl::SupportsSetSizeAndPosInSet() const {
1094 AXObjectImpl* parent = ParentObject();
1095 if (!parent)
1096 return false;
1097
1098 int role = RoleValue();
1099 int parent_role = parent->RoleValue();
1100
1101 if ((role == kListBoxOptionRole && parent_role == kListBoxRole) ||
1102 (role == kListItemRole && parent_role == kListRole) ||
1103 (role == kMenuItemRole && parent_role == kMenuRole) ||
1104 (role == kRadioButtonRole) ||
1105 (role == kTabRole && parent_role == kTabListRole) ||
1106 (role == kTreeItemRole && parent_role == kTreeRole) ||
1107 (role == kTreeItemRole && parent_role == kGroupRole)) {
1108 return true;
1109 }
1110
1111 return false;
1112 }
1113
1114 int AXObjectImpl::IndexInParent() const {
1115 if (!ParentObject())
1116 return 0;
1117
1118 const auto& siblings = ParentObject()->Children();
1119 int child_count = siblings.size();
1120
1121 for (int index = 0; index < child_count; ++index) {
1122 if (siblings[index].Get() == this) {
1123 return index;
1124 }
1125 }
1126 return 0;
1127 }
1128
1129 bool AXObjectImpl::IsLiveRegion() const {
1130 const AtomicString& live_region = LiveRegionStatus();
1131 return EqualIgnoringASCIICase(live_region, "polite") ||
1132 EqualIgnoringASCIICase(live_region, "assertive");
1133 }
1134
1135 AXObjectImpl* AXObjectImpl::LiveRegionRoot() const {
1136 UpdateCachedAttributeValuesIfNeeded();
1137 return cached_live_region_root_;
1138 }
1139
1140 const AtomicString& AXObjectImpl::ContainerLiveRegionStatus() const {
1141 UpdateCachedAttributeValuesIfNeeded();
1142 return cached_live_region_root_ ? cached_live_region_root_->LiveRegionStatus()
1143 : g_null_atom;
1144 }
1145
1146 const AtomicString& AXObjectImpl::ContainerLiveRegionRelevant() const {
1147 UpdateCachedAttributeValuesIfNeeded();
1148 return cached_live_region_root_
1149 ? cached_live_region_root_->LiveRegionRelevant()
1150 : g_null_atom;
1151 }
1152
1153 bool AXObjectImpl::ContainerLiveRegionAtomic() const {
1154 UpdateCachedAttributeValuesIfNeeded();
1155 return cached_live_region_root_ &&
1156 cached_live_region_root_->LiveRegionAtomic();
1157 }
1158
1159 bool AXObjectImpl::ContainerLiveRegionBusy() const {
1160 UpdateCachedAttributeValuesIfNeeded();
1161 return cached_live_region_root_ && cached_live_region_root_->LiveRegionBusy();
1162 }
1163
1164 AXObjectImpl* AXObjectImpl::ElementAccessibilityHitTest(
1165 const IntPoint& point) const {
1166 // Check if there are any mock elements that need to be handled.
1167 for (const auto& child : children_) {
1168 if (child->IsMockObject() &&
1169 child->GetBoundsInFrameCoordinates().Contains(point))
1170 return child->ElementAccessibilityHitTest(point);
1171 }
1172
1173 return const_cast<AXObjectImpl*>(this);
1174 }
1175
1176 const AXObjectImpl::AXObjectVector& AXObjectImpl::Children() {
1177 UpdateChildrenIfNecessary();
1178
1179 return children_;
1180 }
1181
1182 AXObjectImpl* AXObjectImpl::ParentObject() const {
1183 if (IsDetached())
1184 return 0;
1185
1186 if (parent_)
1187 return parent_;
1188
1189 if (AxObjectCache().IsAriaOwned(this))
1190 return AxObjectCache().GetAriaOwnedParent(this);
1191
1192 return ComputeParent();
1193 }
1194
1195 AXObjectImpl* AXObjectImpl::ParentObjectIfExists() const {
1196 if (IsDetached())
1197 return 0;
1198
1199 if (parent_)
1200 return parent_;
1201
1202 return ComputeParentIfExists();
1203 }
1204
1205 AXObjectImpl* AXObjectImpl::ParentObjectUnignored() const {
1206 AXObjectImpl* parent;
1207 for (parent = ParentObject(); parent && parent->AccessibilityIsIgnored();
1208 parent = parent->ParentObject()) {
1209 }
1210
1211 return parent;
1212 }
1213
1214 void AXObjectImpl::UpdateChildrenIfNecessary() {
1215 if (!HasChildren())
1216 AddChildren();
1217 }
1218
1219 void AXObjectImpl::ClearChildren() {
1220 // Detach all weak pointers from objects to their parents.
1221 for (const auto& child : children_)
1222 child->DetachFromParent();
1223
1224 children_.clear();
1225 have_children_ = false;
1226 }
1227
1228 Document* AXObjectImpl::GetDocument() const {
1229 FrameView* frame_view = DocumentFrameView();
1230 if (!frame_view)
1231 return 0;
1232
1233 return frame_view->GetFrame().GetDocument();
1234 }
1235
1236 FrameView* AXObjectImpl::DocumentFrameView() const {
1237 const AXObjectImpl* object = this;
1238 while (object && !object->IsAXLayoutObject())
1239 object = object->ParentObject();
1240
1241 if (!object)
1242 return 0;
1243
1244 return object->DocumentFrameView();
1245 }
1246
1247 String AXObjectImpl::Language() const {
1248 const AtomicString& lang = GetAttribute(langAttr);
1249 if (!lang.IsEmpty())
1250 return lang;
1251
1252 AXObjectImpl* parent = ParentObject();
1253
1254 // As a last resort, fall back to the content language specified in the meta
1255 // tag.
1256 if (!parent) {
1257 Document* doc = GetDocument();
1258 if (doc)
1259 return doc->ContentLanguage();
1260 return g_null_atom;
1261 }
1262
1263 return parent->Language();
1264 }
1265
1266 bool AXObjectImpl::HasAttribute(const QualifiedName& attribute) const {
1267 Node* element_node = GetNode();
1268 if (!element_node)
1269 return false;
1270
1271 if (!element_node->IsElementNode())
1272 return false;
1273
1274 Element* element = ToElement(element_node);
1275 return element->FastHasAttribute(attribute);
1276 }
1277
1278 const AtomicString& AXObjectImpl::GetAttribute(
1279 const QualifiedName& attribute) const {
1280 Node* element_node = GetNode();
1281 if (!element_node)
1282 return g_null_atom;
1283
1284 if (!element_node->IsElementNode())
1285 return g_null_atom;
1286
1287 Element* element = ToElement(element_node);
1288 return element->FastGetAttribute(attribute);
1289 }
1290
1291 //
1292 // Scrollable containers.
1293 //
1294
1295 bool AXObjectImpl::IsScrollableContainer() const {
1296 return !!GetScrollableAreaIfScrollable();
1297 }
1298
1299 IntPoint AXObjectImpl::GetScrollOffset() const {
1300 ScrollableArea* area = GetScrollableAreaIfScrollable();
1301 if (!area)
1302 return IntPoint();
1303
1304 return IntPoint(area->ScrollOffsetInt().Width(),
1305 area->ScrollOffsetInt().Height());
1306 }
1307
1308 IntPoint AXObjectImpl::MinimumScrollOffset() const {
1309 ScrollableArea* area = GetScrollableAreaIfScrollable();
1310 if (!area)
1311 return IntPoint();
1312
1313 return IntPoint(area->MinimumScrollOffsetInt().Width(),
1314 area->MinimumScrollOffsetInt().Height());
1315 }
1316
1317 IntPoint AXObjectImpl::MaximumScrollOffset() const {
1318 ScrollableArea* area = GetScrollableAreaIfScrollable();
1319 if (!area)
1320 return IntPoint();
1321
1322 return IntPoint(area->MaximumScrollOffsetInt().Width(),
1323 area->MaximumScrollOffsetInt().Height());
1324 }
1325
1326 void AXObjectImpl::SetScrollOffset(const IntPoint& offset) const {
1327 ScrollableArea* area = GetScrollableAreaIfScrollable();
1328 if (!area)
1329 return;
1330
1331 // TODO(bokan): This should potentially be a UserScroll.
1332 area->SetScrollOffset(ScrollOffset(offset.X(), offset.Y()),
1333 kProgrammaticScroll);
1334 }
1335
1336 void AXObjectImpl::GetRelativeBounds(
1337 AXObjectImpl** out_container,
1338 FloatRect& out_bounds_in_container,
1339 SkMatrix44& out_container_transform) const {
1340 *out_container = nullptr;
1341 out_bounds_in_container = FloatRect();
1342 out_container_transform.setIdentity();
1343
1344 // First check if it has explicit bounds, for example if this element is tied
1345 // to a canvas path. When explicit coordinates are provided, the ID of the
1346 // explicit container element that the coordinates are relative to must be
1347 // provided too.
1348 if (!explicit_element_rect_.IsEmpty()) {
1349 *out_container = AxObjectCache().ObjectFromAXID(explicit_container_id_);
1350 if (*out_container) {
1351 out_bounds_in_container = FloatRect(explicit_element_rect_);
1352 return;
1353 }
1354 }
1355
1356 LayoutObject* layout_object = LayoutObjectForRelativeBounds();
1357 if (!layout_object)
1358 return;
1359
1360 if (IsWebArea()) {
1361 if (layout_object->GetFrame()->View()) {
1362 out_bounds_in_container.SetSize(
1363 FloatSize(layout_object->GetFrame()->View()->ContentsSize()));
1364 }
1365 return;
1366 }
1367
1368 // First compute the container. The container must be an ancestor in the
1369 // accessibility tree, and its LayoutObject must be an ancestor in the layout
1370 // tree. Get the first such ancestor that's either scrollable or has a paint
1371 // layer.
1372 AXObjectImpl* container = ParentObjectUnignored();
1373 LayoutObject* container_layout_object = nullptr;
1374 while (container) {
1375 container_layout_object = container->GetLayoutObject();
1376 if (container_layout_object &&
1377 container_layout_object->IsBoxModelObject() &&
1378 layout_object->IsDescendantOf(container_layout_object)) {
1379 if (container->IsScrollableContainer() ||
1380 container_layout_object->HasLayer())
1381 break;
1382 }
1383
1384 container = container->ParentObjectUnignored();
1385 }
1386
1387 if (!container)
1388 return;
1389 *out_container = container;
1390 out_bounds_in_container =
1391 layout_object->LocalBoundingBoxRectForAccessibility();
1392
1393 // If the container has a scroll offset, subtract that out because we want our
1394 // bounds to be relative to the *unscrolled* position of the container object.
1395 ScrollableArea* scrollable_area = container->GetScrollableAreaIfScrollable();
1396 if (scrollable_area && !container->IsWebArea()) {
1397 ScrollOffset scroll_offset = scrollable_area->GetScrollOffset();
1398 out_bounds_in_container.Move(scroll_offset);
1399 }
1400
1401 // Compute the transform between the container's coordinate space and this
1402 // object. If the transform is just a simple translation, apply that to the
1403 // bounding box, but if it's a non-trivial transformation like a rotation,
1404 // scaling, etc. then return the full matrix instead.
1405 TransformationMatrix transform = layout_object->LocalToAncestorTransform(
1406 ToLayoutBoxModelObject(container_layout_object));
1407 if (transform.IsIdentityOr2DTranslation()) {
1408 out_bounds_in_container.Move(transform.To2DTranslation());
1409 } else {
1410 out_container_transform = TransformationMatrix::ToSkMatrix44(transform);
1411 }
1412 }
1413
1414 LayoutRect AXObjectImpl::GetBoundsInFrameCoordinates() const {
1415 AXObjectImpl* container = nullptr;
1416 FloatRect bounds;
1417 SkMatrix44 transform;
1418 GetRelativeBounds(&container, bounds, transform);
1419 FloatRect computed_bounds(0, 0, bounds.Width(), bounds.Height());
1420 while (container && container != this) {
1421 computed_bounds.Move(bounds.X(), bounds.Y());
1422 if (!container->IsWebArea()) {
1423 computed_bounds.Move(-container->GetScrollOffset().X(),
1424 -container->GetScrollOffset().Y());
1425 }
1426 if (!transform.isIdentity()) {
1427 TransformationMatrix transformation_matrix(transform);
1428 transformation_matrix.MapRect(computed_bounds);
1429 }
1430 container->GetRelativeBounds(&container, bounds, transform);
1431 }
1432 return LayoutRect(computed_bounds);
1433 }
1434
1435 //
1436 // Modify or take an action on an object.
1437 //
1438
1439 bool AXObjectImpl::Press() {
1440 Document* document = GetDocument();
1441 if (!document)
1442 return false;
1443
1444 UserGestureIndicator gesture_indicator(DocumentUserGestureToken::Create(
1445 document, UserGestureToken::kNewGesture));
1446 Element* action_elem = ActionElement();
1447 if (action_elem) {
1448 action_elem->AccessKeyAction(true);
1449 return true;
1450 }
1451
1452 if (CanSetFocusAttribute()) {
1453 SetFocused(true);
1454 return true;
1455 }
1456
1457 return false;
1458 }
1459
1460 void AXObjectImpl::ScrollToMakeVisible() const {
1461 IntRect object_rect = PixelSnappedIntRect(GetBoundsInFrameCoordinates());
1462 object_rect.SetLocation(IntPoint());
1463 ScrollToMakeVisibleWithSubFocus(object_rect);
1464 }
1465
1466 // This is a 1-dimensional scroll offset helper function that's applied
1467 // separately in the horizontal and vertical directions, because the
1468 // logic is the same. The goal is to compute the best scroll offset
1469 // in order to make an object visible within a viewport.
1470 //
1471 // If the object is already fully visible, returns the same scroll
1472 // offset.
1473 //
1474 // In case the whole object cannot fit, you can specify a
1475 // subfocus - a smaller region within the object that should
1476 // be prioritized. If the whole object can fit, the subfocus is
1477 // ignored.
1478 //
1479 // If possible, the object and subfocus are centered within the
1480 // viewport.
1481 //
1482 // Example 1: the object is already visible, so nothing happens.
1483 // +----------Viewport---------+
1484 // +---Object---+
1485 // +--SubFocus--+
1486 //
1487 // Example 2: the object is not fully visible, so it's centered
1488 // within the viewport.
1489 // Before:
1490 // +----------Viewport---------+
1491 // +---Object---+
1492 // +--SubFocus--+
1493 //
1494 // After:
1495 // +----------Viewport---------+
1496 // +---Object---+
1497 // +--SubFocus--+
1498 //
1499 // Example 3: the object is larger than the viewport, so the
1500 // viewport moves to show as much of the object as possible,
1501 // while also trying to center the subfocus.
1502 // Before:
1503 // +----------Viewport---------+
1504 // +---------------Object--------------+
1505 // +-SubFocus-+
1506 //
1507 // After:
1508 // +----------Viewport---------+
1509 // +---------------Object--------------+
1510 // +-SubFocus-+
1511 //
1512 // When constraints cannot be fully satisfied, the min
1513 // (left/top) position takes precedence over the max (right/bottom).
1514 //
1515 // Note that the return value represents the ideal new scroll offset.
1516 // This may be out of range - the calling function should clip this
1517 // to the available range.
1518 static int ComputeBestScrollOffset(int current_scroll_offset,
1519 int subfocus_min,
1520 int subfocus_max,
1521 int object_min,
1522 int object_max,
1523 int viewport_min,
1524 int viewport_max) {
1525 int viewport_size = viewport_max - viewport_min;
1526
1527 // If the object size is larger than the viewport size, consider
1528 // only a portion that's as large as the viewport, centering on
1529 // the subfocus as much as possible.
1530 if (object_max - object_min > viewport_size) {
1531 // Since it's impossible to fit the whole object in the
1532 // viewport, exit now if the subfocus is already within the viewport.
1533 if (subfocus_min - current_scroll_offset >= viewport_min &&
1534 subfocus_max - current_scroll_offset <= viewport_max)
1535 return current_scroll_offset;
1536
1537 // Subfocus must be within focus.
1538 subfocus_min = std::max(subfocus_min, object_min);
1539 subfocus_max = std::min(subfocus_max, object_max);
1540
1541 // Subfocus must be no larger than the viewport size; favor top/left.
1542 if (subfocus_max - subfocus_min > viewport_size)
1543 subfocus_max = subfocus_min + viewport_size;
1544
1545 // Compute the size of an object centered on the subfocus, the size of the
1546 // viewport.
1547 int centered_object_min = (subfocus_min + subfocus_max - viewport_size) / 2;
1548 int centered_object_max = centered_object_min + viewport_size;
1549
1550 object_min = std::max(object_min, centered_object_min);
1551 object_max = std::min(object_max, centered_object_max);
1552 }
1553
1554 // Exit now if the focus is already within the viewport.
1555 if (object_min - current_scroll_offset >= viewport_min &&
1556 object_max - current_scroll_offset <= viewport_max)
1557 return current_scroll_offset;
1558
1559 // Center the object in the viewport.
1560 return (object_min + object_max - viewport_min - viewport_max) / 2;
1561 }
1562
1563 void AXObjectImpl::ScrollToMakeVisibleWithSubFocus(
1564 const IntRect& subfocus) const {
1565 // Search up the parent chain until we find the first one that's scrollable.
1566 const AXObjectImpl* scroll_parent = ParentObject() ? ParentObject() : this;
1567 ScrollableArea* scrollable_area = 0;
1568 while (scroll_parent) {
1569 scrollable_area = scroll_parent->GetScrollableAreaIfScrollable();
1570 if (scrollable_area)
1571 break;
1572 scroll_parent = scroll_parent->ParentObject();
1573 }
1574 if (!scroll_parent || !scrollable_area)
1575 return;
1576
1577 IntRect object_rect = PixelSnappedIntRect(GetBoundsInFrameCoordinates());
1578 IntSize scroll_offset = scrollable_area->ScrollOffsetInt();
1579 IntRect scroll_visible_rect = scrollable_area->VisibleContentRect();
1580
1581 // Convert the object rect into local coordinates.
1582 if (!scroll_parent->IsWebArea()) {
1583 object_rect.MoveBy(IntPoint(scroll_offset));
1584 object_rect.MoveBy(
1585 -PixelSnappedIntRect(scroll_parent->GetBoundsInFrameCoordinates())
1586 .Location());
1587 }
1588
1589 int desired_x = ComputeBestScrollOffset(
1590 scroll_offset.Width(), object_rect.X() + subfocus.X(),
1591 object_rect.X() + subfocus.MaxX(), object_rect.X(), object_rect.MaxX(), 0,
1592 scroll_visible_rect.Width());
1593 int desired_y = ComputeBestScrollOffset(
1594 scroll_offset.Height(), object_rect.Y() + subfocus.Y(),
1595 object_rect.Y() + subfocus.MaxY(), object_rect.Y(), object_rect.MaxY(), 0,
1596 scroll_visible_rect.Height());
1597
1598 scroll_parent->SetScrollOffset(IntPoint(desired_x, desired_y));
1599
1600 // Convert the subfocus into the coordinates of the scroll parent.
1601 IntRect new_subfocus = subfocus;
1602 IntRect new_element_rect = PixelSnappedIntRect(GetBoundsInFrameCoordinates());
1603 IntRect scroll_parent_rect =
1604 PixelSnappedIntRect(scroll_parent->GetBoundsInFrameCoordinates());
1605 new_subfocus.Move(new_element_rect.X(), new_element_rect.Y());
1606 new_subfocus.Move(-scroll_parent_rect.X(), -scroll_parent_rect.Y());
1607
1608 if (scroll_parent->ParentObject()) {
1609 // Recursively make sure the scroll parent itself is visible.
1610 scroll_parent->ScrollToMakeVisibleWithSubFocus(new_subfocus);
1611 } else {
1612 // To minimize the number of notifications, only fire one on the topmost
1613 // object that has been scrolled.
1614 AxObjectCache().PostNotification(const_cast<AXObjectImpl*>(this),
1615 AXObjectCacheImpl::kAXLocationChanged);
1616 }
1617 }
1618
1619 void AXObjectImpl::ScrollToGlobalPoint(const IntPoint& global_point) const {
1620 // Search up the parent chain and create a vector of all scrollable parent
1621 // objects and ending with this object itself.
1622 HeapVector<Member<const AXObjectImpl>> objects;
1623 AXObjectImpl* parent_object;
1624 for (parent_object = this->ParentObject(); parent_object;
1625 parent_object = parent_object->ParentObject()) {
1626 if (parent_object->GetScrollableAreaIfScrollable())
1627 objects.push_front(parent_object);
1628 }
1629 objects.push_back(this);
1630
1631 // Start with the outermost scrollable (the main window) and try to scroll the
1632 // next innermost object to the given point.
1633 int offset_x = 0, offset_y = 0;
1634 IntPoint point = global_point;
1635 size_t levels = objects.size() - 1;
1636 for (size_t i = 0; i < levels; i++) {
1637 const AXObjectImpl* outer = objects[i];
1638 const AXObjectImpl* inner = objects[i + 1];
1639 ScrollableArea* scrollable_area = outer->GetScrollableAreaIfScrollable();
1640
1641 IntRect inner_rect =
1642 inner->IsWebArea()
1643 ? PixelSnappedIntRect(
1644 inner->ParentObject()->GetBoundsInFrameCoordinates())
1645 : PixelSnappedIntRect(inner->GetBoundsInFrameCoordinates());
1646 IntRect object_rect = inner_rect;
1647 IntSize scroll_offset = scrollable_area->ScrollOffsetInt();
1648
1649 // Convert the object rect into local coordinates.
1650 object_rect.Move(offset_x, offset_y);
1651 if (!outer->IsWebArea())
1652 object_rect.Move(scroll_offset.Width(), scroll_offset.Height());
1653
1654 int desired_x = ComputeBestScrollOffset(
1655 0, object_rect.X(), object_rect.MaxX(), object_rect.X(),
1656 object_rect.MaxX(), point.X(), point.X());
1657 int desired_y = ComputeBestScrollOffset(
1658 0, object_rect.Y(), object_rect.MaxY(), object_rect.Y(),
1659 object_rect.MaxY(), point.Y(), point.Y());
1660 outer->SetScrollOffset(IntPoint(desired_x, desired_y));
1661
1662 if (outer->IsWebArea() && !inner->IsWebArea()) {
1663 // If outer object we just scrolled is a web area (frame) but the inner
1664 // object is not, keep track of the coordinate transformation to apply to
1665 // future nested calculations.
1666 scroll_offset = scrollable_area->ScrollOffsetInt();
1667 offset_x -= (scroll_offset.Width() + point.X());
1668 offset_y -= (scroll_offset.Height() + point.Y());
1669 point.Move(scroll_offset.Width() - inner_rect.Width(),
1670 scroll_offset.Height() - inner_rect.Y());
1671 } else if (inner->IsWebArea()) {
1672 // Otherwise, if the inner object is a web area, reset the coordinate
1673 // transformation.
1674 offset_x = 0;
1675 offset_y = 0;
1676 }
1677 }
1678
1679 // To minimize the number of notifications, only fire one on the topmost
1680 // object that has been scrolled.
1681 DCHECK(objects[0]);
1682 // TODO(nektar): Switch to postNotification(objects[0] and remove |getNode|.
1683 AxObjectCache().PostNotification(objects[0]->GetNode(),
1684 AXObjectCacheImpl::kAXLocationChanged);
1685 }
1686
1687 void AXObjectImpl::SetSequentialFocusNavigationStartingPoint() {
1688 // Call it on the nearest ancestor that overrides this with a specific
1689 // implementation.
1690 if (ParentObject())
1691 ParentObject()->SetSequentialFocusNavigationStartingPoint();
1692 }
1693
1694 void AXObjectImpl::NotifyIfIgnoredValueChanged() {
1695 bool is_ignored = AccessibilityIsIgnored();
1696 if (LastKnownIsIgnoredValue() != is_ignored) {
1697 AxObjectCache().ChildrenChanged(ParentObject());
1698 SetLastKnownIsIgnoredValue(is_ignored);
1699 }
1700 }
1701
1702 void AXObjectImpl::SelectionChanged() {
1703 if (AXObjectImpl* parent = ParentObjectIfExists())
1704 parent->SelectionChanged();
1705 }
1706
1707 int AXObjectImpl::LineForPosition(const VisiblePosition& position) const {
1708 if (position.IsNull() || !GetNode())
1709 return -1;
1710
1711 // If the position is not in the same editable region as this AX object,
1712 // return -1.
1713 Node* container_node = position.DeepEquivalent().ComputeContainerNode();
1714 if (!container_node->IsShadowIncludingInclusiveAncestorOf(GetNode()) &&
1715 !GetNode()->IsShadowIncludingInclusiveAncestorOf(container_node))
1716 return -1;
1717
1718 int line_count = -1;
1719 VisiblePosition current_position = position;
1720 VisiblePosition previous_position;
1721
1722 // Move up until we get to the top.
1723 // FIXME: This only takes us to the top of the rootEditableElement, not the
1724 // top of the top document.
1725 do {
1726 previous_position = current_position;
1727 current_position = PreviousLinePosition(current_position, LayoutUnit(),
1728 kHasEditableAXRole);
1729 ++line_count;
1730 } while (current_position.IsNotNull() &&
1731 !InSameLine(current_position, previous_position));
1732
1733 return line_count;
1734 }
1735
1736 bool AXObjectImpl::IsARIAControl(AccessibilityRole aria_role) {
1737 return IsARIAInput(aria_role) || aria_role == kButtonRole ||
1738 aria_role == kComboBoxRole || aria_role == kSliderRole;
1739 }
1740
1741 bool AXObjectImpl::IsARIAInput(AccessibilityRole aria_role) {
1742 return aria_role == kRadioButtonRole || aria_role == kCheckBoxRole ||
1743 aria_role == kTextFieldRole || aria_role == kSwitchRole ||
1744 aria_role == kSearchBoxRole;
1745 }
1746
1747 AccessibilityRole AXObjectImpl::AriaRoleToWebCoreRole(const String& value) {
1748 DCHECK(!value.IsEmpty());
1749
1750 static const ARIARoleMap* role_map = CreateARIARoleMap();
1751
1752 Vector<String> role_vector;
1753 value.Split(' ', role_vector);
1754 AccessibilityRole role = kUnknownRole;
1755 for (const auto& child : role_vector) {
1756 role = role_map->at(child);
1757 if (role)
1758 return role;
1759 }
1760
1761 return role;
1762 }
1763
1764 bool AXObjectImpl::IsInsideFocusableElementOrARIAWidget(const Node& node) {
1765 const Node* cur_node = &node;
1766 do {
1767 if (cur_node->IsElementNode()) {
1768 const Element* element = ToElement(cur_node);
1769 if (element->IsFocusable())
1770 return true;
1771 String role = element->getAttribute("role");
1772 if (!role.IsEmpty() && AXObjectImpl::IncludesARIAWidgetRole(role))
1773 return true;
1774 if (HasInteractiveARIAAttribute(*element))
1775 return true;
1776 }
1777 cur_node = cur_node->parentNode();
1778 } while (cur_node && !isHTMLBodyElement(node));
1779 return false;
1780 }
1781
1782 bool AXObjectImpl::HasInteractiveARIAAttribute(const Element& element) {
1783 for (size_t i = 0; i < WTF_ARRAY_LENGTH(g_aria_interactive_widget_attributes);
1784 ++i) {
1785 const char* attribute = g_aria_interactive_widget_attributes[i];
1786 if (element.hasAttribute(attribute)) {
1787 return true;
1788 }
1789 }
1790 return false;
1791 }
1792
1793 bool AXObjectImpl::IncludesARIAWidgetRole(const String& role) {
1794 static const HashSet<String, CaseFoldingHash>* role_set =
1795 CreateARIARoleWidgetSet();
1796
1797 Vector<String> role_vector;
1798 role.Split(' ', role_vector);
1799 for (const auto& child : role_vector) {
1800 if (role_set->Contains(child))
1801 return true;
1802 }
1803 return false;
1804 }
1805
1806 bool AXObjectImpl::NameFromContents() const {
1807 // ARIA 1.1, section 5.2.7.5.
1808 switch (RoleValue()) {
1809 case kAnchorRole:
1810 case kButtonRole:
1811 case kCellRole:
1812 case kCheckBoxRole:
1813 case kColumnHeaderRole:
1814 case kDirectoryRole:
1815 case kDisclosureTriangleRole:
1816 case kHeadingRole:
1817 case kLineBreakRole:
1818 case kLinkRole:
1819 case kListBoxOptionRole:
1820 case kListItemRole:
1821 case kMenuItemRole:
1822 case kMenuItemCheckBoxRole:
1823 case kMenuItemRadioRole:
1824 case kMenuListOptionRole:
1825 case kPopUpButtonRole:
1826 case kRadioButtonRole:
1827 case kRowHeaderRole:
1828 case kStaticTextRole:
1829 case kStatusRole:
1830 case kSwitchRole:
1831 case kTabRole:
1832 case kToggleButtonRole:
1833 case kTreeItemRole:
1834 case kUserInterfaceTooltipRole:
1835 return true;
1836 case kRowRole: {
1837 // Spec says we should always expose the name on rows,
1838 // but for performance reasons we only do it
1839 // if the row might receive focus
1840 if (AncestorExposesActiveDescendant()) {
1841 return true;
1842 }
1843 const Node* node = this->GetNode();
1844 return node && node->IsElementNode() && ToElement(node)->IsFocusable();
1845 }
1846 default:
1847 return false;
1848 }
1849 }
1850
1851 AccessibilityRole AXObjectImpl::ButtonRoleType() const {
1852 // If aria-pressed is present, then it should be exposed as a toggle button.
1853 // http://www.w3.org/TR/wai-aria/states_and_properties#aria-pressed
1854 if (AriaPressedIsPresent())
1855 return kToggleButtonRole;
1856 if (AriaHasPopup())
1857 return kPopUpButtonRole;
1858 // We don't contemplate RadioButtonRole, as it depends on the input
1859 // type.
1860
1861 return kButtonRole;
1862 }
1863
1864 const AtomicString& AXObjectImpl::RoleName(AccessibilityRole role) {
1865 static const Vector<AtomicString>* role_name_vector = CreateRoleNameVector();
1866
1867 return role_name_vector->at(role);
1868 }
1869
1870 const AtomicString& AXObjectImpl::InternalRoleName(AccessibilityRole role) {
1871 static const Vector<AtomicString>* internal_role_name_vector =
1872 CreateInternalRoleNameVector();
1873
1874 return internal_role_name_vector->at(role);
1875 }
1876
1877 DEFINE_TRACE(AXObjectImpl) {
1878 visitor->Trace(children_);
1879 visitor->Trace(parent_);
1880 visitor->Trace(cached_live_region_root_);
1881 visitor->Trace(ax_object_cache_);
1882 }
1883
1884 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698