Home | History | Annotate | Download | only in views
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/views/accessibility_event_router_views.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/callback.h"
      9 #include "base/message_loop.h"
     10 #include "base/utf_string_conversions.h"
     11 #include "chrome/browser/browser_process.h"
     12 #include "chrome/browser/extensions/extension_accessibility_api.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/browser/profiles/profile_manager.h"
     15 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
     16 #include "content/common/notification_type.h"
     17 #include "ui/base/models/combobox_model.h"
     18 #include "ui/base/accessibility/accessible_view_state.h"
     19 #include "views/controls/button/checkbox.h"
     20 #include "views/controls/button/custom_button.h"
     21 #include "views/controls/button/menu_button.h"
     22 #include "views/controls/button/native_button.h"
     23 #include "views/controls/combobox/combobox.h"
     24 #include "views/controls/link.h"
     25 #include "views/controls/menu/menu_item_view.h"
     26 #include "views/controls/menu/submenu_view.h"
     27 #include "views/controls/textfield/textfield.h"
     28 #include "views/view.h"
     29 #include "views/widget/native_widget.h"
     30 #include "views/widget/widget.h"
     31 #include "views/window/window.h"
     32 
     33 using views::FocusManager;
     34 
     35 AccessibilityEventRouterViews::AccessibilityEventRouterViews()
     36     : most_recent_profile_(NULL),
     37       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
     38 }
     39 
     40 AccessibilityEventRouterViews::~AccessibilityEventRouterViews() {
     41 }
     42 
     43 // static
     44 AccessibilityEventRouterViews* AccessibilityEventRouterViews::GetInstance() {
     45   return Singleton<AccessibilityEventRouterViews>::get();
     46 }
     47 
     48 void AccessibilityEventRouterViews::HandleAccessibilityEvent(
     49     views::View* view, ui::AccessibilityTypes::Event event_type) {
     50   if (!ExtensionAccessibilityEventRouter::GetInstance()->
     51       IsAccessibilityEnabled()) {
     52     return;
     53   }
     54 
     55   switch (event_type) {
     56     case ui::AccessibilityTypes::EVENT_FOCUS:
     57       DispatchAccessibilityNotification(
     58           view, NotificationType::ACCESSIBILITY_CONTROL_FOCUSED);
     59       break;
     60     case ui::AccessibilityTypes::EVENT_MENUSTART:
     61     case ui::AccessibilityTypes::EVENT_MENUPOPUPSTART:
     62       DispatchAccessibilityNotification(
     63           view, NotificationType::ACCESSIBILITY_MENU_OPENED);
     64       break;
     65     case ui::AccessibilityTypes::EVENT_MENUEND:
     66     case ui::AccessibilityTypes::EVENT_MENUPOPUPEND:
     67       DispatchAccessibilityNotification(
     68           view, NotificationType::ACCESSIBILITY_MENU_CLOSED);
     69       break;
     70     case ui::AccessibilityTypes::EVENT_TEXT_CHANGED:
     71     case ui::AccessibilityTypes::EVENT_SELECTION_CHANGED:
     72       DispatchAccessibilityNotification(
     73           view, NotificationType::ACCESSIBILITY_TEXT_CHANGED);
     74       break;
     75     case ui::AccessibilityTypes::EVENT_VALUE_CHANGED:
     76       DispatchAccessibilityNotification(
     77           view, NotificationType::ACCESSIBILITY_CONTROL_ACTION);
     78       break;
     79     case ui::AccessibilityTypes::EVENT_ALERT:
     80     case ui::AccessibilityTypes::EVENT_NAME_CHANGED:
     81       // TODO(dmazzoni): re-evaluate this list later and see
     82       // if supporting any of these would be useful feature requests or
     83       // they'd just be superfluous.
     84       NOTIMPLEMENTED();
     85       break;
     86   }
     87 }
     88 
     89 void AccessibilityEventRouterViews::HandleMenuItemFocused(
     90     const std::wstring& menu_name,
     91     const std::wstring& menu_item_name,
     92     int item_index,
     93     int item_count,
     94     bool has_submenu) {
     95   if (!ExtensionAccessibilityEventRouter::GetInstance()->
     96       IsAccessibilityEnabled()) {
     97     return;
     98   }
     99 
    100   if (!most_recent_profile_)
    101     return;
    102 
    103   AccessibilityMenuItemInfo info(
    104       most_recent_profile_,
    105       WideToUTF8(menu_item_name),
    106       has_submenu,
    107       item_index,
    108       item_count);
    109   SendAccessibilityNotification(
    110       NotificationType::ACCESSIBILITY_CONTROL_FOCUSED, &info);
    111 }
    112 
    113 //
    114 // Private methods
    115 //
    116 
    117 std::string AccessibilityEventRouterViews::GetViewName(views::View* view) {
    118   ui::AccessibleViewState state;
    119   view->GetAccessibleState(&state);
    120   return UTF16ToUTF8(state.name);
    121 }
    122 
    123 void AccessibilityEventRouterViews::DispatchAccessibilityNotification(
    124     views::View* view, NotificationType type) {
    125   // Get the profile associated with this view. If it's not found, use
    126   // the most recent profile where accessibility events were sent, or
    127   // the default profile.
    128   Profile* profile = NULL;
    129   views::Window* window = view->GetWindow();
    130   if (window) {
    131     profile = reinterpret_cast<Profile*>(
    132         window->AsWidget()->native_widget()->GetNativeWindowProperty(
    133             Profile::kProfileKey));
    134   }
    135   if (!profile)
    136     profile = most_recent_profile_;
    137   if (!profile)
    138     profile = g_browser_process->profile_manager()->GetDefaultProfile();
    139   if (!profile) {
    140     NOTREACHED();
    141     return;
    142   }
    143 
    144   most_recent_profile_ = profile;
    145   std::string class_name = view->GetClassName();
    146 
    147   if (class_name == views::Checkbox::kViewClassName) {
    148     SendCheckboxNotification(view, type, profile);
    149   } else if (class_name == views::MenuButton::kViewClassName ||
    150       type == NotificationType::ACCESSIBILITY_MENU_OPENED ||
    151       type == NotificationType::ACCESSIBILITY_MENU_CLOSED) {
    152     SendMenuNotification(view, type, profile);
    153   } else if (IsMenuEvent(view, type)) {
    154     SendMenuItemNotification(view, type, profile);
    155   } else if (class_name == views::CustomButton::kViewClassName ||
    156              class_name == views::NativeButton::kViewClassName ||
    157              class_name == views::TextButton::kViewClassName) {
    158     SendButtonNotification(view, type, profile);
    159   } else if (class_name == views::Link::kViewClassName) {
    160     SendLinkNotification(view, type, profile);
    161   } else if (class_name == LocationBarView::kViewClassName) {
    162     SendLocationBarNotification(view, type, profile);
    163   } else if (class_name == views::Textfield::kViewClassName) {
    164     SendTextfieldNotification(view, type, profile);
    165   } else if (class_name == views::Combobox::kViewClassName) {
    166     SendComboboxNotification(view, type, profile);
    167   }
    168 }
    169 
    170 void AccessibilityEventRouterViews::SendButtonNotification(
    171     views::View* view, NotificationType type, Profile* profile) {
    172   AccessibilityButtonInfo info(profile, GetViewName(view));
    173   SendAccessibilityNotification(type, &info);
    174 }
    175 
    176 void AccessibilityEventRouterViews::SendLinkNotification(
    177     views::View* view, NotificationType type, Profile* profile) {
    178   AccessibilityLinkInfo info(profile, GetViewName(view));
    179   SendAccessibilityNotification(type, &info);
    180 }
    181 
    182 void AccessibilityEventRouterViews::SendMenuNotification(
    183     views::View* view, NotificationType type, Profile* profile) {
    184   AccessibilityMenuInfo info(profile, GetViewName(view));
    185   SendAccessibilityNotification(type, &info);
    186 }
    187 
    188 void AccessibilityEventRouterViews::SendMenuItemNotification(
    189     views::View* view, NotificationType type, Profile* profile) {
    190   std::string name = GetViewName(view);
    191 
    192   bool has_submenu = false;
    193   int index = -1;
    194   int count = -1;
    195 
    196   if (view->GetClassName() == views::MenuItemView::kViewClassName)
    197     has_submenu = static_cast<views::MenuItemView*>(view)->HasSubmenu();
    198 
    199   views::View* parent_menu = view->parent();
    200   while (parent_menu != NULL && parent_menu->GetClassName() !=
    201          views::SubmenuView::kViewClassName) {
    202     parent_menu = parent_menu->parent();
    203   }
    204   if (parent_menu) {
    205     count = 0;
    206     RecursiveGetMenuItemIndexAndCount(parent_menu, view, &index, &count);
    207   }
    208 
    209   AccessibilityMenuItemInfo info(profile, name, has_submenu, index, count);
    210   SendAccessibilityNotification(type, &info);
    211 }
    212 
    213 void AccessibilityEventRouterViews::RecursiveGetMenuItemIndexAndCount(
    214     views::View* menu, views::View* item, int* index, int* count) {
    215   for (int i = 0; i < menu->child_count(); ++i) {
    216     views::View* child = menu->GetChildViewAt(i);
    217     int previous_count = *count;
    218     RecursiveGetMenuItemIndexAndCount(child, item, index, count);
    219     if (child->GetClassName() == views::MenuItemView::kViewClassName &&
    220         *count == previous_count) {
    221       if (item == child)
    222         *index = *count;
    223       (*count)++;
    224     } else if (child->GetClassName() == views::TextButton::kViewClassName) {
    225       if (item == child)
    226         *index = *count;
    227       (*count)++;
    228     }
    229   }
    230 }
    231 
    232 bool AccessibilityEventRouterViews::IsMenuEvent(
    233     views::View* view, NotificationType type) {
    234   if (type == NotificationType::ACCESSIBILITY_MENU_OPENED ||
    235       type == NotificationType::ACCESSIBILITY_MENU_CLOSED)
    236     return true;
    237 
    238   while (view) {
    239     ui::AccessibleViewState state;
    240     view->GetAccessibleState(&state);
    241     ui::AccessibilityTypes::Role role = state.role;
    242     if (role == ui::AccessibilityTypes::ROLE_MENUITEM ||
    243         role == ui::AccessibilityTypes::ROLE_MENUPOPUP) {
    244       return true;
    245     }
    246     view = view->parent();
    247   }
    248 
    249   return false;
    250 }
    251 
    252 void AccessibilityEventRouterViews::SendLocationBarNotification(
    253     views::View* view, NotificationType type, Profile* profile) {
    254   ui::AccessibleViewState state;
    255   view->GetAccessibleState(&state);
    256   std::string name = UTF16ToUTF8(state.name);
    257   AccessibilityTextBoxInfo info(profile, name, false);
    258   std::string value = UTF16ToUTF8(state.value);
    259   info.SetValue(value, state.selection_start, state.selection_end);
    260   SendAccessibilityNotification(type, &info);
    261 }
    262 
    263 void AccessibilityEventRouterViews::SendTextfieldNotification(
    264     views::View* view, NotificationType type, Profile* profile) {
    265   ui::AccessibleViewState state;
    266   view->GetAccessibleState(&state);
    267   std::string name = UTF16ToUTF8(state.name);
    268   views::Textfield* textfield = static_cast<views::Textfield*>(view);
    269   bool password = textfield->IsPassword();
    270   AccessibilityTextBoxInfo info(profile, name, password);
    271   std::string value = UTF16ToUTF8(state.value);
    272   info.SetValue(value, state.selection_start, state.selection_end);
    273   SendAccessibilityNotification(type, &info);
    274 }
    275 
    276 void AccessibilityEventRouterViews::SendComboboxNotification(
    277     views::View* view, NotificationType type, Profile* profile) {
    278   ui::AccessibleViewState state;
    279   view->GetAccessibleState(&state);
    280   std::string name = UTF16ToUTF8(state.name);
    281   std::string value = UTF16ToUTF8(state.value);
    282   AccessibilityComboBoxInfo info(
    283       profile, name, value, state.index, state.count);
    284   SendAccessibilityNotification(type, &info);
    285 }
    286 
    287 void AccessibilityEventRouterViews::SendCheckboxNotification(
    288     views::View* view, NotificationType type, Profile* profile) {
    289   ui::AccessibleViewState state;
    290   view->GetAccessibleState(&state);
    291   std::string name = UTF16ToUTF8(state.name);
    292   std::string value = UTF16ToUTF8(state.value);
    293   AccessibilityCheckboxInfo info(
    294       profile, name, state.state == ui::AccessibilityTypes::STATE_CHECKED);
    295   SendAccessibilityNotification(type, &info);
    296 }
    297 
    298