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.
5 #include "chrome/browser/accessibility/accessibility_extension_api.h"
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"
28 namespace keys = extension_accessibility_api_constants;
29 namespace accessibility_private = extensions::api::accessibility_private;
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<base::ListValue> ControlInfoToEventArguments(
35 const AccessibilityEventInfo* info) {
36 base::DictionaryValue* dict = new base::DictionaryValue();
37 info->SerializeToDict(dict);
39 scoped_ptr<base::ListValue> args(new base::ListValue());
44 ExtensionAccessibilityEventRouter*
45 ExtensionAccessibilityEventRouter::GetInstance() {
46 return Singleton<ExtensionAccessibilityEventRouter>::get();
49 ExtensionAccessibilityEventRouter::ExtensionAccessibilityEventRouter()
53 ExtensionAccessibilityEventRouter::~ExtensionAccessibilityEventRouter() {
54 control_event_callback_.Reset();
57 void ExtensionAccessibilityEventRouter::SetAccessibilityEnabled(bool enabled) {
61 bool ExtensionAccessibilityEventRouter::IsAccessibilityEnabled() const {
65 void ExtensionAccessibilityEventRouter::SetControlEventCallbackForTesting(
66 ControlEventCallback control_event_callback) {
67 DCHECK(control_event_callback_.is_null());
68 control_event_callback_ = control_event_callback;
71 void ExtensionAccessibilityEventRouter::ClearControlEventCallback() {
72 control_event_callback_.Reset();
75 void ExtensionAccessibilityEventRouter::HandleWindowEvent(
77 const AccessibilityWindowInfo* info) {
78 if (!control_event_callback_.is_null())
79 control_event_callback_.Run(event, info);
81 if (event == ui::AX_EVENT_ALERT)
85 void ExtensionAccessibilityEventRouter::HandleMenuEvent(
87 const AccessibilityMenuInfo* info) {
89 case ui::AX_EVENT_MENU_START:
90 case ui::AX_EVENT_MENU_POPUP_START:
93 case ui::AX_EVENT_MENU_END:
94 case ui::AX_EVENT_MENU_POPUP_END:
97 case ui::AX_EVENT_FOCUS:
98 OnControlFocused(info);
100 case ui::AX_EVENT_HOVER:
101 OnControlHover(info);
108 void ExtensionAccessibilityEventRouter::HandleControlEvent(
110 const AccessibilityControlInfo* info) {
111 if (!control_event_callback_.is_null())
112 control_event_callback_.Run(event, info);
115 case ui::AX_EVENT_TEXT_CHANGED:
116 case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
119 case ui::AX_EVENT_VALUE_CHANGED:
120 case ui::AX_EVENT_ALERT:
121 OnControlAction(info);
123 case ui::AX_EVENT_FOCUS:
124 OnControlFocused(info);
126 case ui::AX_EVENT_HOVER:
127 OnControlHover(info);
134 void ExtensionAccessibilityEventRouter::OnWindowOpened(
135 const AccessibilityWindowInfo* info) {
136 scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
137 DispatchEvent(info->profile(),
138 accessibility_private::OnWindowOpened::kEventName,
142 void ExtensionAccessibilityEventRouter::OnControlFocused(
143 const AccessibilityControlInfo* info) {
144 last_focused_control_dict_.Clear();
145 info->SerializeToDict(&last_focused_control_dict_);
146 scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
147 DispatchEvent(info->profile(),
148 accessibility_private::OnControlFocused::kEventName,
152 void ExtensionAccessibilityEventRouter::OnControlAction(
153 const AccessibilityControlInfo* info) {
154 scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
155 DispatchEvent(info->profile(),
156 accessibility_private::OnControlAction::kEventName,
160 void ExtensionAccessibilityEventRouter::OnControlHover(
161 const AccessibilityControlInfo* info) {
162 scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
163 DispatchEvent(info->profile(),
164 accessibility_private::OnControlHover::kEventName,
168 void ExtensionAccessibilityEventRouter::OnTextChanged(
169 const AccessibilityControlInfo* info) {
170 scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
171 DispatchEvent(info->profile(),
172 accessibility_private::OnTextChanged::kEventName,
176 void ExtensionAccessibilityEventRouter::OnMenuOpened(
177 const AccessibilityMenuInfo* info) {
178 scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
179 DispatchEvent(info->profile(),
180 accessibility_private::OnMenuOpened::kEventName,
184 void ExtensionAccessibilityEventRouter::OnMenuClosed(
185 const AccessibilityMenuInfo* info) {
186 scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
187 DispatchEvent(info->profile(),
188 accessibility_private::OnMenuClosed::kEventName,
192 void ExtensionAccessibilityEventRouter::OnChromeVoxLoadStateChanged(
195 bool make_announcements) {
196 scoped_ptr<base::ListValue> event_args(new base::ListValue());
197 event_args->AppendBoolean(loading);
198 event_args->AppendBoolean(make_announcements);
199 ExtensionAccessibilityEventRouter::DispatchEventToChromeVox(
201 accessibility_private::OnChromeVoxLoadStateChanged::kEventName,
206 void ExtensionAccessibilityEventRouter::DispatchEventToChromeVox(
208 const char* event_name,
209 scoped_ptr<base::ListValue> event_args) {
210 extensions::ExtensionSystem* system =
211 extensions::ExtensionSystem::Get(profile);
214 scoped_ptr<extensions::Event> event(new extensions::Event(event_name,
216 system->event_router()->DispatchEventWithLazyListener(
217 extension_misc::kChromeVoxExtensionId, event.Pass());
220 void ExtensionAccessibilityEventRouter::DispatchEvent(
222 const char* event_name,
223 scoped_ptr<base::ListValue> event_args) {
224 if (!enabled_ || !profile)
226 extensions::EventRouter* event_router = extensions::EventRouter::Get(profile);
230 scoped_ptr<extensions::Event> event(new extensions::Event(
231 event_name, event_args.Pass()));
232 event_router->BroadcastEvent(event.Pass());
235 bool AccessibilityPrivateSetAccessibilityEnabledFunction::RunSync() {
237 EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(0, &enabled));
238 ExtensionAccessibilityEventRouter::GetInstance()
239 ->SetAccessibilityEnabled(enabled);
243 bool AccessibilityPrivateSetNativeAccessibilityEnabledFunction::RunSync() {
245 EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(0, &enabled));
247 content::BrowserAccessibilityState::GetInstance()->
248 EnableAccessibility();
250 content::BrowserAccessibilityState::GetInstance()->
251 DisableAccessibility();
256 bool AccessibilityPrivateGetFocusedControlFunction::RunSync() {
257 // Get the serialized dict from the last focused control and return it.
258 // However, if the dict is empty, that means we haven't seen any focus
259 // events yet, so return null instead.
260 ExtensionAccessibilityEventRouter *accessibility_event_router =
261 ExtensionAccessibilityEventRouter::GetInstance();
262 base::DictionaryValue *last_focused_control_dict =
263 accessibility_event_router->last_focused_control_dict();
264 if (last_focused_control_dict->size()) {
265 SetResult(last_focused_control_dict->DeepCopyWithoutEmptyChildren());
267 SetResult(base::Value::CreateNullValue());
272 bool AccessibilityPrivateGetAlertsForTabFunction::RunSync() {
274 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
276 TabStripModel* tab_strip = NULL;
277 content::WebContents* contents = NULL;
279 if (!extensions::ExtensionTabUtil::GetTabById(tab_id,
286 error_ = extensions::ErrorUtils::FormatErrorMessage(
287 extensions::tabs_constants::kTabNotFoundError,
288 base::IntToString(tab_id));
292 base::ListValue* alerts_value = new base::ListValue;
294 InfoBarService* infobar_service = InfoBarService::FromWebContents(contents);
295 for (size_t i = 0; i < infobar_service->infobar_count(); ++i) {
296 // TODO(hashimoto): Make other kind of alerts available. crosbug.com/24281
297 ConfirmInfoBarDelegate* confirm_infobar_delegate =
298 infobar_service->infobar_at(i)->delegate()->AsConfirmInfoBarDelegate();
299 if (confirm_infobar_delegate) {
300 base::DictionaryValue* alert_value = new base::DictionaryValue;
301 const base::string16 message_text =
302 confirm_infobar_delegate->GetMessageText();
303 alert_value->SetString(keys::kMessageKey, message_text);
304 alerts_value->Append(alert_value);
308 SetResult(alerts_value);