Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / accessibility / accessibility_extension_api.cc
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 }