blob: 1cb85d62c640b1a3442e94aa2de29219e17bc468 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/ax_role_properties.h"
#include "build/build_config.h"
namespace ui {
namespace {
#if defined(OS_WIN) || defined(OS_CHROMEOS)
constexpr bool kExposeLayoutTableAsDataTable = true;
#else
constexpr bool kExposeLayoutTableAsDataTable = false;
#endif // defined(OS_WIN)
} // namespace
bool IsAlert(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kAlert:
case ax::mojom::Role::kAlertDialog:
return true;
default:
return false;
}
}
bool IsClickable(const AXNodeData& data) {
// If it has a custom default action verb except for
// ax::mojom::DefaultActionVerb::kClickAncestor, it's definitely clickable.
// ax::mojom::DefaultActionVerb::kClickAncestor is used when an element with a
// click listener is present in its ancestry chain.
if (data.HasIntAttribute(ax::mojom::IntAttribute::kDefaultActionVerb) &&
(data.GetDefaultActionVerb() !=
ax::mojom::DefaultActionVerb::kClickAncestor))
return true;
switch (data.role) {
case ax::mojom::Role::kButton:
case ax::mojom::Role::kCheckBox:
case ax::mojom::Role::kColorWell:
case ax::mojom::Role::kDisclosureTriangle:
case ax::mojom::Role::kDocBackLink:
case ax::mojom::Role::kDocBiblioRef:
case ax::mojom::Role::kDocGlossRef:
case ax::mojom::Role::kDocNoteRef:
case ax::mojom::Role::kLink:
case ax::mojom::Role::kListBoxOption:
case ax::mojom::Role::kMenuButton:
case ax::mojom::Role::kMenuItem:
case ax::mojom::Role::kMenuItemCheckBox:
case ax::mojom::Role::kMenuItemRadio:
case ax::mojom::Role::kMenuListOption:
case ax::mojom::Role::kMenuListPopup:
case ax::mojom::Role::kPopUpButton:
case ax::mojom::Role::kRadioButton:
case ax::mojom::Role::kSwitch:
case ax::mojom::Role::kTab:
case ax::mojom::Role::kToggleButton:
return true;
default:
return false;
}
}
bool IsCellOrTableHeader(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kCell:
case ax::mojom::Role::kColumnHeader:
case ax::mojom::Role::kRowHeader:
return true;
case ax::mojom::Role::kLayoutTableCell:
return kExposeLayoutTableAsDataTable;
default:
return false;
}
}
bool IsContainerWithSelectableChildren(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kComboBoxGrouping:
case ax::mojom::Role::kComboBoxMenuButton:
case ax::mojom::Role::kGrid:
case ax::mojom::Role::kListBox:
case ax::mojom::Role::kListGrid:
case ax::mojom::Role::kMenu:
case ax::mojom::Role::kMenuBar:
case ax::mojom::Role::kMenuListPopup:
case ax::mojom::Role::kRadioGroup:
case ax::mojom::Role::kTabList:
case ax::mojom::Role::kToolbar:
case ax::mojom::Role::kTree:
case ax::mojom::Role::kTreeGrid:
return true;
default:
return false;
}
}
bool IsControl(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kButton:
case ax::mojom::Role::kCheckBox:
case ax::mojom::Role::kColorWell:
case ax::mojom::Role::kComboBoxMenuButton:
case ax::mojom::Role::kDisclosureTriangle:
case ax::mojom::Role::kListBox:
case ax::mojom::Role::kListGrid:
case ax::mojom::Role::kMenu:
case ax::mojom::Role::kMenuBar:
case ax::mojom::Role::kMenuButton:
case ax::mojom::Role::kMenuItem:
case ax::mojom::Role::kMenuItemCheckBox:
case ax::mojom::Role::kMenuItemRadio:
case ax::mojom::Role::kMenuListOption:
case ax::mojom::Role::kMenuListPopup:
case ax::mojom::Role::kPopUpButton:
case ax::mojom::Role::kRadioButton:
case ax::mojom::Role::kScrollBar:
case ax::mojom::Role::kSearchBox:
case ax::mojom::Role::kSlider:
case ax::mojom::Role::kSpinButton:
case ax::mojom::Role::kSwitch:
case ax::mojom::Role::kTab:
case ax::mojom::Role::kTextField:
case ax::mojom::Role::kTextFieldWithComboBox:
case ax::mojom::Role::kToggleButton:
case ax::mojom::Role::kTree:
return true;
default:
return false;
}
}
bool IsDocument(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kRootWebArea:
case ax::mojom::Role::kWebArea:
return true;
default:
return false;
}
}
bool IsHeading(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kHeading:
case ax::mojom::Role::kDocSubtitle:
return true;
default:
return false;
}
}
bool IsHeadingOrTableHeader(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kColumnHeader:
case ax::mojom::Role::kDocSubtitle:
case ax::mojom::Role::kHeading:
case ax::mojom::Role::kRowHeader:
return true;
default:
return false;
}
}
bool IsImage(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kCanvas:
case ax::mojom::Role::kDocCover:
case ax::mojom::Role::kGraphicsSymbol:
case ax::mojom::Role::kImage:
case ax::mojom::Role::kImageMap:
case ax::mojom::Role::kSvgRoot:
case ax::mojom::Role::kVideo:
return true;
default:
return false;
}
}
bool IsInvokable(const AXNodeData& data) {
// A control is "invokable" if it initiates an action when activated but
// does not maintain any state. A control that maintains state when activated
// would be considered a toggle or expand-collapse element - these elements
// are "clickable" but not "invokable".
return IsClickable(data) && !SupportsExpandCollapse(data) &&
!SupportsToggle(data.role);
}
bool IsItemLike(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kArticle:
case ax::mojom::Role::kListItem:
case ax::mojom::Role::kMenuItem:
case ax::mojom::Role::kMenuItemRadio:
case ax::mojom::Role::kTab:
case ax::mojom::Role::kMenuItemCheckBox:
case ax::mojom::Role::kTreeItem:
case ax::mojom::Role::kListBoxOption:
case ax::mojom::Role::kMenuListOption:
case ax::mojom::Role::kRadioButton:
case ax::mojom::Role::kDescriptionListTerm:
case ax::mojom::Role::kTerm:
return true;
default:
return false;
}
}
bool IsLink(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kDocBackLink:
case ax::mojom::Role::kDocBiblioRef:
case ax::mojom::Role::kDocGlossRef:
case ax::mojom::Role::kDocNoteRef:
case ax::mojom::Role::kLink:
return true;
default:
return false;
}
}
bool IsList(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kDescriptionList:
case ax::mojom::Role::kDirectory:
case ax::mojom::Role::kDocBibliography:
case ax::mojom::Role::kList:
case ax::mojom::Role::kListBox:
case ax::mojom::Role::kListGrid:
return true;
default:
return false;
}
}
bool IsListItem(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kDescriptionListTerm:
case ax::mojom::Role::kDocBiblioEntry:
case ax::mojom::Role::kDocEndnote:
case ax::mojom::Role::kListBoxOption:
case ax::mojom::Role::kListItem:
case ax::mojom::Role::kTerm:
return true;
default:
return false;
}
}
bool IsMenuItem(ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kMenuItem:
case ax::mojom::Role::kMenuItemCheckBox:
case ax::mojom::Role::kMenuItemRadio:
return true;
default:
return false;
}
}
bool IsMenuRelated(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kMenu:
case ax::mojom::Role::kMenuBar:
case ax::mojom::Role::kMenuButton:
case ax::mojom::Role::kMenuItem:
case ax::mojom::Role::kMenuItemCheckBox:
case ax::mojom::Role::kMenuItemRadio:
case ax::mojom::Role::kMenuListOption:
case ax::mojom::Role::kMenuListPopup:
return true;
default:
return false;
}
}
bool IsRangeValueSupported(const AXNodeData& data) {
// https://www.w3.org/TR/wai-aria-1.1/#aria-valuenow
// https://www.w3.org/TR/wai-aria-1.1/#aria-valuetext
// Roles that support aria-valuetext / aria-valuenow
switch (data.role) {
case ax::mojom::Role::kMeter:
case ax::mojom::Role::kProgressIndicator:
case ax::mojom::Role::kScrollBar:
case ax::mojom::Role::kSlider:
case ax::mojom::Role::kSpinButton:
return true;
case ax::mojom::Role::kSplitter:
return data.HasState(ax::mojom::State::kFocusable);
default:
return false;
}
}
bool IsRowContainer(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kGrid:
case ax::mojom::Role::kListGrid:
case ax::mojom::Role::kTable:
case ax::mojom::Role::kTree:
case ax::mojom::Role::kTreeGrid:
return true;
case ax::mojom::Role::kLayoutTable:
return kExposeLayoutTableAsDataTable;
default:
return false;
}
}
bool IsSetLike(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kDescriptionList:
case ax::mojom::Role::kDirectory:
case ax::mojom::Role::kDocBibliography:
case ax::mojom::Role::kFeed:
case ax::mojom::Role::kGroup:
case ax::mojom::Role::kList:
case ax::mojom::Role::kListBox:
case ax::mojom::Role::kListGrid:
case ax::mojom::Role::kMenu:
case ax::mojom::Role::kMenuBar:
case ax::mojom::Role::kMenuListPopup:
case ax::mojom::Role::kRadioGroup:
case ax::mojom::Role::kTabList:
case ax::mojom::Role::kTree:
case ax::mojom::Role::kPopUpButton:
return true;
default:
return false;
}
}
bool IsStaticList(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kList:
case ax::mojom::Role::kDescriptionList:
return true;
default:
return false;
}
}
bool IsTableColumn(ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kColumn:
return true;
case ax::mojom::Role::kLayoutTableColumn:
return kExposeLayoutTableAsDataTable;
default:
return false;
}
}
bool IsTableHeader(ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kColumnHeader:
case ax::mojom::Role::kRowHeader:
return true;
default:
return false;
}
}
bool IsTableLike(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kGrid:
case ax::mojom::Role::kListGrid:
case ax::mojom::Role::kTable:
case ax::mojom::Role::kTreeGrid:
return true;
case ax::mojom::Role::kLayoutTable:
return kExposeLayoutTableAsDataTable;
default:
return false;
}
}
bool IsTableRow(ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kRow:
return true;
case ax::mojom::Role::kLayoutTableRow:
return kExposeLayoutTableAsDataTable;
default:
return false;
}
}
bool IsTextOrLineBreak(ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kLineBreak:
case ax::mojom::Role::kStaticText:
return true;
default:
return false;
}
}
bool IsReadOnlySupported(const ax::mojom::Role role) {
// https://www.w3.org/TR/wai-aria-1.1/#aria-readonly
// Roles that support aria-readonly
switch (role) {
case ax::mojom::Role::kCheckBox:
case ax::mojom::Role::kComboBoxGrouping:
case ax::mojom::Role::kComboBoxMenuButton:
case ax::mojom::Role::kGrid:
case ax::mojom::Role::kListBox:
case ax::mojom::Role::kMenuItemCheckBox:
case ax::mojom::Role::kMenuItemRadio:
case ax::mojom::Role::kMenuListPopup:
case ax::mojom::Role::kPopUpButton:
case ax::mojom::Role::kRadioButton:
case ax::mojom::Role::kRadioGroup:
case ax::mojom::Role::kSearchBox:
case ax::mojom::Role::kSlider:
case ax::mojom::Role::kSpinButton:
case ax::mojom::Role::kSwitch:
case ax::mojom::Role::kTextField:
case ax::mojom::Role::kTextFieldWithComboBox:
case ax::mojom::Role::kTreeGrid:
return true;
// https://www.w3.org/TR/wai-aria-1.1/#aria-readonly
// ARIA-1.1+ 'gridcell', supports aria-readonly, but 'cell' does not
//
// https://www.w3.org/TR/wai-aria-1.1/#columnheader
// https://www.w3.org/TR/wai-aria-1.1/#rowheader
// While the [columnheader|rowheader] role can be used in both interactive
// grids and non-interactive tables, the use of aria-readonly and
// aria-required is only applicable to interactive elements.
// Therefore, [...] user agents SHOULD NOT expose either property to
// assistive technologies unless the columnheader descends from a grid.
case ax::mojom::Role::kCell:
case ax::mojom::Role::kRowHeader:
case ax::mojom::Role::kColumnHeader:
return false;
default:
break;
}
return false;
}
bool SupportsExpandCollapse(const AXNodeData& data) {
if (data.GetHasPopup() != ax::mojom::HasPopup::kFalse ||
data.HasState(ax::mojom::State::kExpanded) ||
data.HasState(ax::mojom::State::kCollapsed))
return true;
switch (data.role) {
case ax::mojom::Role::kComboBoxGrouping:
case ax::mojom::Role::kComboBoxMenuButton:
case ax::mojom::Role::kDisclosureTriangle:
case ax::mojom::Role::kTextFieldWithComboBox:
case ax::mojom::Role::kTreeItem:
return true;
default:
return false;
}
}
bool SupportsOrientation(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kComboBoxGrouping:
case ax::mojom::Role::kComboBoxMenuButton:
case ax::mojom::Role::kListBox:
case ax::mojom::Role::kMenu:
case ax::mojom::Role::kMenuBar:
case ax::mojom::Role::kRadioGroup:
case ax::mojom::Role::kScrollBar:
case ax::mojom::Role::kSlider:
case ax::mojom::Role::kSplitter:
case ax::mojom::Role::kTabList:
case ax::mojom::Role::kToolbar:
case ax::mojom::Role::kTreeGrid:
case ax::mojom::Role::kTree:
return true;
default:
return false;
}
}
bool SupportsToggle(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kCheckBox:
case ax::mojom::Role::kMenuItemCheckBox:
case ax::mojom::Role::kSwitch:
case ax::mojom::Role::kToggleButton:
return true;
default:
return false;
}
}
bool ShouldHaveReadonlyStateByDefault(const ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kArticle:
case ax::mojom::Role::kDefinition:
case ax::mojom::Role::kDescriptionList:
case ax::mojom::Role::kDescriptionListTerm:
case ax::mojom::Role::kDocument:
case ax::mojom::Role::kGraphicsDocument:
case ax::mojom::Role::kImage:
case ax::mojom::Role::kImageMap:
case ax::mojom::Role::kList:
case ax::mojom::Role::kListItem:
case ax::mojom::Role::kProgressIndicator:
case ax::mojom::Role::kRootWebArea:
case ax::mojom::Role::kTerm:
case ax::mojom::Role::kTimer:
case ax::mojom::Role::kToolbar:
case ax::mojom::Role::kTooltip:
case ax::mojom::Role::kWebArea:
return true;
case ax::mojom::Role::kGrid:
// TODO(aleventhal) this changed between ARIA 1.0 and 1.1,
// need to determine whether grids/treegrids should really be readonly
// or editable by default
break;
default:
break;
}
return false;
}
} // namespace ui