Commit a98e3c6f authored by dmazzoni@chromium.org's avatar dmazzoni@chromium.org

Implement accessible states and notifications for the tab strip.

This change just exposes information about what tabs are selected
and fires proper notifications when tab selection changes. This will
allow screen readers to announce when switching tabs even if the
web content doesn't have focus. It's also a good first step towards
making the tab strip fully accessible too (e.g. making it possible to
perform operations on multiple tabs with just the keyboard).

As part of this change I renamed the "selection changed" event to
"text selection changed" so that there's no confusion between the
text-selection-related events, and tab/list selection events.

BUG=100412

Review URL: https://codereview.chromium.org/349033010

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282396 0039d316-1c4b-4281-b951-d872f2087c98
parent 9b034f7b
...@@ -113,7 +113,7 @@ void ExtensionAccessibilityEventRouter::HandleControlEvent( ...@@ -113,7 +113,7 @@ void ExtensionAccessibilityEventRouter::HandleControlEvent(
switch (event) { switch (event) {
case ui::AX_EVENT_TEXT_CHANGED: case ui::AX_EVENT_TEXT_CHANGED:
case ui::AX_EVENT_SELECTION_CHANGED: case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
OnTextChanged(info); OnTextChanged(info);
break; break;
case ui::AX_EVENT_VALUE_CHANGED: case ui::AX_EVENT_VALUE_CHANGED:
......
...@@ -49,7 +49,7 @@ void AccessibilityEventRouterViews::HandleAccessibilityEvent( ...@@ -49,7 +49,7 @@ void AccessibilityEventRouterViews::HandleAccessibilityEvent(
} }
if (event_type == ui::AX_EVENT_TEXT_CHANGED || if (event_type == ui::AX_EVENT_TEXT_CHANGED ||
event_type == ui::AX_EVENT_SELECTION_CHANGED) { event_type == ui::AX_EVENT_TEXT_SELECTION_CHANGED) {
// These two events should only be sent for views that have focus. This // These two events should only be sent for views that have focus. This
// enforces the invariant that we fire events triggered by user action and // enforces the invariant that we fire events triggered by user action and
// not by programmatic logic. For example, the location bar can be updated // not by programmatic logic. For example, the location bar can be updated
......
...@@ -1015,6 +1015,10 @@ void Tab::OnGestureEvent(ui::GestureEvent* event) { ...@@ -1015,6 +1015,10 @@ void Tab::OnGestureEvent(ui::GestureEvent* event) {
void Tab::GetAccessibleState(ui::AXViewState* state) { void Tab::GetAccessibleState(ui::AXViewState* state) {
state->role = ui::AX_ROLE_TAB; state->role = ui::AX_ROLE_TAB;
state->name = data_.title; state->name = data_.title;
state->AddStateFlag(ui::AX_STATE_MULTISELECTABLE);
state->AddStateFlag(ui::AX_STATE_SELECTABLE);
if (IsSelected())
state->AddStateFlag(ui::AX_STATE_SELECTED);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
......
...@@ -824,12 +824,30 @@ void TabStrip::SetSelection(const ui::ListSelectionModel& old_selection, ...@@ -824,12 +824,30 @@ void TabStrip::SetSelection(const ui::ListSelectionModel& old_selection,
} }
} }
// Use STLSetDifference to get the indices of elements newly selected
// and no longer selected, since selected_indices() is always sorted.
ui::ListSelectionModel::SelectedIndices no_longer_selected = ui::ListSelectionModel::SelectedIndices no_longer_selected =
base::STLSetDifference<ui::ListSelectionModel::SelectedIndices>( base::STLSetDifference<ui::ListSelectionModel::SelectedIndices>(
old_selection.selected_indices(), old_selection.selected_indices(),
new_selection.selected_indices()); new_selection.selected_indices());
for (size_t i = 0; i < no_longer_selected.size(); ++i) ui::ListSelectionModel::SelectedIndices newly_selected =
base::STLSetDifference<ui::ListSelectionModel::SelectedIndices>(
new_selection.selected_indices(),
old_selection.selected_indices());
// Fire accessibility events that reflect the changes to selection, and
// stop the mini tab title animation on tabs no longer selected.
for (size_t i = 0; i < no_longer_selected.size(); ++i) {
tab_at(no_longer_selected[i])->StopMiniTabTitleAnimation(); tab_at(no_longer_selected[i])->StopMiniTabTitleAnimation();
tab_at(no_longer_selected[i])->NotifyAccessibilityEvent(
ui::AX_EVENT_SELECTION_REMOVE, true);
}
for (size_t i = 0; i < newly_selected.size(); ++i) {
tab_at(newly_selected[i])->NotifyAccessibilityEvent(
ui::AX_EVENT_SELECTION_ADD, true);
}
tab_at(new_selection.active())->NotifyAccessibilityEvent(
ui::AX_EVENT_SELECTION, true);
} }
void TabStrip::TabTitleChangedNotLoading(int model_index) { void TabStrip::TabTitleChangedNotLoading(int model_index) {
......
...@@ -41,11 +41,14 @@ ...@@ -41,11 +41,14 @@
scrolledToAnchor, scrolledToAnchor,
selectedChildrenChanged, selectedChildrenChanged,
selectedTextChanged, selectedTextChanged,
selectionChanged, selection,
selectionAdd,
selectionRemove,
show, show,
textChanged, textChanged,
textInserted, textInserted,
textRemoved, textRemoved,
textSelectionChanged,
valueChanged valueChanged
}; };
......
...@@ -168,7 +168,7 @@ void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent( ...@@ -168,7 +168,7 @@ void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent(
env, android_node->GetText()).obj()); env, android_node->GetText()).obj());
break; break;
} }
case ui::AX_EVENT_SELECTED_TEXT_CHANGED: case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
Java_BrowserAccessibilityManager_handleTextSelectionChanged( Java_BrowserAccessibilityManager_handleTextSelectionChanged(
env, obj.obj(), node->GetId()); env, obj.obj(), node->GetId());
break; break;
......
...@@ -96,12 +96,6 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( ...@@ -96,12 +96,6 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent(
case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED: case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED:
// Not used on Mac. // Not used on Mac.
return; return;
case ui::AX_EVENT_SHOW:
// Not used on Mac.
return;
case ui::AX_EVENT_HIDE:
// Not used on Mac.
return;
case ui::AX_EVENT_ROW_COUNT_CHANGED: case ui::AX_EVENT_ROW_COUNT_CHANGED:
event_id = NSAccessibilityRowCountChangedNotification; event_id = NSAccessibilityRowCountChangedNotification;
break; break;
...@@ -117,15 +111,9 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( ...@@ -117,15 +111,9 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent(
case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED: case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
event_id = NSAccessibilitySelectedChildrenChangedNotification; event_id = NSAccessibilitySelectedChildrenChangedNotification;
break; break;
case ui::AX_EVENT_SELECTED_TEXT_CHANGED: case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
event_id = NSAccessibilitySelectedTextChangedNotification; event_id = NSAccessibilitySelectedTextChangedNotification;
break; break;
case ui::AX_EVENT_TEXT_INSERTED:
// Not used on Mac.
return;
case ui::AX_EVENT_TEXT_REMOVED:
// Not used on Mac.
return;
case ui::AX_EVENT_VALUE_CHANGED: case ui::AX_EVENT_VALUE_CHANGED:
event_id = NSAccessibilityValueChangedNotification; event_id = NSAccessibilityValueChangedNotification;
break; break;
......
...@@ -242,17 +242,11 @@ void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( ...@@ -242,17 +242,11 @@ void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent(
case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED: case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
event_id = EVENT_OBJECT_SELECTIONWITHIN; event_id = EVENT_OBJECT_SELECTIONWITHIN;
break; break;
case ui::AX_EVENT_SELECTED_TEXT_CHANGED:
event_id = IA2_EVENT_TEXT_CARET_MOVED;
break;
case ui::AX_EVENT_TEXT_CHANGED: case ui::AX_EVENT_TEXT_CHANGED:
event_id = EVENT_OBJECT_NAMECHANGE; event_id = EVENT_OBJECT_NAMECHANGE;
break; break;
case ui::AX_EVENT_TEXT_INSERTED: case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
event_id = IA2_EVENT_TEXT_INSERTED; event_id = IA2_EVENT_TEXT_CARET_MOVED;
break;
case ui::AX_EVENT_TEXT_REMOVED:
event_id = IA2_EVENT_TEXT_REMOVED;
break; break;
case ui::AX_EVENT_VALUE_CHANGED: case ui::AX_EVENT_VALUE_CHANGED:
event_id = EVENT_OBJECT_VALUECHANGE; event_id = EVENT_OBJECT_VALUECHANGE;
......
...@@ -325,16 +325,12 @@ ui::AXEvent AXEventFromBlink(blink::WebAXEvent event) { ...@@ -325,16 +325,12 @@ ui::AXEvent AXEventFromBlink(blink::WebAXEvent event) {
return ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED; return ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED;
case blink::WebAXEventAutocorrectionOccured: case blink::WebAXEventAutocorrectionOccured:
return ui::AX_EVENT_AUTOCORRECTION_OCCURED; return ui::AX_EVENT_AUTOCORRECTION_OCCURED;
case blink::WebAXEventBlur:
return ui::AX_EVENT_BLUR;
case blink::WebAXEventCheckedStateChanged: case blink::WebAXEventCheckedStateChanged:
return ui::AX_EVENT_CHECKED_STATE_CHANGED; return ui::AX_EVENT_CHECKED_STATE_CHANGED;
case blink::WebAXEventChildrenChanged: case blink::WebAXEventChildrenChanged:
return ui::AX_EVENT_CHILDREN_CHANGED; return ui::AX_EVENT_CHILDREN_CHANGED;
case blink::WebAXEventFocus: case blink::WebAXEventFocus:
return ui::AX_EVENT_FOCUS; return ui::AX_EVENT_FOCUS;
case blink::WebAXEventHide:
return ui::AX_EVENT_HIDE;
case blink::WebAXEventInvalidStatusChanged: case blink::WebAXEventInvalidStatusChanged:
return ui::AX_EVENT_INVALID_STATUS_CHANGED; return ui::AX_EVENT_INVALID_STATUS_CHANGED;
case blink::WebAXEventLayoutComplete: case blink::WebAXEventLayoutComplete:
...@@ -362,15 +358,9 @@ ui::AXEvent AXEventFromBlink(blink::WebAXEvent event) { ...@@ -362,15 +358,9 @@ ui::AXEvent AXEventFromBlink(blink::WebAXEvent event) {
case blink::WebAXEventSelectedChildrenChanged: case blink::WebAXEventSelectedChildrenChanged:
return ui::AX_EVENT_SELECTED_CHILDREN_CHANGED; return ui::AX_EVENT_SELECTED_CHILDREN_CHANGED;
case blink::WebAXEventSelectedTextChanged: case blink::WebAXEventSelectedTextChanged:
return ui::AX_EVENT_SELECTED_TEXT_CHANGED; return ui::AX_EVENT_TEXT_SELECTION_CHANGED;
case blink::WebAXEventShow:
return ui::AX_EVENT_SHOW;
case blink::WebAXEventTextChanged: case blink::WebAXEventTextChanged:
return ui::AX_EVENT_TEXT_CHANGED; return ui::AX_EVENT_TEXT_CHANGED;
case blink::WebAXEventTextInserted:
return ui::AX_EVENT_TEXT_INSERTED;
case blink::WebAXEventTextRemoved:
return ui::AX_EVENT_TEXT_REMOVED;
case blink::WebAXEventValueChanged: case blink::WebAXEventValueChanged:
return ui::AX_EVENT_VALUE_CHANGED; return ui::AX_EVENT_VALUE_CHANGED;
default: default:
......
...@@ -7,42 +7,55 @@ ...@@ -7,42 +7,55 @@
[camel_case_enum_to_string=true] namespace ui { [camel_case_enum_to_string=true] namespace ui {
// For new entries to the following three enums, also add to // For new entries to the following three enums, also add to
// chrome/common/extensions/apis/automation.idl. // chrome/common/extensions/api/automation.idl.
//
// Explanation of the comments next to these events:
//
// Web: this event is only used in web content. Unless a specific platform
// is specified, it fires a native event on multiple platforms.
//
// Native: this event is only used in native UI.
//
// Implicit: it would be cleaner if we just updated the AX node
// and each platform fired the appropriate events to indicate which
// platform-specific attributes changed.
//
// If unspecified, the event is used across web and native on multiple
// platforms.
enum AXEvent { enum AXEvent {
activedescendantchanged, activedescendantchanged, // Web
alert, alert,
aria_attribute_changed, aria_attribute_changed, // Implicit
autocorrection_occured, autocorrection_occured, // Unknown: http://crbug.com/392498
blur, blur, // Remove: http://crbug.com/392502
checked_state_changed, checked_state_changed, // Implicit
children_changed, children_changed,
destroyed,
focus, focus,
hide, hide, // Remove: http://crbug.com/392502
hover, hover,
invalid_status_changed, invalid_status_changed, // Implicit
layout_complete, layout_complete, // Web
live_region_changed, live_region_changed, // Web
load_complete, load_complete, // Web
location_changed, location_changed, // Web
menu_end, menu_end, // Native / Win
menu_list_item_selected, menu_list_item_selected, // Web
menu_list_value_changed, menu_list_value_changed, // Web
menu_popup_end, menu_popup_end, // Native / Win
menu_popup_start, menu_popup_start, // Native / Win
menu_start, menu_start, // Native / Win
row_collapsed, row_collapsed, // Web / Mac
row_count_changed, row_count_changed, // Web / Mac
row_expanded, row_expanded, // Web / Mac
scroll_position_changed, scroll_position_changed, // Web
scrolled_to_anchor, scrolled_to_anchor, // Web
selected_children_changed, selected_children_changed, // Web
selected_text_changed, selection, // Native
selection_changed, selection_add, // Native
show, selection_remove, // Native
show, // Remove: http://crbug.com/392502
text_changed, text_changed,
text_inserted, text_selection_changed,
text_removed,
value_changed value_changed
}; };
......
...@@ -1224,9 +1224,15 @@ int32 NativeViewAccessibilityWin::MSAAEvent(ui::AXEvent event) { ...@@ -1224,9 +1224,15 @@ int32 NativeViewAccessibilityWin::MSAAEvent(ui::AXEvent event) {
return EVENT_SYSTEM_MENUPOPUPSTART; return EVENT_SYSTEM_MENUPOPUPSTART;
case ui::AX_EVENT_MENU_POPUP_END: case ui::AX_EVENT_MENU_POPUP_END:
return EVENT_SYSTEM_MENUPOPUPEND; return EVENT_SYSTEM_MENUPOPUPEND;
case ui::AX_EVENT_SELECTION:
return EVENT_OBJECT_SELECTION;
case ui::AX_EVENT_SELECTION_ADD:
return EVENT_OBJECT_SELECTIONADD;
case ui::AX_EVENT_SELECTION_REMOVE:
return EVENT_OBJECT_SELECTIONREMOVE;
case ui::AX_EVENT_TEXT_CHANGED: case ui::AX_EVENT_TEXT_CHANGED:
return EVENT_OBJECT_NAMECHANGE; return EVENT_OBJECT_NAMECHANGE;
case ui::AX_EVENT_SELECTION_CHANGED: case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
return IA2_EVENT_TEXT_CARET_MOVED; return IA2_EVENT_TEXT_CARET_MOVED;
case ui::AX_EVENT_VALUE_CHANGED: case ui::AX_EVENT_VALUE_CHANGED:
return EVENT_OBJECT_VALUECHANGE; return EVENT_OBJECT_VALUECHANGE;
...@@ -1335,6 +1341,8 @@ int32 NativeViewAccessibilityWin::MSAAState(const ui::AXViewState& state) { ...@@ -1335,6 +1341,8 @@ int32 NativeViewAccessibilityWin::MSAAState(const ui::AXViewState& state) {
msaa_state |= STATE_SYSTEM_PROTECTED; msaa_state |= STATE_SYSTEM_PROTECTED;
if (state.HasStateFlag(ui::AX_STATE_READ_ONLY)) if (state.HasStateFlag(ui::AX_STATE_READ_ONLY))
msaa_state |= STATE_SYSTEM_READONLY; msaa_state |= STATE_SYSTEM_READONLY;
if (state.HasStateFlag(ui::AX_STATE_SELECTABLE))
msaa_state |= STATE_SYSTEM_SELECTABLE;
if (state.HasStateFlag(ui::AX_STATE_SELECTED)) if (state.HasStateFlag(ui::AX_STATE_SELECTED))
msaa_state |= STATE_SYSTEM_SELECTED; msaa_state |= STATE_SYSTEM_SELECTED;
if (state.HasStateFlag(ui::AX_STATE_FOCUSED)) if (state.HasStateFlag(ui::AX_STATE_FOCUSED))
......
...@@ -1568,9 +1568,9 @@ void Textfield::UpdateAfterChange(bool text_changed, bool cursor_changed) { ...@@ -1568,9 +1568,9 @@ void Textfield::UpdateAfterChange(bool text_changed, bool cursor_changed) {
if (cursor_repaint_timer_.IsRunning()) if (cursor_repaint_timer_.IsRunning())
cursor_repaint_timer_.Reset(); cursor_repaint_timer_.Reset();
if (!text_changed) { if (!text_changed) {
// TEXT_CHANGED implies SELECTION_CHANGED, so we only need to fire // TEXT_CHANGED implies TEXT_SELECTION_CHANGED, so we only need to fire
// this if only the selection changed. // this if only the selection changed.
NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION_CHANGED, true); NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_SELECTION_CHANGED, true);
} }
} }
if (text_changed || cursor_changed) { if (text_changed || cursor_changed) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment