#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "content/public/browser/notification_service.h"
-#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/accessibility/ax_view_state.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/submenu_view.h"
+#include "ui/views/controls/tree/tree_view.h"
#include "ui/views/focus/view_storage.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
using views::FocusManager;
+using views::ViewStorage;
AccessibilityEventRouterViews::AccessibilityEventRouterViews()
- : most_recent_profile_(NULL) {
+ : most_recent_profile_(NULL),
+ most_recent_view_id_(
+ ViewStorage::GetInstance()->CreateStorageID()) {
// Register for notification when profile is destroyed to ensure that all
// observers are detatched at that time.
registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
}
void AccessibilityEventRouterViews::HandleAccessibilityEvent(
- views::View* view, ui::AccessibilityTypes::Event event_type) {
+ views::View* view, ui::AXEvent event_type) {
if (!ExtensionAccessibilityEventRouter::GetInstance()->
IsAccessibilityEnabled()) {
return;
}
- if (event_type == ui::AccessibilityTypes::EVENT_TEXT_CHANGED ||
- event_type == ui::AccessibilityTypes::EVENT_SELECTION_CHANGED) {
+ if (event_type == ui::AX_EVENT_TEXT_CHANGED ||
+ event_type == ui::AX_EVENT_TEXT_SELECTION_CHANGED) {
// 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
// not by programmatic logic. For example, the location bar can be updated
// event loop, to handle cases where the view's state changes after
// the call to post the event. It's safe to use base::Unretained(this)
// because AccessibilityEventRouterViews is a singleton.
- views::ViewStorage* view_storage = views::ViewStorage::GetInstance();
+ ViewStorage* view_storage = ViewStorage::GetInstance();
int view_storage_id = view_storage->CreateStorageID();
view_storage->StoreView(view_storage_id, view);
base::MessageLoop::current()->PostTask(
}
void AccessibilityEventRouterViews::HandleMenuItemFocused(
- const string16& menu_name,
- const string16& menu_item_name,
+ const base::string16& menu_name,
+ const base::string16& menu_item_name,
int item_index,
int item_count,
bool has_submenu) {
return;
AccessibilityMenuItemInfo info(most_recent_profile_,
- UTF16ToUTF8(menu_item_name),
- UTF16ToUTF8(menu_name),
+ base::UTF16ToUTF8(menu_item_name),
+ base::UTF16ToUTF8(menu_name),
has_submenu,
item_index,
item_count);
SendControlAccessibilityNotification(
- ui::AccessibilityTypes::EVENT_FOCUS, &info);
+ ui::AX_EVENT_FOCUS, &info);
}
void AccessibilityEventRouterViews::Observe(
void AccessibilityEventRouterViews::DispatchEventOnViewStorageId(
int view_storage_id,
- ui::AccessibilityTypes::Event type) {
- views::ViewStorage* view_storage = views::ViewStorage::GetInstance();
+ ui::AXEvent type) {
+ ViewStorage* view_storage = ViewStorage::GetInstance();
views::View* view = view_storage->RetrieveView(view_storage_id);
view_storage->RemoveView(view_storage_id);
if (!view)
}
void AccessibilityEventRouterViews::DispatchAccessibilityEvent(
- views::View* view, ui::AccessibilityTypes::Event type) {
+ views::View* view, ui::AXEvent type) {
// Get the profile associated with this view. If it's not found, use
// the most recent profile where accessibility events were sent, or
// the default profile.
most_recent_profile_ = profile;
- if (type == ui::AccessibilityTypes::EVENT_MENUSTART ||
- type == ui::AccessibilityTypes::EVENT_MENUPOPUPSTART ||
- type == ui::AccessibilityTypes::EVENT_MENUEND ||
- type == ui::AccessibilityTypes::EVENT_MENUPOPUPEND) {
+ if (type == ui::AX_EVENT_MENU_START ||
+ type == ui::AX_EVENT_MENU_POPUP_START ||
+ type == ui::AX_EVENT_MENU_END ||
+ type == ui::AX_EVENT_MENU_POPUP_END) {
SendMenuNotification(view, type, profile);
return;
}
- ui::AccessibleViewState state;
+ view = FindFirstAccessibleAncestor(view);
+
+ // Since multiple items could share a highest focusable view, these items
+ // could all dispatch the same accessibility hover events, which isn't
+ // necessary.
+ if (type == ui::AX_EVENT_HOVER &&
+ ViewStorage::GetInstance()->RetrieveView(most_recent_view_id_) == view) {
+ return;
+ }
+ // If there was already a view stored here from before, it must be removed
+ // before storing a new view.
+ ViewStorage::GetInstance()->RemoveView(most_recent_view_id_);
+ ViewStorage::GetInstance()->StoreView(most_recent_view_id_, view);
+
+ ui::AXViewState state;
view->GetAccessibleState(&state);
+ if (type == ui::AX_EVENT_ALERT &&
+ !(state.role == ui::AX_ROLE_ALERT ||
+ state.role == ui::AX_ROLE_WINDOW)) {
+ SendAlertControlNotification(view, type, profile);
+ return;
+ }
+
switch (state.role) {
- case ui::AccessibilityTypes::ROLE_ALERT:
- case ui::AccessibilityTypes::ROLE_WINDOW:
+ case ui::AX_ROLE_ALERT:
+ case ui::AX_ROLE_DIALOG:
+ case ui::AX_ROLE_WINDOW:
SendWindowNotification(view, type, profile);
break;
- case ui::AccessibilityTypes::ROLE_BUTTONMENU:
- case ui::AccessibilityTypes::ROLE_MENUBAR:
- case ui::AccessibilityTypes::ROLE_MENUPOPUP:
+ case ui::AX_ROLE_POP_UP_BUTTON:
+ case ui::AX_ROLE_MENU_BAR:
+ case ui::AX_ROLE_MENU_LIST_POPUP:
SendMenuNotification(view, type, profile);
break;
- case ui::AccessibilityTypes::ROLE_BUTTONDROPDOWN:
- case ui::AccessibilityTypes::ROLE_PUSHBUTTON:
+ case ui::AX_ROLE_BUTTON_DROP_DOWN:
+ case ui::AX_ROLE_BUTTON:
SendButtonNotification(view, type, profile);
break;
- case ui::AccessibilityTypes::ROLE_CHECKBUTTON:
+ case ui::AX_ROLE_CHECK_BOX:
SendCheckboxNotification(view, type, profile);
break;
- case ui::AccessibilityTypes::ROLE_COMBOBOX:
+ case ui::AX_ROLE_COMBO_BOX:
SendComboboxNotification(view, type, profile);
break;
- case ui::AccessibilityTypes::ROLE_LINK:
+ case ui::AX_ROLE_LINK:
SendLinkNotification(view, type, profile);
break;
- case ui::AccessibilityTypes::ROLE_LOCATION_BAR:
- case ui::AccessibilityTypes::ROLE_TEXT:
+ case ui::AX_ROLE_LOCATION_BAR:
+ case ui::AX_ROLE_TEXT_FIELD:
SendTextfieldNotification(view, type, profile);
break;
- case ui::AccessibilityTypes::ROLE_MENUITEM:
+ case ui::AX_ROLE_MENU_ITEM:
SendMenuItemNotification(view, type, profile);
break;
- case ui::AccessibilityTypes::ROLE_RADIOBUTTON:
+ case ui::AX_ROLE_RADIO_BUTTON:
// Not used anymore?
- case ui::AccessibilityTypes::ROLE_SLIDER:
+ case ui::AX_ROLE_SLIDER:
SendSliderNotification(view, type, profile);
break;
+ case ui::AX_ROLE_STATIC_TEXT:
+ SendStaticTextNotification(view, type, profile);
+ break;
+ case ui::AX_ROLE_TREE:
+ SendTreeNotification(view, type, profile);
+ break;
+ case ui::AX_ROLE_TAB:
+ SendTabNotification(view, type, profile);
+ break;
+ case ui::AX_ROLE_TREE_ITEM:
+ SendTreeItemNotification(view, type, profile);
+ break;
default:
+ // Hover events can fire on literally any view, so it's safe to
+ // ignore ones we don't care about.
+ if (type == ui::AX_EVENT_HOVER)
+ break;
+
// If this is encountered, please file a bug with the role that wasn't
// caught so we can add accessibility extension API support.
NOTREACHED();
}
// static
+void AccessibilityEventRouterViews::SendTabNotification(
+ views::View* view,
+ ui::AXEvent event,
+ Profile* profile) {
+ ui::AXViewState state;
+ view->GetAccessibleState(&state);
+ if (state.index == -1)
+ return;
+ std::string name = base::UTF16ToUTF8(state.name);
+ std::string context = GetViewContext(view);
+ AccessibilityTabInfo info(profile, name, context, state.index, state.count);
+ info.set_bounds(view->GetBoundsInScreen());
+ SendControlAccessibilityNotification(event, &info);
+}
+
+// static
void AccessibilityEventRouterViews::SendButtonNotification(
views::View* view,
- ui::AccessibilityTypes::Event event,
+ ui::AXEvent event,
Profile* profile) {
AccessibilityButtonInfo info(
profile, GetViewName(view), GetViewContext(view));
+ info.set_bounds(view->GetBoundsInScreen());
+ SendControlAccessibilityNotification(event, &info);
+}
+
+// static
+void AccessibilityEventRouterViews::SendStaticTextNotification(
+ views::View* view,
+ ui::AXEvent event,
+ Profile* profile) {
+ AccessibilityStaticTextInfo info(
+ profile, GetViewName(view), GetViewContext(view));
+ info.set_bounds(view->GetBoundsInScreen());
SendControlAccessibilityNotification(event, &info);
}
// static
void AccessibilityEventRouterViews::SendLinkNotification(
views::View* view,
- ui::AccessibilityTypes::Event event,
+ ui::AXEvent event,
Profile* profile) {
AccessibilityLinkInfo info(profile, GetViewName(view), GetViewContext(view));
+ info.set_bounds(view->GetBoundsInScreen());
SendControlAccessibilityNotification(event, &info);
}
// static
void AccessibilityEventRouterViews::SendMenuNotification(
views::View* view,
- ui::AccessibilityTypes::Event event,
+ ui::AXEvent event,
Profile* profile) {
AccessibilityMenuInfo info(profile, GetViewName(view));
+ info.set_bounds(view->GetBoundsInScreen());
SendMenuAccessibilityNotification(event, &info);
}
// static
void AccessibilityEventRouterViews::SendMenuItemNotification(
views::View* view,
- ui::AccessibilityTypes::Event event,
+ ui::AXEvent event,
Profile* profile) {
std::string name = GetViewName(view);
std::string context = GetViewContext(view);
AccessibilityMenuItemInfo info(
profile, name, context, has_submenu, index, count);
+ info.set_bounds(view->GetBoundsInScreen());
+ SendControlAccessibilityNotification(event, &info);
+}
+
+// static
+void AccessibilityEventRouterViews::SendTreeNotification(
+ views::View* view,
+ ui::AXEvent event,
+ Profile* profile) {
+ AccessibilityTreeInfo info(profile, GetViewName(view));
+ info.set_bounds(view->GetBoundsInScreen());
+ SendControlAccessibilityNotification(event, &info);
+}
+
+// static
+void AccessibilityEventRouterViews::SendTreeItemNotification(
+ views::View* view,
+ ui::AXEvent event,
+ Profile* profile) {
+ std::string name = GetViewName(view);
+ std::string context = GetViewContext(view);
+
+ if (strcmp(view->GetClassName(), views::TreeView::kViewClassName) != 0) {
+ NOTREACHED();
+ return;
+ }
+
+ views::TreeView* tree = static_cast<views::TreeView*>(view);
+ ui::TreeModelNode* selected_node = tree->GetSelectedNode();
+ ui::TreeModel* model = tree->model();
+
+ int siblings_count = model->GetChildCount(model->GetRoot());
+ int children_count = -1;
+ int index = -1;
+ int depth = -1;
+ bool is_expanded = false;
+
+ if (selected_node) {
+ children_count = model->GetChildCount(selected_node);
+ is_expanded = tree->IsExpanded(selected_node);
+ ui::TreeModelNode* parent_node = model->GetParent(selected_node);
+ if (parent_node) {
+ index = model->GetIndexOf(parent_node, selected_node);
+ siblings_count = model->GetChildCount(parent_node);
+ }
+ // Get node depth.
+ depth = 0;
+ while (parent_node) {
+ depth++;
+ parent_node = model->GetParent(parent_node);
+ }
+ }
+
+ AccessibilityTreeItemInfo info(
+ profile, name, context, depth, index, siblings_count, children_count,
+ is_expanded);
+ info.set_bounds(view->GetBoundsInScreen());
SendControlAccessibilityNotification(event, &info);
}
// static
void AccessibilityEventRouterViews::SendTextfieldNotification(
views::View* view,
- ui::AccessibilityTypes::Event event,
+ ui::AXEvent event,
Profile* profile) {
- ui::AccessibleViewState state;
+ ui::AXViewState state;
view->GetAccessibleState(&state);
- std::string name = UTF16ToUTF8(state.name);
+ std::string name = base::UTF16ToUTF8(state.name);
std::string context = GetViewContext(view);
- bool password =
- (state.state & ui::AccessibilityTypes::STATE_PROTECTED) != 0;
+ bool password = state.HasStateFlag(ui::AX_STATE_PROTECTED);
AccessibilityTextBoxInfo info(profile, name, context, password);
- std::string value = UTF16ToUTF8(state.value);
+ std::string value = base::UTF16ToUTF8(state.value);
info.SetValue(value, state.selection_start, state.selection_end);
+ info.set_bounds(view->GetBoundsInScreen());
SendControlAccessibilityNotification(event, &info);
}
// static
void AccessibilityEventRouterViews::SendComboboxNotification(
views::View* view,
- ui::AccessibilityTypes::Event event,
+ ui::AXEvent event,
Profile* profile) {
- ui::AccessibleViewState state;
+ ui::AXViewState state;
view->GetAccessibleState(&state);
- std::string name = UTF16ToUTF8(state.name);
- std::string value = UTF16ToUTF8(state.value);
+ std::string name = base::UTF16ToUTF8(state.name);
+ std::string value = base::UTF16ToUTF8(state.value);
std::string context = GetViewContext(view);
AccessibilityComboBoxInfo info(
profile, name, context, value, state.index, state.count);
+ info.set_bounds(view->GetBoundsInScreen());
SendControlAccessibilityNotification(event, &info);
}
// static
void AccessibilityEventRouterViews::SendCheckboxNotification(
views::View* view,
- ui::AccessibilityTypes::Event event,
+ ui::AXEvent event,
Profile* profile) {
- ui::AccessibleViewState state;
+ ui::AXViewState state;
view->GetAccessibleState(&state);
- std::string name = UTF16ToUTF8(state.name);
+ std::string name = base::UTF16ToUTF8(state.name);
std::string context = GetViewContext(view);
AccessibilityCheckboxInfo info(
profile,
name,
context,
- state.state == ui::AccessibilityTypes::STATE_CHECKED);
+ state.HasStateFlag(ui::AX_STATE_CHECKED));
+ info.set_bounds(view->GetBoundsInScreen());
SendControlAccessibilityNotification(event, &info);
}
// static
void AccessibilityEventRouterViews::SendWindowNotification(
views::View* view,
- ui::AccessibilityTypes::Event event,
+ ui::AXEvent event,
Profile* profile) {
- ui::AccessibleViewState state;
+ ui::AXViewState state;
view->GetAccessibleState(&state);
std::string window_text;
// If it's an alert, try to get the text from the contents of the
// static text, not the window title.
- if (state.role == ui::AccessibilityTypes::ROLE_ALERT)
+ if (state.role == ui::AX_ROLE_ALERT)
window_text = RecursiveGetStaticText(view);
// Otherwise get it from the window's accessible name.
if (window_text.empty())
- window_text = UTF16ToUTF8(state.name);
+ window_text = base::UTF16ToUTF8(state.name);
AccessibilityWindowInfo info(profile, window_text);
+ info.set_bounds(view->GetBoundsInScreen());
SendWindowAccessibilityNotification(event, &info);
}
// static
void AccessibilityEventRouterViews::SendSliderNotification(
views::View* view,
- ui::AccessibilityTypes::Event event,
+ ui::AXEvent event,
Profile* profile) {
- ui::AccessibleViewState state;
+ ui::AXViewState state;
view->GetAccessibleState(&state);
- std::string name = UTF16ToUTF8(state.name);
- std::string value = UTF16ToUTF8(state.value);
+ std::string name = base::UTF16ToUTF8(state.name);
+ std::string value = base::UTF16ToUTF8(state.value);
std::string context = GetViewContext(view);
AccessibilitySliderInfo info(
profile,
name,
context,
value);
+ info.set_bounds(view->GetBoundsInScreen());
+ SendControlAccessibilityNotification(event, &info);
+}
+
+// static
+void AccessibilityEventRouterViews::SendAlertControlNotification(
+ views::View* view,
+ ui::AXEvent event,
+ Profile* profile) {
+ ui::AXViewState state;
+ view->GetAccessibleState(&state);
+
+ std::string name = base::UTF16ToUTF8(state.name);
+ AccessibilityAlertInfo info(
+ profile,
+ name);
+ info.set_bounds(view->GetBoundsInScreen());
SendControlAccessibilityNotification(event, &info);
}
// static
std::string AccessibilityEventRouterViews::GetViewName(views::View* view) {
- ui::AccessibleViewState state;
+ ui::AXViewState state;
view->GetAccessibleState(&state);
- return UTF16ToUTF8(state.name);
+ return base::UTF16ToUTF8(state.name);
}
// static
for (views::View* parent = view->parent();
parent;
parent = parent->parent()) {
- ui::AccessibleViewState state;
+ ui::AXViewState state;
parent->GetAccessibleState(&state);
// Two cases are handled right now. More could be added in the future
// depending on how the UI evolves.
- // A control in a toolbar should use the toolbar's accessible name
- // as the context.
- if (state.role == ui::AccessibilityTypes::ROLE_TOOLBAR &&
+ // A control inside of alert, toolbar or dialog should use that container's
+ // accessible name.
+ if ((state.role == ui::AX_ROLE_ALERT ||
+ state.role == ui::AX_ROLE_DIALOG ||
+ state.role == ui::AX_ROLE_TOOLBAR) &&
!state.name.empty()) {
- return UTF16ToUTF8(state.name);
+ return base::UTF16ToUTF8(state.name);
}
// A control inside of an alert or dialog (including an infobar)
// should grab the first static text descendant as the context;
// that's the prompt.
- if (state.role == ui::AccessibilityTypes::ROLE_ALERT ||
- state.role == ui::AccessibilityTypes::ROLE_DIALOG) {
+ if (state.role == ui::AX_ROLE_ALERT ||
+ state.role == ui::AX_ROLE_DIALOG) {
views::View* static_text_child = FindDescendantWithAccessibleRole(
- parent, ui::AccessibilityTypes::ROLE_STATICTEXT);
+ parent, ui::AX_ROLE_STATIC_TEXT);
if (static_text_child) {
- ui::AccessibleViewState state;
+ ui::AXViewState state;
static_text_child->GetAccessibleState(&state);
if (!state.name.empty())
- return UTF16ToUTF8(state.name);
+ return base::UTF16ToUTF8(state.name);
}
return std::string();
}
// static
views::View* AccessibilityEventRouterViews::FindDescendantWithAccessibleRole(
- views::View* view, ui::AccessibilityTypes::Role role) {
- ui::AccessibleViewState state;
+ views::View* view, ui::AXRole role) {
+ ui::AXViewState state;
view->GetAccessibleState(&state);
if (state.role == role)
return view;
int* count) {
for (int i = 0; i < menu->child_count(); ++i) {
views::View* child = menu->child_at(i);
+ if (!child->visible())
+ continue;
+
int previous_count = *count;
RecursiveGetMenuItemIndexAndCount(child, item, index, count);
- ui::AccessibleViewState state;
+ ui::AXViewState state;
child->GetAccessibleState(&state);
- if (state.role == ui::AccessibilityTypes::ROLE_MENUITEM &&
+ if (state.role == ui::AX_ROLE_MENU_ITEM &&
*count == previous_count) {
if (item == child)
*index = *count;
(*count)++;
- } else if (state.role == ui::AccessibilityTypes::ROLE_PUSHBUTTON) {
+ } else if (state.role == ui::AX_ROLE_BUTTON) {
if (item == child)
*index = *count;
(*count)++;
// static
std::string AccessibilityEventRouterViews::RecursiveGetStaticText(
views::View* view) {
- ui::AccessibleViewState state;
+ ui::AXViewState state;
view->GetAccessibleState(&state);
- if (state.role == ui::AccessibilityTypes::ROLE_STATICTEXT)
- return UTF16ToUTF8(state.name);
+ if (state.role == ui::AX_ROLE_STATIC_TEXT)
+ return base::UTF16ToUTF8(state.name);
for (int i = 0; i < view->child_count(); ++i) {
views::View* child = view->child_at(i);
}
return std::string();
}
+
+// static
+views::View* AccessibilityEventRouterViews::FindFirstAccessibleAncestor(
+ views::View* view) {
+ views::View* temp_view = view;
+ while (temp_view->parent() && !temp_view->IsAccessibilityFocusable())
+ temp_view = temp_view->parent();
+ if (temp_view->IsAccessibilityFocusable())
+ return temp_view;
+ return view;
+}