Upstream version 7.36.149.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/confirm_infobar_delegate.h"
15 #include "chrome/browser/infobars/infobar_service.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/common/extensions/api/accessibility_private.h"
18 #include "components/infobars/core/infobar.h"
19 #include "content/public/browser/browser_accessibility_state.h"
20 #include "extensions/browser/event_router.h"
21 #include "extensions/browser/extension_host.h"
22 #include "extensions/browser/extension_system.h"
23 #include "extensions/browser/lazy_background_task_queue.h"
24 #include "extensions/common/error_utils.h"
25 #include "extensions/common/manifest_handlers/background_info.h"
26
27 namespace keys = extension_accessibility_api_constants;
28 namespace accessibility_private = extensions::api::accessibility_private;
29
30 // Returns the AccessibilityControlInfo serialized into a JSON string,
31 // consisting of an array of a single object of type AccessibilityObject,
32 // as defined in the accessibility extension api's json schema.
33 scoped_ptr<base::ListValue> ControlInfoToEventArguments(
34     const AccessibilityEventInfo* info) {
35   base::DictionaryValue* dict = new base::DictionaryValue();
36   info->SerializeToDict(dict);
37
38   scoped_ptr<base::ListValue> args(new base::ListValue());
39   args->Append(dict);
40   return args.Pass();
41 }
42
43 ExtensionAccessibilityEventRouter*
44     ExtensionAccessibilityEventRouter::GetInstance() {
45   return Singleton<ExtensionAccessibilityEventRouter>::get();
46 }
47
48 ExtensionAccessibilityEventRouter::ExtensionAccessibilityEventRouter()
49     : enabled_(false) {
50 }
51
52 ExtensionAccessibilityEventRouter::~ExtensionAccessibilityEventRouter() {
53   control_event_callback_.Reset();
54 }
55
56 void ExtensionAccessibilityEventRouter::SetAccessibilityEnabled(bool enabled) {
57   enabled_ = enabled;
58 }
59
60 bool ExtensionAccessibilityEventRouter::IsAccessibilityEnabled() const {
61   return enabled_;
62 }
63
64 void ExtensionAccessibilityEventRouter::SetControlEventCallbackForTesting(
65     ControlEventCallback control_event_callback) {
66   DCHECK(control_event_callback_.is_null());
67   control_event_callback_ = control_event_callback;
68 }
69
70 void ExtensionAccessibilityEventRouter::ClearControlEventCallback() {
71   control_event_callback_.Reset();
72 }
73
74 void ExtensionAccessibilityEventRouter::HandleWindowEvent(
75     ui::AXEvent event,
76     const AccessibilityWindowInfo* info) {
77   if (!control_event_callback_.is_null())
78     control_event_callback_.Run(event, info);
79
80   if (event == ui::AX_EVENT_ALERT)
81     OnWindowOpened(info);
82 }
83
84 void ExtensionAccessibilityEventRouter::HandleMenuEvent(
85     ui::AXEvent event,
86     const AccessibilityMenuInfo* info) {
87   switch (event) {
88     case ui::AX_EVENT_MENU_START:
89     case ui::AX_EVENT_MENU_POPUP_START:
90       OnMenuOpened(info);
91       break;
92     case ui::AX_EVENT_MENU_END:
93     case ui::AX_EVENT_MENU_POPUP_END:
94       OnMenuClosed(info);
95       break;
96     case ui::AX_EVENT_FOCUS:
97       OnControlFocused(info);
98       break;
99     default:
100       NOTREACHED();
101   }
102 }
103
104 void ExtensionAccessibilityEventRouter::HandleControlEvent(
105     ui::AXEvent event,
106     const AccessibilityControlInfo* info) {
107   if (!control_event_callback_.is_null())
108     control_event_callback_.Run(event, info);
109
110   switch (event) {
111     case ui::AX_EVENT_TEXT_CHANGED:
112     case ui::AX_EVENT_SELECTION_CHANGED:
113       OnTextChanged(info);
114       break;
115     case ui::AX_EVENT_VALUE_CHANGED:
116     case ui::AX_EVENT_ALERT:
117       OnControlAction(info);
118       break;
119     case ui::AX_EVENT_FOCUS:
120       OnControlFocused(info);
121       break;
122     default:
123       NOTREACHED();
124   }
125 }
126
127 void ExtensionAccessibilityEventRouter::OnWindowOpened(
128     const AccessibilityWindowInfo* info) {
129   scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
130   DispatchEvent(info->profile(),
131                 accessibility_private::OnWindowOpened::kEventName,
132                 args.Pass());
133 }
134
135 void ExtensionAccessibilityEventRouter::OnControlFocused(
136     const AccessibilityControlInfo* info) {
137   last_focused_control_dict_.Clear();
138   info->SerializeToDict(&last_focused_control_dict_);
139   scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
140   DispatchEvent(info->profile(),
141                 accessibility_private::OnControlFocused::kEventName,
142                 args.Pass());
143 }
144
145 void ExtensionAccessibilityEventRouter::OnControlAction(
146     const AccessibilityControlInfo* info) {
147   scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
148   DispatchEvent(info->profile(),
149                 accessibility_private::OnControlAction::kEventName,
150                 args.Pass());
151 }
152
153 void ExtensionAccessibilityEventRouter::OnTextChanged(
154     const AccessibilityControlInfo* info) {
155   scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
156   DispatchEvent(info->profile(),
157                 accessibility_private::OnTextChanged::kEventName,
158                 args.Pass());
159 }
160
161 void ExtensionAccessibilityEventRouter::OnMenuOpened(
162     const AccessibilityMenuInfo* info) {
163   scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
164   DispatchEvent(info->profile(),
165                 accessibility_private::OnMenuOpened::kEventName,
166                 args.Pass());
167 }
168
169 void ExtensionAccessibilityEventRouter::OnMenuClosed(
170     const AccessibilityMenuInfo* info) {
171   scoped_ptr<base::ListValue> args(ControlInfoToEventArguments(info));
172   DispatchEvent(info->profile(),
173                 accessibility_private::OnMenuClosed::kEventName,
174                 args.Pass());
175 }
176
177 void ExtensionAccessibilityEventRouter::OnChromeVoxLoadStateChanged(
178     Profile* profile,
179     bool loading,
180     bool make_announcements) {
181   scoped_ptr<base::ListValue> event_args(new base::ListValue());
182   event_args->Append(base::Value::CreateBooleanValue(loading));
183   event_args->Append(base::Value::CreateBooleanValue(make_announcements));
184   ExtensionAccessibilityEventRouter::DispatchEventToChromeVox(
185       profile,
186       accessibility_private::OnChromeVoxLoadStateChanged::kEventName,
187       event_args.Pass());
188 }
189
190 // Static.
191 void ExtensionAccessibilityEventRouter::DispatchEventToChromeVox(
192     Profile* profile,
193     const char* event_name,
194     scoped_ptr<base::ListValue> event_args) {
195   extensions::ExtensionSystem* system =
196       extensions::ExtensionSystem::Get(profile);
197   if (!system)
198     return;
199   scoped_ptr<extensions::Event> event(new extensions::Event(event_name,
200                                                             event_args.Pass()));
201   system->event_router()->DispatchEventWithLazyListener(
202       extension_misc::kChromeVoxExtensionId, event.Pass());
203 }
204
205 void ExtensionAccessibilityEventRouter::DispatchEvent(
206     Profile* profile,
207     const char* event_name,
208     scoped_ptr<base::ListValue> event_args) {
209   if (!enabled_ || !profile)
210     return;
211   extensions::EventRouter* event_router = extensions::EventRouter::Get(profile);
212   if (!event_router)
213     return;
214
215   scoped_ptr<extensions::Event> event(new extensions::Event(
216       event_name, event_args.Pass()));
217   event_router->BroadcastEvent(event.Pass());
218 }
219
220 bool AccessibilityPrivateSetAccessibilityEnabledFunction::RunSync() {
221   bool enabled;
222   EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(0, &enabled));
223   ExtensionAccessibilityEventRouter::GetInstance()
224       ->SetAccessibilityEnabled(enabled);
225   return true;
226 }
227
228 bool AccessibilityPrivateSetNativeAccessibilityEnabledFunction::RunSync() {
229   bool enabled;
230   EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(0, &enabled));
231   if (enabled) {
232     content::BrowserAccessibilityState::GetInstance()->
233         EnableAccessibility();
234   } else {
235     content::BrowserAccessibilityState::GetInstance()->
236         DisableAccessibility();
237   }
238   return true;
239 }
240
241 bool AccessibilityPrivateGetFocusedControlFunction::RunSync() {
242   // Get the serialized dict from the last focused control and return it.
243   // However, if the dict is empty, that means we haven't seen any focus
244   // events yet, so return null instead.
245   ExtensionAccessibilityEventRouter *accessibility_event_router =
246       ExtensionAccessibilityEventRouter::GetInstance();
247   base::DictionaryValue *last_focused_control_dict =
248       accessibility_event_router->last_focused_control_dict();
249   if (last_focused_control_dict->size()) {
250     SetResult(last_focused_control_dict->DeepCopyWithoutEmptyChildren());
251   } else {
252     SetResult(base::Value::CreateNullValue());
253   }
254   return true;
255 }
256
257 bool AccessibilityPrivateGetAlertsForTabFunction::RunSync() {
258   int tab_id;
259   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
260
261   TabStripModel* tab_strip = NULL;
262   content::WebContents* contents = NULL;
263   int tab_index = -1;
264   if (!extensions::ExtensionTabUtil::GetTabById(tab_id,
265                                                 GetProfile(),
266                                                 include_incognito(),
267                                                 NULL,
268                                                 &tab_strip,
269                                                 &contents,
270                                                 &tab_index)) {
271     error_ = extensions::ErrorUtils::FormatErrorMessage(
272         extensions::tabs_constants::kTabNotFoundError,
273         base::IntToString(tab_id));
274     return false;
275   }
276
277   base::ListValue* alerts_value = new base::ListValue;
278
279   InfoBarService* infobar_service = InfoBarService::FromWebContents(contents);
280   for (size_t i = 0; i < infobar_service->infobar_count(); ++i) {
281     // TODO(hashimoto): Make other kind of alerts available.  crosbug.com/24281
282     ConfirmInfoBarDelegate* confirm_infobar_delegate =
283         infobar_service->infobar_at(i)->delegate()->AsConfirmInfoBarDelegate();
284     if (confirm_infobar_delegate) {
285       base::DictionaryValue* alert_value = new base::DictionaryValue;
286       const base::string16 message_text =
287           confirm_infobar_delegate->GetMessageText();
288       alert_value->SetString(keys::kMessageKey, message_text);
289       alerts_value->Append(alert_value);
290     }
291   }
292
293   SetResult(alerts_value);
294   return true;
295 }