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