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