Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / automation_internal / automation_internal_api.cc
1 // Copyright 2014 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/extensions/api/automation_internal/automation_internal_api.h"
6
7 #include <vector>
8
9 #include "base/strings/string_number_conversions.h"
10 #include "chrome/browser/extensions/api/automation_internal/automation_action_adapter.h"
11 #include "chrome/browser/extensions/api/automation_internal/automation_util.h"
12 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
13 #include "chrome/browser/extensions/extension_tab_util.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/tabs/tab_strip_model.h"
16 #include "chrome/common/extensions/api/automation_internal.h"
17 #include "chrome/common/extensions/manifest_handlers/automation.h"
18 #include "content/public/browser/ax_event_notification_details.h"
19 #include "content/public/browser/render_frame_host.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "content/public/browser/render_widget_host.h"
22 #include "content/public/browser/render_widget_host_view.h"
23 #include "content/public/browser/web_contents.h"
24 #include "extensions/common/permissions/permissions_data.h"
25
26 #if defined(OS_CHROMEOS)
27 #include "chrome/browser/ui/ash/accessibility/automation_manager_ash.h"
28 #endif
29
30 namespace extensions {
31 class AutomationWebContentsObserver;
32 }  // namespace extensions
33
34 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::AutomationWebContentsObserver);
35
36 namespace {
37 const int kDesktopProcessID = 0;
38 const int kDesktopRoutingID = 0;
39
40 const char kCannotRequestAutomationOnPage[] =
41     "Cannot request automation tree on url \"*\". "
42     "Extension manifest must request permission to access this host.";
43 }  // namespace
44
45 namespace extensions {
46
47 bool CanRequestAutomation(const Extension* extension,
48                           const AutomationInfo* automation_info,
49                           const content::WebContents* contents) {
50   if (automation_info->desktop)
51     return true;
52
53   const GURL& url = contents->GetURL();
54   // TODO(aboxhall): check for webstore URL
55   if (automation_info->matches.MatchesURL(url))
56     return true;
57
58   int tab_id = ExtensionTabUtil::GetTabId(contents);
59   content::RenderProcessHost* process = contents->GetRenderProcessHost();
60   int process_id = process ? process->GetID() : -1;
61   std::string unused_error;
62   return extension->permissions_data()->CanAccessPage(
63       extension, url, url, tab_id, process_id, &unused_error);
64 }
65
66 // Helper class that receives accessibility data from |WebContents|.
67 class AutomationWebContentsObserver
68     : public content::WebContentsObserver,
69       public content::WebContentsUserData<AutomationWebContentsObserver> {
70  public:
71   virtual ~AutomationWebContentsObserver() {}
72
73   // content::WebContentsObserver overrides.
74   virtual void AccessibilityEventReceived(
75       const std::vector<content::AXEventNotificationDetails>& details)
76       OVERRIDE {
77     automation_util::DispatchAccessibilityEventsToAutomation(
78         details, browser_context_);
79   }
80
81   virtual void RenderFrameDeleted(
82       content::RenderFrameHost* render_frame_host) OVERRIDE {
83     automation_util::DispatchTreeDestroyedEventToAutomation(
84         render_frame_host->GetProcess()->GetID(),
85         render_frame_host->GetRoutingID(),
86         browser_context_);
87   }
88
89  private:
90   friend class content::WebContentsUserData<AutomationWebContentsObserver>;
91
92   AutomationWebContentsObserver(
93       content::WebContents* web_contents)
94       : content::WebContentsObserver(web_contents),
95         browser_context_(web_contents->GetBrowserContext()) {}
96
97   content::BrowserContext* browser_context_;
98
99   DISALLOW_COPY_AND_ASSIGN(AutomationWebContentsObserver);
100 };
101
102 // Helper class that implements an action adapter for a |RenderFrameHost|.
103 class RenderFrameHostActionAdapter : public AutomationActionAdapter {
104  public:
105   explicit RenderFrameHostActionAdapter(content::RenderFrameHost* rfh)
106       : rfh_(rfh) {}
107
108   virtual ~RenderFrameHostActionAdapter() {}
109
110   // AutomationActionAdapter implementation.
111   virtual void DoDefault(int32 id) OVERRIDE {
112     rfh_->AccessibilityDoDefaultAction(id);
113   }
114
115   virtual void Focus(int32 id) OVERRIDE {
116     rfh_->AccessibilitySetFocus(id);
117   }
118
119   virtual void MakeVisible(int32 id) OVERRIDE {
120     rfh_->AccessibilityScrollToMakeVisible(id, gfx::Rect());
121   }
122
123   virtual void SetSelection(int32 id, int32 start, int32 end) OVERRIDE {
124     rfh_->AccessibilitySetTextSelection(id, start, end);
125   }
126
127  private:
128   content::RenderFrameHost* rfh_;
129
130   DISALLOW_COPY_AND_ASSIGN(RenderFrameHostActionAdapter);
131 };
132
133 ExtensionFunction::ResponseAction
134 AutomationInternalEnableTabFunction::Run() {
135   const AutomationInfo* automation_info = AutomationInfo::Get(extension());
136   EXTENSION_FUNCTION_VALIDATE(automation_info);
137
138   using api::automation_internal::EnableTab::Params;
139   scoped_ptr<Params> params(Params::Create(*args_));
140   EXTENSION_FUNCTION_VALIDATE(params.get());
141   content::WebContents* contents = NULL;
142   if (params->tab_id.get()) {
143     int tab_id = *params->tab_id;
144     if (!ExtensionTabUtil::GetTabById(tab_id,
145                                       GetProfile(),
146                                       include_incognito(),
147                                       NULL, /* browser out param*/
148                                       NULL, /* tab_strip out param */
149                                       &contents,
150                                       NULL /* tab_index out param */)) {
151       return RespondNow(
152           Error(tabs_constants::kTabNotFoundError, base::IntToString(tab_id)));
153     }
154   } else {
155     contents = GetCurrentBrowser()->tab_strip_model()->GetActiveWebContents();
156     if (!contents)
157       return RespondNow(Error("No active tab"));
158   }
159   content::RenderFrameHost* rfh = contents->GetMainFrame();
160   if (!rfh)
161     return RespondNow(Error("Could not enable accessibility for active tab"));
162
163   if (!CanRequestAutomation(extension(), automation_info, contents)) {
164     return RespondNow(
165         Error(kCannotRequestAutomationOnPage, contents->GetURL().spec()));
166   }
167   AutomationWebContentsObserver::CreateForWebContents(contents);
168   contents->EnableTreeOnlyAccessibilityMode();
169   return RespondNow(
170       ArgumentList(api::automation_internal::EnableTab::Results::Create(
171           rfh->GetProcess()->GetID(), rfh->GetRoutingID())));
172   }
173
174 ExtensionFunction::ResponseAction
175 AutomationInternalPerformActionFunction::Run() {
176   const AutomationInfo* automation_info = AutomationInfo::Get(extension());
177   EXTENSION_FUNCTION_VALIDATE(automation_info && automation_info->interact);
178
179   using api::automation_internal::PerformAction::Params;
180   scoped_ptr<Params> params(Params::Create(*args_));
181   EXTENSION_FUNCTION_VALIDATE(params.get());
182
183   if (params->args.process_id == kDesktopProcessID &&
184       params->args.routing_id == kDesktopRoutingID) {
185 #if defined(OS_CHROMEOS)
186     return RouteActionToAdapter(
187         params.get(), AutomationManagerAsh::GetInstance());
188 #else
189     NOTREACHED();
190     return RespondNow(Error("Unexpected action on desktop automation tree;"
191                             " platform does not support desktop automation"));
192 #endif  // defined(OS_CHROMEOS)
193   }
194   content::RenderFrameHost* rfh =
195       content::RenderFrameHost::FromID(params->args.process_id,
196                                        params->args.routing_id);
197   if (!rfh)
198     return RespondNow(Error("Ignoring action on destroyed node"));
199
200   const content::WebContents* contents =
201       content::WebContents::FromRenderFrameHost(rfh);
202   if (!CanRequestAutomation(extension(), automation_info, contents)) {
203     return RespondNow(
204         Error(kCannotRequestAutomationOnPage, contents->GetURL().spec()));
205   }
206
207   RenderFrameHostActionAdapter adapter(rfh);
208   return RouteActionToAdapter(params.get(), &adapter);
209 }
210
211 ExtensionFunction::ResponseAction
212 AutomationInternalPerformActionFunction::RouteActionToAdapter(
213     api::automation_internal::PerformAction::Params* params,
214     AutomationActionAdapter* adapter) {
215   int32 automation_id = params->args.automation_node_id;
216   switch (params->args.action_type) {
217     case api::automation_internal::ACTION_TYPE_DODEFAULT:
218       adapter->DoDefault(automation_id);
219       break;
220     case api::automation_internal::ACTION_TYPE_FOCUS:
221       adapter->Focus(automation_id);
222       break;
223     case api::automation_internal::ACTION_TYPE_MAKEVISIBLE:
224       adapter->MakeVisible(automation_id);
225       break;
226     case api::automation_internal::ACTION_TYPE_SETSELECTION: {
227       api::automation_internal::SetSelectionParams selection_params;
228       EXTENSION_FUNCTION_VALIDATE(
229           api::automation_internal::SetSelectionParams::Populate(
230               params->opt_args.additional_properties, &selection_params));
231       adapter->SetSelection(automation_id,
232                            selection_params.start_index,
233                            selection_params.end_index);
234       break;
235     }
236     default:
237       NOTREACHED();
238   }
239   return RespondNow(NoArguments());
240 }
241
242 ExtensionFunction::ResponseAction
243 AutomationInternalEnableDesktopFunction::Run() {
244 #if defined(OS_CHROMEOS)
245   const AutomationInfo* automation_info = AutomationInfo::Get(extension());
246   if (!automation_info || !automation_info->desktop)
247     return RespondNow(Error("desktop permission must be requested"));
248
249   AutomationManagerAsh::GetInstance()->Enable(browser_context());
250   return RespondNow(NoArguments());
251 #else
252   return RespondNow(Error("getDesktop is unsupported by this platform"));
253 #endif  // defined(OS_CHROMEOS)
254 }
255
256 }  // namespace extensions