Upstream version 9.38.198.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 namespace keys = extension_accessibility_api_constants;
29 namespace accessibility_private = extensions::api::accessibility_private;
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<base::ListValue> ControlInfoToEventArguments(
35     const AccessibilityEventInfo* info) {
36   base::DictionaryValue* dict = new base::DictionaryValue();
37   info->SerializeToDict(dict);
38
39   scoped_ptr<base::ListValue> args(new base::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::AXEvent event,
77     const AccessibilityWindowInfo* info) {
78   if (!control_event_callback_.is_null())
79     control_event_callback_.Run(event, info);
80
81   if (event == ui::AX_EVENT_ALERT)
82     OnWindowOpened(info);
83 }
84
85 void ExtensionAccessibilityEventRouter::HandleMenuEvent(
86     ui::AXEvent event,
87     const AccessibilityMenuInfo* info) {
88   switch (event) {
89     case ui::AX_EVENT_MENU_START:
90     case ui::AX_EVENT_MENU_POPUP_START:
91       OnMenuOpened(info);
92       break;
93     case ui::AX_EVENT_MENU_END:
94     case ui::AX_EVENT_MENU_POPUP_END:
95       OnMenuClosed(info);
96       break;
97     case ui::AX_EVENT_FOCUS:
98       OnControlFocused(info);
99       break;
100     case ui::AX_EVENT_HOVER:
101       OnControlHover(info);
102       break;
103     default:
104       NOTREACHED();
105   }
106 }
107
108 void ExtensionAccessibilityEventRouter::HandleControlEvent(
109     ui::AXEvent event,
110     const AccessibilityControlInfo* info) {
111   if (!control_event_callback_.is_null())
112     control_event_callback_.Run(event, info);
113
114   switch (event) {
115     case ui::AX_EVENT_TEXT_CHANGED:
116     case ui::AX_EVENT_TEXT_SELECTION_CHANGED:
117       OnTextChanged(info);
118       break;
119     case ui::AX_EVENT_VALUE_CHANGED:
120     case ui::AX_EVENT_ALERT:
121       OnControlAction(info);
122       break;
123     case ui::AX_EVENT_FOCUS:
124       OnControlFocused(info);
125       break;
126     case ui::AX_EVENT_HOVER:
127       OnControlHover(info);
128       break;
129     default:
130       NOTREACHED();
131   }
132 }
133
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,
139                 args.Pass());
140 }
141
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,
149                 args.Pass());
150 }
151
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,
157                 args.Pass());
158 }
159
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,
165                 args.Pass());
166 }
167
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,
173                 args.Pass());
174 }
175
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,
181                 args.Pass());
182 }
183
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,
189                 args.Pass());
190 }
191
192 void ExtensionAccessibilityEventRouter::OnChromeVoxLoadStateChanged(
193     Profile* profile,
194     bool loading,
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(
200       profile,
201       accessibility_private::OnChromeVoxLoadStateChanged::kEventName,
202       event_args.Pass());
203 }
204
205 // Static.
206 void ExtensionAccessibilityEventRouter::DispatchEventToChromeVox(
207     Profile* profile,
208     const char* event_name,
209     scoped_ptr<base::ListValue> event_args) {
210   extensions::ExtensionSystem* system =
211       extensions::ExtensionSystem::Get(profile);
212   if (!system)
213     return;
214   scoped_ptr<extensions::Event> event(new extensions::Event(event_name,
215                                                             event_args.Pass()));
216   system->event_router()->DispatchEventWithLazyListener(
217       extension_misc::kChromeVoxExtensionId, event.Pass());
218 }
219
220 void ExtensionAccessibilityEventRouter::DispatchEvent(
221     Profile* profile,
222     const char* event_name,
223     scoped_ptr<base::ListValue> event_args) {
224   if (!enabled_ || !profile)
225     return;
226   extensions::EventRouter* event_router = extensions::EventRouter::Get(profile);
227   if (!event_router)
228     return;
229
230   scoped_ptr<extensions::Event> event(new extensions::Event(
231       event_name, event_args.Pass()));
232   event_router->BroadcastEvent(event.Pass());
233 }
234
235 bool AccessibilityPrivateSetAccessibilityEnabledFunction::RunSync() {
236   bool enabled;
237   EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(0, &enabled));
238   ExtensionAccessibilityEventRouter::GetInstance()
239       ->SetAccessibilityEnabled(enabled);
240   return true;
241 }
242
243 bool AccessibilityPrivateSetNativeAccessibilityEnabledFunction::RunSync() {
244   bool enabled;
245   EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(0, &enabled));
246   if (enabled) {
247     content::BrowserAccessibilityState::GetInstance()->
248         EnableAccessibility();
249   } else {
250     content::BrowserAccessibilityState::GetInstance()->
251         DisableAccessibility();
252   }
253   return true;
254 }
255
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());
266   } else {
267     SetResult(base::Value::CreateNullValue());
268   }
269   return true;
270 }
271
272 bool AccessibilityPrivateGetAlertsForTabFunction::RunSync() {
273   int tab_id;
274   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
275
276   TabStripModel* tab_strip = NULL;
277   content::WebContents* contents = NULL;
278   int tab_index = -1;
279   if (!extensions::ExtensionTabUtil::GetTabById(tab_id,
280                                                 GetProfile(),
281                                                 include_incognito(),
282                                                 NULL,
283                                                 &tab_strip,
284                                                 &contents,
285                                                 &tab_index)) {
286     error_ = extensions::ErrorUtils::FormatErrorMessage(
287         extensions::tabs_constants::kTabNotFoundError,
288         base::IntToString(tab_id));
289     return false;
290   }
291
292   base::ListValue* alerts_value = new base::ListValue;
293
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);
305     }
306   }
307
308   SetResult(alerts_value);
309   return true;
310 }