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