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_host.h"
     13 #include "chrome/browser/extensions/extension_service.h"
     14 #include "chrome/browser/extensions/extension_system.h"
     15 #include "chrome/browser/extensions/extension_tab_util.h"
     16 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
     17 #include "chrome/browser/infobars/infobar.h"
     18 #include "chrome/browser/infobars/infobar_service.h"
     19 #include "chrome/browser/profiles/profile.h"
     20 #include "chrome/common/extensions/api/experimental_accessibility.h"
     21 #include "content/public/browser/browser_accessibility_state.h"
     22 #include "extensions/browser/event_router.h"
     23 #include "extensions/browser/lazy_background_task_queue.h"
     24 #include "extensions/common/error_utils.h"
     25 #include "extensions/common/manifest_handlers/background_info.h"
     26 
     27 namespace keys = extension_accessibility_api_constants;
     28 namespace experimental_accessibility =
     29     extensions::api::experimental_accessibility;
     30 
     31 // Returns the AccessibilityControlInfo serialized into a JSON string,
     32 // consisting of an array of a single object of type AccessibilityObject,
     33 // as defined in the accessibility extension api's json schema.
     34 scoped_ptr<ListValue> ControlInfoToEventArguments(
     35     const AccessibilityEventInfo* info) {
     36   DictionaryValue* dict = new DictionaryValue();
     37   info->SerializeToDict(dict);
     38 
     39   scoped_ptr<ListValue> args(new ListValue());
     40   args->Append(dict);
     41   return args.Pass();
     42 }
     43 
     44 ExtensionAccessibilityEventRouter*
     45     ExtensionAccessibilityEventRouter::GetInstance() {
     46   return Singleton<ExtensionAccessibilityEventRouter>::get();
     47 }
     48 
     49 ExtensionAccessibilityEventRouter::ExtensionAccessibilityEventRouter()
     50     : enabled_(false) {
     51 }
     52 
     53 ExtensionAccessibilityEventRouter::~ExtensionAccessibilityEventRouter() {
     54   control_event_callback_.Reset();
     55 }
     56 
     57 void ExtensionAccessibilityEventRouter::SetAccessibilityEnabled(bool enabled) {
     58   enabled_ = enabled;
     59 }
     60 
     61 bool ExtensionAccessibilityEventRouter::IsAccessibilityEnabled() const {
     62   return enabled_;
     63 }
     64 
     65 void ExtensionAccessibilityEventRouter::SetControlEventCallbackForTesting(
     66     ControlEventCallback control_event_callback) {
     67   DCHECK(control_event_callback_.is_null());
     68   control_event_callback_ = control_event_callback;
     69 }
     70 
     71 void ExtensionAccessibilityEventRouter::ClearControlEventCallback() {
     72   control_event_callback_.Reset();
     73 }
     74 
     75 void ExtensionAccessibilityEventRouter::HandleWindowEvent(
     76     ui::AccessibilityTypes::Event event,
     77     const AccessibilityWindowInfo* info) {
     78   if (!control_event_callback_.is_null())
     79     control_event_callback_.Run(event, info);
     80 
     81   if (event == ui::AccessibilityTypes::EVENT_ALERT)
     82     OnWindowOpened(info);
     83 }
     84 
     85 void ExtensionAccessibilityEventRouter::HandleMenuEvent(
     86     ui::AccessibilityTypes::Event event,
     87     const AccessibilityMenuInfo* info) {
     88   switch (event) {
     89     case ui::AccessibilityTypes::EVENT_MENUSTART:
     90     case ui::AccessibilityTypes::EVENT_MENUPOPUPSTART:
     91       OnMenuOpened(info);
     92       break;
     93     case ui::AccessibilityTypes::EVENT_MENUEND:
     94     case ui::AccessibilityTypes::EVENT_MENUPOPUPEND:
     95       OnMenuClosed(info);
     96       break;
     97     case ui::AccessibilityTypes::EVENT_FOCUS:
     98       OnControlFocused(info);
     99       break;
    100     default:
    101       NOTREACHED();
    102   }
    103 }
    104 
    105 void ExtensionAccessibilityEventRouter::HandleControlEvent(
    106     ui::AccessibilityTypes::Event event,
    107     const AccessibilityControlInfo* info) {
    108   if (!control_event_callback_.is_null())
    109     control_event_callback_.Run(event, info);
    110 
    111   switch (event) {
    112     case ui::AccessibilityTypes::EVENT_TEXT_CHANGED:
    113     case ui::AccessibilityTypes::EVENT_SELECTION_CHANGED:
    114       OnTextChanged(info);
    115       break;
    116     case ui::AccessibilityTypes::EVENT_VALUE_CHANGED:
    117     case ui::AccessibilityTypes::EVENT_ALERT:
    118       OnControlAction(info);
    119       break;
    120     case ui::AccessibilityTypes::EVENT_FOCUS:
    121       OnControlFocused(info);
    122       break;
    123     default:
    124       NOTREACHED();
    125   }
    126 }
    127 
    128 void ExtensionAccessibilityEventRouter::OnWindowOpened(
    129     const AccessibilityWindowInfo* info) {
    130   scoped_ptr<ListValue> args(ControlInfoToEventArguments(info));
    131   DispatchEvent(info->profile(),
    132                 experimental_accessibility::OnWindowOpened::kEventName,
    133                 args.Pass());
    134 }
    135 
    136 void ExtensionAccessibilityEventRouter::OnControlFocused(
    137     const AccessibilityControlInfo* info) {
    138   last_focused_control_dict_.Clear();
    139   info->SerializeToDict(&last_focused_control_dict_);
    140   scoped_ptr<ListValue> args(ControlInfoToEventArguments(info));
    141   DispatchEvent(info->profile(),
    142                 experimental_accessibility::OnControlFocused::kEventName,
    143                 args.Pass());
    144 }
    145 
    146 void ExtensionAccessibilityEventRouter::OnControlAction(
    147     const AccessibilityControlInfo* info) {
    148   scoped_ptr<ListValue> args(ControlInfoToEventArguments(info));
    149   DispatchEvent(info->profile(),
    150                 experimental_accessibility::OnControlAction::kEventName,
    151                 args.Pass());
    152 }
    153 
    154 void ExtensionAccessibilityEventRouter::OnTextChanged(
    155     const AccessibilityControlInfo* info) {
    156   scoped_ptr<ListValue> args(ControlInfoToEventArguments(info));
    157   DispatchEvent(info->profile(),
    158                 experimental_accessibility::OnTextChanged::kEventName,
    159                 args.Pass());
    160 }
    161 
    162 void ExtensionAccessibilityEventRouter::OnMenuOpened(
    163     const AccessibilityMenuInfo* info) {
    164   scoped_ptr<ListValue> args(ControlInfoToEventArguments(info));
    165   DispatchEvent(info->profile(),
    166                 experimental_accessibility::OnMenuOpened::kEventName,
    167                 args.Pass());
    168 }
    169 
    170 void ExtensionAccessibilityEventRouter::OnMenuClosed(
    171     const AccessibilityMenuInfo* info) {
    172   scoped_ptr<ListValue> args(ControlInfoToEventArguments(info));
    173   DispatchEvent(info->profile(),
    174                 experimental_accessibility::OnMenuClosed::kEventName,
    175                 args.Pass());
    176 }
    177 
    178 void ExtensionAccessibilityEventRouter::OnChromeVoxLoadStateChanged(
    179     Profile* profile,
    180     bool loading,
    181     bool make_announcements) {
    182   scoped_ptr<base::ListValue> event_args(new base::ListValue());
    183   event_args->Append(base::Value::CreateBooleanValue(loading));
    184   event_args->Append(base::Value::CreateBooleanValue(make_announcements));
    185   ExtensionAccessibilityEventRouter::DispatchEventToChromeVox(profile,
    186       experimental_accessibility::OnChromeVoxLoadStateChanged::kEventName,
    187       event_args.Pass());
    188 }
    189 
    190 // Static.
    191 void ExtensionAccessibilityEventRouter::DispatchEventToChromeVox(
    192     Profile* profile,
    193     const char* event_name,
    194     scoped_ptr<base::ListValue> event_args) {
    195   extensions::ExtensionSystem* system =
    196       extensions::ExtensionSystem::Get(profile);
    197   if (!system)
    198     return;
    199   scoped_ptr<extensions::Event> event(new extensions::Event(event_name,
    200                                                             event_args.Pass()));
    201   system->event_router()->DispatchEventWithLazyListener(
    202       extension_misc::kChromeVoxExtensionId, event.Pass());
    203 }
    204 
    205 void ExtensionAccessibilityEventRouter::DispatchEvent(
    206     Profile* profile,
    207     const char* event_name,
    208     scoped_ptr<base::ListValue> event_args) {
    209   if (enabled_ && profile &&
    210       extensions::ExtensionSystem::Get(profile)->event_router()) {
    211     scoped_ptr<extensions::Event> event(new extensions::Event(
    212         event_name, event_args.Pass()));
    213     extensions::ExtensionSystem::Get(profile)->event_router()->
    214         BroadcastEvent(event.Pass());
    215   }
    216 }
    217 
    218 bool AccessibilitySetAccessibilityEnabledFunction::RunImpl() {
    219   bool enabled;
    220   EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(0, &enabled));
    221   ExtensionAccessibilityEventRouter::GetInstance()
    222       ->SetAccessibilityEnabled(enabled);
    223   return true;
    224 }
    225 
    226 bool AccessibilitySetNativeAccessibilityEnabledFunction::RunImpl() {
    227   bool enabled;
    228   EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(0, &enabled));
    229   if (enabled) {
    230     content::BrowserAccessibilityState::GetInstance()->
    231         EnableAccessibility();
    232   } else {
    233     content::BrowserAccessibilityState::GetInstance()->
    234         DisableAccessibility();
    235   }
    236   return true;
    237 }
    238 
    239 bool AccessibilityGetFocusedControlFunction::RunImpl() {
    240   // Get the serialized dict from the last focused control and return it.
    241   // However, if the dict is empty, that means we haven't seen any focus
    242   // events yet, so return null instead.
    243   ExtensionAccessibilityEventRouter *accessibility_event_router =
    244       ExtensionAccessibilityEventRouter::GetInstance();
    245   DictionaryValue *last_focused_control_dict =
    246       accessibility_event_router->last_focused_control_dict();
    247   if (last_focused_control_dict->size()) {
    248     SetResult(last_focused_control_dict->DeepCopyWithoutEmptyChildren());
    249   } else {
    250     SetResult(Value::CreateNullValue());
    251   }
    252   return true;
    253 }
    254 
    255 bool AccessibilityGetAlertsForTabFunction::RunImpl() {
    256   int tab_id;
    257   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
    258 
    259   TabStripModel* tab_strip = NULL;
    260   content::WebContents* contents = NULL;
    261   int tab_index = -1;
    262   if (!extensions::ExtensionTabUtil::GetTabById(tab_id,
    263                                                 GetProfile(),
    264                                                 include_incognito(),
    265                                                 NULL,
    266                                                 &tab_strip,
    267                                                 &contents,
    268                                                 &tab_index)) {
    269     error_ = extensions::ErrorUtils::FormatErrorMessage(
    270         extensions::tabs_constants::kTabNotFoundError,
    271         base::IntToString(tab_id));
    272     return false;
    273   }
    274 
    275   ListValue* alerts_value = new ListValue;
    276 
    277   InfoBarService* infobar_service = InfoBarService::FromWebContents(contents);
    278   for (size_t i = 0; i < infobar_service->infobar_count(); ++i) {
    279     // TODO(hashimoto): Make other kind of alerts available.  crosbug.com/24281
    280     ConfirmInfoBarDelegate* confirm_infobar_delegate =
    281         infobar_service->infobar_at(i)->delegate()->AsConfirmInfoBarDelegate();
    282     if (confirm_infobar_delegate) {
    283       DictionaryValue* alert_value = new DictionaryValue;
    284       const base::string16 message_text =
    285           confirm_infobar_delegate->GetMessageText();
    286       alert_value->SetString(keys::kMessageKey, message_text);
    287       alerts_value->Append(alert_value);
    288     }
    289   }
    290 
    291   SetResult(alerts_value);
    292   return true;
    293 }
    294