Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / accessibility / accessibility_event_router_views.cc
index 931cf0d..22c7171 100644 (file)
 #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,
@@ -41,14 +45,14 @@ AccessibilityEventRouterViews* AccessibilityEventRouterViews::GetInstance() {
 }
 
 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
@@ -67,7 +71,7 @@ void AccessibilityEventRouterViews::HandleAccessibilityEvent(
   // 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(
@@ -79,8 +83,8 @@ void AccessibilityEventRouterViews::HandleAccessibilityEvent(
 }
 
 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) {
@@ -93,13 +97,13 @@ void AccessibilityEventRouterViews::HandleMenuItemFocused(
     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(
@@ -118,8 +122,8 @@ 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)
@@ -131,7 +135,7 @@ void AccessibilityEventRouterViews::DispatchEventOnViewStorageId(
 }
 
 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.
@@ -154,53 +158,92 @@ void AccessibilityEventRouterViews::DispatchAccessibilityEvent(
 
   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();
@@ -208,37 +251,67 @@ void AccessibilityEventRouterViews::DispatchAccessibilityEvent(
 }
 
 // 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);
@@ -262,104 +335,181 @@ void AccessibilityEventRouterViews::SendMenuItemNotification(
 
   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
@@ -367,31 +517,33 @@ std::string AccessibilityEventRouterViews::GetViewContext(views::View* view) {
   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();
     }
@@ -402,8 +554,8 @@ std::string AccessibilityEventRouterViews::GetViewContext(views::View* view) {
 
 // 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;
@@ -426,16 +578,19 @@ void AccessibilityEventRouterViews::RecursiveGetMenuItemIndexAndCount(
     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)++;
@@ -446,10 +601,10 @@ void AccessibilityEventRouterViews::RecursiveGetMenuItemIndexAndCount(
 // 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);
@@ -459,3 +614,14 @@ std::string AccessibilityEventRouterViews::RecursiveGetStaticText(
   }
   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;
+}