Home | History | Annotate | Download | only in accessibility
      1 // Copyright (c) 2012 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/accessibility/accessibility_extension_api.h"
      6 
      7 #include "base/json/json_writer.h"
      8 #include "base/strings/string_number_conversions.h"
      9 #include "base/values.h"
     10 #include "chrome/browser/accessibility/accessibility_extension_api_constants.h"
     11 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
     12 #include "chrome/browser/extensions/extension_service.h"
     13 #include "chrome/browser/extensions/extension_tab_util.h"
     14 #include "chrome/browser/infobars/infobar_service.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "chrome/common/extensions/api/accessibility_private.h"
     17 #include "chrome/common/extensions/extension_constants.h"
     18 #include "components/infobars/core/confirm_infobar_delegate.h"
     19 #include "components/infobars/core/infobar.h"
     20 #include "content/public/browser/browser_accessibility_state.h"
     21 #include "extensions/browser/event_router.h"
     22 #include "extensions/browser/extension_host.h"
     23 #include "extensions/browser/extension_system.h"
     24 #include "extensions/browser/lazy_background_task_queue.h"
     25 #include "extensions/common/error_utils.h"
     26 #include "extensions/common/manifest_handlers/background_info.h"
     27 
     28 #if defined(OS_CHROMEOS)
     29 #include "chrome/browser/chromeos/ui/accessibility_focus_ring_controller.h"
     30 #endif
     31 
     32 namespace keys = extension_accessibility_api_constants;
     33 namespace accessibility_private = extensions::api::accessibility_private;
     34 
     35 // Returns the AccessibilityControlInfo serialized into a JSON string,
     36 // consisting of an array of a single object of type AccessibilityObject,
     37 // as defined in the accessibility extension api's json schema.
     38 scoped_ptr<base::ListValue> ControlInfoToEventArguments(
     39     const AccessibilityEventInfo* info) {
     40   base::DictionaryValue* dict = new base::DictionaryValue();
     41   info->SerializeToDict(dict);
     42 
     43   scoped_ptr<base::ListValue> args(new base::ListValue());
     44   args->Append(dict);
     45   return args.Pass();
     46 }
     47 
     48 ExtensionAccessibilityEventRouter*
     49     ExtensionAccessibilityEventRouter::GetInstance() {
     50   return Singleton<ExtensionAccessibilityEventRouter>::get();
     51 }
     52 
     53 ExtensionAccessibilityEventRouter::ExtensionAccessibilityEventRouter()
     54     : enabled_(false) {
     55 }
     56 
     57 ExtensionAccessibilityEventRouter::~ExtensionAccessibilityEventRouter() {
     58   control_event_callback_.Reset();
     59 }
     60 
     61 void ExtensionAccessibilityEventRouter::SetAccessibilityEnabled(bool enabled) {
     62   enabled_ = enabled;
     63 }
     64 
     65 bool ExtensionAccessibilityEventRouter::IsAccessibilityEnabled() const {
     66   return enabled_;
     67 }
     68 
     69 void ExtensionAccessibilityEventRouter::SetControlEventCallbackForTesting(
     70     ControlEventCallback control_event_callback) {
     71   DCHECK(control_event_callback_.is_null());
     72   control_event_callback_ = control_event_callback;
     73 }
     74 
     75 void ExtensionAccessibilityEventRouter::ClearControlEventCallback() {
     76   control_event_callback_.Reset();
     77 }
     78 
     79 void ExtensionAccessibilityEventRouter::HandleWindowEvent(
     80     ui::AXEvent event,
     81     const AccessibilityWindowInfo* info) {
     82   if (!control_event_callback_.is_null())
     83     control_event_callback_.Run(event, info);
     84 
     85   if (event == ui::AX_EVENT_ALERT)
     86     OnWindowOpened(info);
     87 }
     88 
     89 void ExtensionAccessibilityEventRouter::HandleMenuEvent(
     90     ui::AXEvent event,
     91     const AccessibilityMenuInfo* info) {
     92   switch (event) {
     93     case ui::AX_EVENT_MENU_START:
     94     case ui::AX_EVENT_MENU_POPUP_START:
     95       OnMenuOpened(info);
     96       break;
     97     case ui::AX_EVENT_MENU_END:
     98     case ui::AX_EVENT_MENU_POPUP_END:
     99       OnMenuClosed(info);
    100       break;
    101     case ui::AX_EVENT_FOCUS:
    102       OnControlFocused(info);
    103       break;
    104     case ui::AX_EVENT_HOVER:
    105       OnControlHover(info);
    106       break;
    107     default:
    108       NOTREACHED();
    109   }
    110 }
    111 
    112 void ExtensionAccessibilityEventRouter::HandleControlEvent(
    113     ui::AXEvent event,
    114     const AccessibilityControlInfo* info) {
    115   if (!control_event_callback_.is_null())
    116     control_event_callback_.Run(event, info);
    117 
    118   switch (event) {
    119     case ui::AX_EVENT_TEXT_CHANGED:
    120     case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
    121       OnTextChanged(info);
    122       break;
    123     case ui::AX_EVENT_VALUE_CHANGED:
    124     case ui::AX_EVENT_ALERT:
    125       OnControlAction(info);
    126       break;
    127     case ui::AX_EVENT_FOCUS:
    128       OnControlFocused(info);
    129       break;
    130     case ui::AX_EVENT_HOVER:
    131       OnControlHover(info);
    132       break;
    133     default:
    134       NOTREACHED();
    135   }
    136 }
    137 
    138 void ExtensionAccessibilityEventRouter::OnWindowOpened(
    139     const AccessibilityWindowInfo* info) {
    140   scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
    141   DispatchEvent(info->profile(),
    142                 accessibility_private::OnWindowOpened::kEventName,
    143                 args.Pass());
    144 }
    145 
    146 void ExtensionAccessibilityEventRouter::OnControlFocused(
    147     const AccessibilityControlInfo* info) {
    148   last_focused_control_dict_.Clear();
    149   info->SerializeToDict(&last_focused_control_dict_);
    150   scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
    151   DispatchEvent(info->profile(),
    152                 accessibility_private::OnControlFocused::kEventName,
    153                 args.Pass());
    154 }
    155 
    156 void ExtensionAccessibilityEventRouter::OnControlAction(
    157     const AccessibilityControlInfo* info) {
    158   scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
    159   DispatchEvent(info->profile(),
    160                 accessibility_private::OnControlAction::kEventName,
    161                 args.Pass());
    162 }
    163 
    164 void ExtensionAccessibilityEventRouter::OnControlHover(
    165     const AccessibilityControlInfo* info) {
    166   scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
    167   DispatchEvent(info->profile(),
    168                 accessibility_private::OnControlHover::kEventName,
    169                 args.Pass());
    170 }
    171 
    172 void ExtensionAccessibilityEventRouter::OnTextChanged(
    173     const AccessibilityControlInfo* info) {
    174   scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
    175   DispatchEvent(info->profile(),
    176                 accessibility_private::OnTextChanged::kEventName,
    177                 args.Pass());
    178 }
    179 
    180 void ExtensionAccessibilityEventRouter::OnMenuOpened(
    181     const AccessibilityMenuInfo* info) {
    182   scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
    183   DispatchEvent(info->profile(),
    184                 accessibility_private::OnMenuOpened::kEventName,
    185                 args.Pass());
    186 }
    187 
    188 void ExtensionAccessibilityEventRouter::OnMenuClosed(
    189     const AccessibilityMenuInfo* info) {
    190   scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
    191   DispatchEvent(info->profile(),
    192                 accessibility_private::OnMenuClosed::kEventName,
    193                 args.Pass());
    194 }
    195 
    196 void ExtensionAccessibilityEventRouter::OnChromeVoxLoadStateChanged(
    197     Profile* profile,
    198     bool loading,
    199     bool make_announcements) {
    200   scoped_ptr<base::ListValue> event_args(new base::ListValue());
    201   event_args->AppendBoolean(loading);
    202   event_args->AppendBoolean(make_announcements);
    203   ExtensionAccessibilityEventRouter::DispatchEventToChromeVox(
    204       profile,
    205       accessibility_private::OnChromeVoxLoadStateChanged::kEventName,
    206       event_args.Pass());
    207 }
    208 
    209 // Static.
    210 void ExtensionAccessibilityEventRouter::DispatchEventToChromeVox(
    211     Profile* profile,
    212     const char* event_name,
    213     scoped_ptr<base::ListValue> event_args) {
    214   extensions::ExtensionSystem* system =
    215       extensions::ExtensionSystem::Get(profile);
    216   if (!system)
    217     return;
    218   scoped_ptr<extensions::Event> event(new extensions::Event(event_name,
    219                                                             event_args.Pass()));
    220   system->event_router()->DispatchEventWithLazyListener(
    221       extension_misc::kChromeVoxExtensionId, event.Pass());
    222 }
    223 
    224 void ExtensionAccessibilityEventRouter::DispatchEvent(
    225     Profile* profile,
    226     const char* event_name,
    227     scoped_ptr<base::ListValue> event_args) {
    228   if (!enabled_ || !profile)
    229     return;
    230   extensions::EventRouter* event_router = extensions::EventRouter::Get(profile);
    231   if (!event_router)
    232     return;
    233 
    234   scoped_ptr<extensions::Event> event(new extensions::Event(
    235       event_name, event_args.Pass()));
    236   event_router->BroadcastEvent(event.Pass());
    237 }
    238 
    239 bool AccessibilityPrivateSetAccessibilityEnabledFunction::RunSync() {
    240   bool enabled;
    241   EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(0, &enabled));
    242   ExtensionAccessibilityEventRouter::GetInstance()
    243       ->SetAccessibilityEnabled(enabled);
    244   return true;
    245 }
    246 
    247 bool AccessibilityPrivateSetNativeAccessibilityEnabledFunction::RunSync() {
    248   bool enabled;
    249   EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(0, &enabled));
    250   if (enabled) {
    251     content::BrowserAccessibilityState::GetInstance()->
    252         EnableAccessibility();
    253   } else {
    254     content::BrowserAccessibilityState::GetInstance()->
    255         DisableAccessibility();
    256   }
    257   return true;
    258 }
    259 
    260 bool AccessibilityPrivateGetFocusedControlFunction::RunSync() {
    261   // Get the serialized dict from the last focused control and return it.
    262   // However, if the dict is empty, that means we haven't seen any focus
    263   // events yet, so return null instead.
    264   ExtensionAccessibilityEventRouter *accessibility_event_router =
    265       ExtensionAccessibilityEventRouter::GetInstance();
    266   base::DictionaryValue *last_focused_control_dict =
    267       accessibility_event_router->last_focused_control_dict();
    268   if (last_focused_control_dict->size()) {
    269     SetResult(last_focused_control_dict->DeepCopyWithoutEmptyChildren());
    270   } else {
    271     SetResult(base::Value::CreateNullValue());
    272   }
    273   return true;
    274 }
    275 
    276 bool AccessibilityPrivateGetAlertsForTabFunction::RunSync() {
    277   int tab_id;
    278   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
    279 
    280   TabStripModel* tab_strip = NULL;
    281   content::WebContents* contents = NULL;
    282   int tab_index = -1;
    283   if (!extensions::ExtensionTabUtil::GetTabById(tab_id,
    284                                                 GetProfile(),
    285                                                 include_incognito(),
    286                                                 NULL,
    287                                                 &tab_strip,
    288                                                 &contents,
    289                                                 &tab_index)) {
    290     error_ = extensions::ErrorUtils::FormatErrorMessage(
    291         extensions::tabs_constants::kTabNotFoundError,
    292         base::IntToString(tab_id));
    293     return false;
    294   }
    295 
    296   base::ListValue* alerts_value = new base::ListValue;
    297 
    298   InfoBarService* infobar_service = InfoBarService::FromWebContents(contents);
    299   for (size_t i = 0; i < infobar_service->infobar_count(); ++i) {
    300     // TODO(hashimoto): Make other kind of alerts available.  crosbug.com/24281
    301     ConfirmInfoBarDelegate* confirm_infobar_delegate =
    302         infobar_service->infobar_at(i)->delegate()->AsConfirmInfoBarDelegate();
    303     if (confirm_infobar_delegate) {
    304       base::DictionaryValue* alert_value = new base::DictionaryValue;
    305       const base::string16 message_text =
    306           confirm_infobar_delegate->GetMessageText();
    307       alert_value->SetString(keys::kMessageKey, message_text);
    308       alerts_value->Append(alert_value);
    309     }
    310   }
    311 
    312   SetResult(alerts_value);
    313   return true;
    314 }
    315 
    316 bool AccessibilityPrivateSetFocusRingFunction::RunSync() {
    317 #if defined(OS_CHROMEOS)
    318   base::ListValue* rect_values = NULL;
    319   EXTENSION_FUNCTION_VALIDATE(args_->GetList(0, &rect_values));
    320 
    321   std::vector<gfx::Rect> rects;
    322   for (size_t i = 0; i < rect_values->GetSize(); ++i) {
    323     base::DictionaryValue* rect_value = NULL;
    324     EXTENSION_FUNCTION_VALIDATE(rect_values->GetDictionary(i, &rect_value));
    325     int left, top, width, height;
    326     EXTENSION_FUNCTION_VALIDATE(rect_value->GetInteger(keys::kLeft, &left));
    327     EXTENSION_FUNCTION_VALIDATE(rect_value->GetInteger(keys::kTop, &top));
    328     EXTENSION_FUNCTION_VALIDATE(rect_value->GetInteger(keys::kWidth, &width));
    329     EXTENSION_FUNCTION_VALIDATE(rect_value->GetInteger(keys::kHeight, &height));
    330     rects.push_back(gfx::Rect(left, top, width, height));
    331   }
    332 
    333   chromeos::AccessibilityFocusRingController::GetInstance()->SetFocusRing(
    334       rects);
    335   return true;
    336 #endif  // defined(OS_CHROMEOS)
    337 
    338   error_ = keys:: kErrorNotSupported;
    339   return false;
    340 }
    341