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/chrome_notification_types.h" 12 #include "chrome/browser/extensions/api/tabs/tabs_constants.h" 13 #include "chrome/browser/extensions/event_router.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_delegate.h" 18 #include "chrome/browser/infobars/infobar_service.h" 19 #include "chrome/browser/profiles/profile.h" 20 #include "content/public/browser/browser_accessibility_state.h" 21 #include "content/public/browser/notification_service.h" 22 #include "extensions/common/error_utils.h" 23 24 namespace keys = extension_accessibility_api_constants; 25 26 // Returns the AccessibilityControlInfo serialized into a JSON string, 27 // consisting of an array of a single object of type AccessibilityObject, 28 // as defined in the accessibility extension api's json schema. 29 scoped_ptr<ListValue> ControlInfoToEventArguments( 30 const AccessibilityEventInfo* info) { 31 DictionaryValue* dict = new DictionaryValue(); 32 info->SerializeToDict(dict); 33 34 scoped_ptr<ListValue> args(new ListValue()); 35 args->Append(dict); 36 return args.Pass(); 37 } 38 39 ExtensionAccessibilityEventRouter* 40 ExtensionAccessibilityEventRouter::GetInstance() { 41 return Singleton<ExtensionAccessibilityEventRouter>::get(); 42 } 43 44 ExtensionAccessibilityEventRouter::ExtensionAccessibilityEventRouter() 45 : enabled_(false) { 46 registrar_.Add(this, 47 chrome::NOTIFICATION_ACCESSIBILITY_WINDOW_OPENED, 48 content::NotificationService::AllSources()); 49 registrar_.Add(this, 50 chrome::NOTIFICATION_ACCESSIBILITY_WINDOW_CLOSED, 51 content::NotificationService::AllSources()); 52 registrar_.Add(this, 53 chrome::NOTIFICATION_ACCESSIBILITY_CONTROL_FOCUSED, 54 content::NotificationService::AllSources()); 55 registrar_.Add(this, 56 chrome::NOTIFICATION_ACCESSIBILITY_CONTROL_ACTION, 57 content::NotificationService::AllSources()); 58 registrar_.Add(this, 59 chrome::NOTIFICATION_ACCESSIBILITY_TEXT_CHANGED, 60 content::NotificationService::AllSources()); 61 registrar_.Add(this, 62 chrome::NOTIFICATION_ACCESSIBILITY_MENU_OPENED, 63 content::NotificationService::AllSources()); 64 registrar_.Add(this, 65 chrome::NOTIFICATION_ACCESSIBILITY_MENU_CLOSED, 66 content::NotificationService::AllSources()); 67 } 68 69 ExtensionAccessibilityEventRouter::~ExtensionAccessibilityEventRouter() { 70 } 71 72 void ExtensionAccessibilityEventRouter::Observe( 73 int type, 74 const content::NotificationSource& source, 75 const content::NotificationDetails& details) { 76 switch (type) { 77 case chrome::NOTIFICATION_ACCESSIBILITY_WINDOW_OPENED: 78 OnWindowOpened( 79 content::Details<const AccessibilityWindowInfo>(details).ptr()); 80 break; 81 case chrome::NOTIFICATION_ACCESSIBILITY_WINDOW_CLOSED: 82 OnWindowClosed( 83 content::Details<const AccessibilityWindowInfo>(details).ptr()); 84 break; 85 case chrome::NOTIFICATION_ACCESSIBILITY_CONTROL_FOCUSED: 86 OnControlFocused( 87 content::Details<const AccessibilityControlInfo>(details).ptr()); 88 break; 89 case chrome::NOTIFICATION_ACCESSIBILITY_CONTROL_ACTION: 90 OnControlAction( 91 content::Details<const AccessibilityControlInfo>(details).ptr()); 92 break; 93 case chrome::NOTIFICATION_ACCESSIBILITY_TEXT_CHANGED: 94 OnTextChanged( 95 content::Details<const AccessibilityControlInfo>(details).ptr()); 96 break; 97 case chrome::NOTIFICATION_ACCESSIBILITY_MENU_OPENED: 98 OnMenuOpened( 99 content::Details<const AccessibilityMenuInfo>(details).ptr()); 100 break; 101 case chrome::NOTIFICATION_ACCESSIBILITY_MENU_CLOSED: 102 OnMenuClosed( 103 content::Details<const AccessibilityMenuInfo>(details).ptr()); 104 break; 105 default: 106 NOTREACHED(); 107 } 108 } 109 110 void ExtensionAccessibilityEventRouter::SetAccessibilityEnabled(bool enabled) { 111 enabled_ = enabled; 112 } 113 114 bool ExtensionAccessibilityEventRouter::IsAccessibilityEnabled() const { 115 return enabled_; 116 } 117 118 void ExtensionAccessibilityEventRouter::OnWindowOpened( 119 const AccessibilityWindowInfo* info) { 120 scoped_ptr<ListValue> args(ControlInfoToEventArguments(info)); 121 DispatchEvent(info->profile(), keys::kOnWindowOpened, args.Pass()); 122 } 123 124 void ExtensionAccessibilityEventRouter::OnWindowClosed( 125 const AccessibilityWindowInfo* info) { 126 scoped_ptr<ListValue> args(ControlInfoToEventArguments(info)); 127 DispatchEvent(info->profile(), keys::kOnWindowClosed, args.Pass()); 128 } 129 130 void ExtensionAccessibilityEventRouter::OnControlFocused( 131 const AccessibilityControlInfo* info) { 132 last_focused_control_dict_.Clear(); 133 info->SerializeToDict(&last_focused_control_dict_); 134 scoped_ptr<ListValue> args(ControlInfoToEventArguments(info)); 135 DispatchEvent(info->profile(), keys::kOnControlFocused, args.Pass()); 136 } 137 138 void ExtensionAccessibilityEventRouter::OnControlAction( 139 const AccessibilityControlInfo* info) { 140 scoped_ptr<ListValue> args(ControlInfoToEventArguments(info)); 141 DispatchEvent(info->profile(), keys::kOnControlAction, args.Pass()); 142 } 143 144 void ExtensionAccessibilityEventRouter::OnTextChanged( 145 const AccessibilityControlInfo* info) { 146 scoped_ptr<ListValue> args(ControlInfoToEventArguments(info)); 147 DispatchEvent(info->profile(), keys::kOnTextChanged, args.Pass()); 148 } 149 150 void ExtensionAccessibilityEventRouter::OnMenuOpened( 151 const AccessibilityMenuInfo* info) { 152 scoped_ptr<ListValue> args(ControlInfoToEventArguments(info)); 153 DispatchEvent(info->profile(), keys::kOnMenuOpened, args.Pass()); 154 } 155 156 void ExtensionAccessibilityEventRouter::OnMenuClosed( 157 const AccessibilityMenuInfo* info) { 158 scoped_ptr<ListValue> args(ControlInfoToEventArguments(info)); 159 DispatchEvent(info->profile(), keys::kOnMenuClosed, args.Pass()); 160 } 161 162 void ExtensionAccessibilityEventRouter::DispatchEvent( 163 Profile* profile, 164 const char* event_name, 165 scoped_ptr<base::ListValue> event_args) { 166 if (enabled_ && profile && 167 extensions::ExtensionSystem::Get(profile)->event_router()) { 168 scoped_ptr<extensions::Event> event(new extensions::Event( 169 event_name, event_args.Pass())); 170 extensions::ExtensionSystem::Get(profile)->event_router()-> 171 BroadcastEvent(event.Pass()); 172 } 173 } 174 175 bool AccessibilitySetAccessibilityEnabledFunction::RunImpl() { 176 bool enabled; 177 EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(0, &enabled)); 178 ExtensionAccessibilityEventRouter::GetInstance() 179 ->SetAccessibilityEnabled(enabled); 180 return true; 181 } 182 183 bool AccessibilitySetNativeAccessibilityEnabledFunction::RunImpl() { 184 bool enabled; 185 EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(0, &enabled)); 186 if (enabled) { 187 content::BrowserAccessibilityState::GetInstance()-> 188 EnableAccessibility(); 189 } else { 190 content::BrowserAccessibilityState::GetInstance()-> 191 DisableAccessibility(); 192 } 193 return true; 194 } 195 196 bool AccessibilityGetFocusedControlFunction::RunImpl() { 197 // Get the serialized dict from the last focused control and return it. 198 // However, if the dict is empty, that means we haven't seen any focus 199 // events yet, so return null instead. 200 ExtensionAccessibilityEventRouter *accessibility_event_router = 201 ExtensionAccessibilityEventRouter::GetInstance(); 202 DictionaryValue *last_focused_control_dict = 203 accessibility_event_router->last_focused_control_dict(); 204 if (last_focused_control_dict->size()) { 205 SetResult(last_focused_control_dict->DeepCopyWithoutEmptyChildren()); 206 } else { 207 SetResult(Value::CreateNullValue()); 208 } 209 return true; 210 } 211 212 bool AccessibilityGetAlertsForTabFunction::RunImpl() { 213 int tab_id; 214 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); 215 216 TabStripModel* tab_strip = NULL; 217 content::WebContents* contents = NULL; 218 int tab_index = -1; 219 if (!ExtensionTabUtil::GetTabById(tab_id, profile(), include_incognito(), 220 NULL, &tab_strip, &contents, &tab_index)) { 221 error_ = extensions::ErrorUtils::FormatErrorMessage( 222 extensions::tabs_constants::kTabNotFoundError, 223 base::IntToString(tab_id)); 224 return false; 225 } 226 227 ListValue* alerts_value = new ListValue; 228 229 InfoBarService* infobar_service = InfoBarService::FromWebContents(contents); 230 for (size_t i = 0; i < infobar_service->infobar_count(); ++i) { 231 // TODO(hashimoto): Make other kind of alerts available. crosbug.com/24281 232 ConfirmInfoBarDelegate* confirm_infobar_delegate = 233 infobar_service->infobar_at(i)->AsConfirmInfoBarDelegate(); 234 if (confirm_infobar_delegate) { 235 DictionaryValue* alert_value = new DictionaryValue; 236 const string16 message_text = confirm_infobar_delegate->GetMessageText(); 237 alert_value->SetString(keys::kMessageKey, message_text); 238 alerts_value->Append(alert_value); 239 } 240 } 241 242 SetResult(alerts_value); 243 return true; 244 } 245