Index: ui/accessibility/platform/ax_platform_node_win.cc |
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc |
index 5cb972b0b43322fdd3638b7c531f0cee1e105880..b62e4184c94686053575e33c5391cd112d2ee6b4 100644 |
--- a/ui/accessibility/platform/ax_platform_node_win.cc |
+++ b/ui/accessibility/platform/ax_platform_node_win.cc |
@@ -1506,43 +1506,162 @@ std::string AXPlatformNodeWin::StringOverrideForMSAARole() { |
return ""; |
} |
+bool AXPlatformNodeWin::ShouldNodeHaveReadonlyState( |
+ const AXNodeData& data) const { |
+ if (data.GetBoolAttribute(ui::AX_ATTR_ARIA_READONLY)) |
+ return true; |
+ |
+ if (!data.HasState(ui::AX_STATE_READ_ONLY)) |
+ return false; |
+ |
+ switch (data.role) { |
+ case ui::AX_ROLE_ARTICLE: |
+ case ui::AX_ROLE_BUSY_INDICATOR: |
+ case ui::AX_ROLE_DEFINITION: |
+ case ui::AX_ROLE_DESCRIPTION_LIST: |
+ case ui::AX_ROLE_DESCRIPTION_LIST_TERM: |
+ case ui::AX_ROLE_DOCUMENT: |
+ case ui::AX_ROLE_IFRAME: |
+ case ui::AX_ROLE_IMAGE: |
+ case ui::AX_ROLE_IMAGE_MAP: |
+ case ui::AX_ROLE_IMAGE_MAP_LINK: |
+ case ui::AX_ROLE_LIST: |
+ case ui::AX_ROLE_LIST_ITEM: |
+ case ui::AX_ROLE_PROGRESS_INDICATOR: |
+ case ui::AX_ROLE_ROOT_WEB_AREA: |
+ case ui::AX_ROLE_RULER: |
+ case ui::AX_ROLE_SCROLL_AREA: |
+ case ui::AX_ROLE_TERM: |
+ case ui::AX_ROLE_TIMER: |
+ case ui::AX_ROLE_TOOLBAR: |
+ case ui::AX_ROLE_TOOLTIP: |
+ case ui::AX_ROLE_WEB_AREA: |
+ return true; |
+ |
+ case ui::AX_ROLE_GRID: |
+ // 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 |
+ // msaa_state |= STATE_SYSTEM_READONLY; |
+ break; |
+ |
+ case ui::AX_ROLE_TEXT_FIELD: |
+ case ui::AX_ROLE_SEARCH_BOX: |
+ if (data.HasState(ui::AX_STATE_READ_ONLY)) |
+ return true; |
+ |
+ default: |
+ break; |
+ } |
+ return false; |
+} |
+ |
+bool AXPlatformNodeWin::ShouldNodeHaveFocusableState( |
+ const AXNodeData& data) const { |
+ switch (data.role) { |
+ case ui::AX_ROLE_DOCUMENT: |
+ case ui::AX_ROLE_ROOT_WEB_AREA: |
+ case ui::AX_ROLE_WEB_AREA: |
+ return true; |
+ |
+ case ui::AX_ROLE_IFRAME: |
+ return false; |
+ |
+ case ui::AX_ROLE_LIST_BOX_OPTION: |
+ case ui::AX_ROLE_MENU_LIST_OPTION: |
+ if (data.HasState(ui::AX_STATE_SELECTABLE)) |
+ return true; |
+ |
+ default: |
+ break; |
+ } |
+ |
+ return data.HasState(ui::AX_STATE_FOCUSABLE); |
+} |
+ |
int AXPlatformNodeWin::MSAAState() { |
const AXNodeData& data = GetData(); |
- const uint32_t state = data.state; |
- |
int msaa_state = 0; |
- if (state & (1 << ui::AX_STATE_COLLAPSED)) |
+ |
+ // Map the AXState to MSAA state. Note that some of the states are not |
+ // currently handled. |
+ |
+ if (data.HasState(ui::AX_STATE_BUSY)) |
+ msaa_state |= STATE_SYSTEM_BUSY; |
+ |
+ if (data.HasState(ui::AX_STATE_COLLAPSED)) |
msaa_state |= STATE_SYSTEM_COLLAPSED; |
- if (state & (1 << ui::AX_STATE_DEFAULT)) |
+ |
+ if (data.HasState(ui::AX_STATE_DEFAULT)) |
msaa_state |= STATE_SYSTEM_DEFAULT; |
- if (state & (1 << ui::AX_STATE_EXPANDED)) |
+ |
+ if (data.HasState(ui::AX_STATE_DISABLED)) |
+ msaa_state |= STATE_SYSTEM_UNAVAILABLE; |
+ |
+ // TODO(dougt) unhandled ux::AX_STATE_EDITABLE |
+ |
+ if (data.HasState(ui::AX_STATE_EXPANDED)) |
msaa_state |= STATE_SYSTEM_EXPANDED; |
- if (state & (1 << ui::AX_STATE_FOCUSABLE)) |
+ |
+ if (ShouldNodeHaveFocusableState(data)) |
msaa_state |= STATE_SYSTEM_FOCUSABLE; |
- if (state & (1 << ui::AX_STATE_HASPOPUP)) |
+ |
+ if (data.HasState(ui::AX_STATE_HASPOPUP)) |
msaa_state |= STATE_SYSTEM_HASPOPUP; |
- if (state & (1 << ui::AX_STATE_HOVERED)) |
- msaa_state |= STATE_SYSTEM_HOTTRACKED; |
- if (state & (1 << ui::AX_STATE_INVISIBLE) || |
+ |
+ // TODO(dougt) unhandled ux::AX_STATE_HORIZONTAL |
+ |
+ if (data.HasState(ui::AX_STATE_HOVERED)) { |
+ // Expose whether or not the mouse is over an element, but suppress |
+ // this for tests because it can make the test results flaky depending |
+ // on the position of the mouse. |
+ if (delegate_->ShouldIgnoreHoveredStateForTesting()) |
+ msaa_state |= STATE_SYSTEM_HOTTRACKED; |
+ } |
+ |
+ // TODO(dougt) Why do we set any state on AX_ROLE_IGNORED? |
+ if (data.HasState(ui::AX_STATE_INVISIBLE) || |
GetData().role == ui::AX_ROLE_IGNORED) { |
msaa_state |= STATE_SYSTEM_INVISIBLE; |
} |
- if (state & (1 << ui::AX_STATE_LINKED)) |
+ if (data.HasState(ui::AX_STATE_LINKED)) |
msaa_state |= STATE_SYSTEM_LINKED; |
- if (state & (1 << ui::AX_STATE_OFFSCREEN)) |
+ |
+ // TODO(dougt) unhandled ux::AX_STATE_MULTILINE |
+ |
+ if (data.HasState(ui::AX_STATE_MULTISELECTABLE)) { |
+ msaa_state |= STATE_SYSTEM_EXTSELECTABLE; |
+ msaa_state |= STATE_SYSTEM_MULTISELECTABLE; |
+ } |
+ |
+ if (data.HasState(ui::AX_STATE_OFFSCREEN)) |
msaa_state |= STATE_SYSTEM_OFFSCREEN; |
- if (state & (1 << ui::AX_STATE_PROTECTED)) |
+ |
+ if (data.HasState(ui::AX_STATE_PROTECTED)) |
msaa_state |= STATE_SYSTEM_PROTECTED; |
- if (state & (1 << ui::AX_STATE_READ_ONLY)) |
+ |
+ // READONLY state is complex on windows. We set STATE_SYSTEM_READONLY |
+ // on *some* roles even if the node data isn't marked as AX_STATE_READ_ONLY. |
+ if (ShouldNodeHaveReadonlyState(data)) |
msaa_state |= STATE_SYSTEM_READONLY; |
- if (state & (1 << ui::AX_STATE_SELECTABLE)) |
+ |
+ // TODO(dougt) unhandled ux::AX_STATE_REQUIRED |
+ // TODO(dougt) unhandled ux::AX_STATE_RICHLY_EDITABLE |
+ |
+ if (data.HasState(ui::AX_STATE_SELECTABLE)) |
msaa_state |= STATE_SYSTEM_SELECTABLE; |
- if (state & (1 << ui::AX_STATE_SELECTED)) |
+ |
+ if (data.HasState(ui::AX_STATE_SELECTED)) |
msaa_state |= STATE_SYSTEM_SELECTED; |
- if (state & (1 << ui::AX_STATE_DISABLED)) |
- msaa_state |= STATE_SYSTEM_UNAVAILABLE; |
+ // TODO(dougt) unhandled VERTICAL |
+ |
+ if (data.HasState(ui::AX_STATE_VISITED)) |
+ msaa_state |= STATE_SYSTEM_TRAVERSED; |
+ |
+ // |
// Checked state |
+ // |
const auto checked_state = static_cast<ui::AXCheckedState>( |
GetIntAttribute(ui::AX_ATTR_CHECKED_STATE)); |
switch (checked_state) { |
@@ -1558,6 +1677,9 @@ int AXPlatformNodeWin::MSAAState() { |
break; |
} |
+ // |
+ // Handle STATE_SYSTEM_FOCUSED |
+ // |
gfx::NativeViewAccessible focus = delegate_->GetFocus(); |
if (focus == GetNativeViewAccessible()) |
msaa_state |= STATE_SYSTEM_FOCUSED; |
@@ -1569,10 +1691,16 @@ int AXPlatformNodeWin::MSAAState() { |
// the menu bar, but we don't currently track focus inside menu pop-ups, |
// and Chrome only has one menu visible at a time so this works for now. |
if (data.role == ui::AX_ROLE_MENU_BAR && |
- !(state & (1 << ui::AX_STATE_INVISIBLE))) { |
+ !(data.HasState(ui::AX_STATE_INVISIBLE))) { |
msaa_state |= STATE_SYSTEM_FOCUSED; |
} |
+ // Handle STATE_SYSTEM_LINKED |
+ if (GetData().role == ui::AX_ROLE_IMAGE_MAP_LINK || |
+ GetData().role == ui::AX_ROLE_LINK) { |
+ msaa_state |= STATE_SYSTEM_LINKED; |
+ } |
+ |
return msaa_state; |
} |