app_list: Re-enable people search.
[chromium-blink-merge.git] / chrome / browser / extensions / api / automation_internal / automation_internal_api.cc
blob296ce4689316fef8b12aba2a32c42ae78c3a55f1
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.
5 #include "chrome/browser/extensions/api/automation_internal/automation_internal_api.h"
7 #include <vector>
9 #include "base/strings/string16.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/accessibility/ax_tree_id_registry.h"
13 #include "chrome/browser/extensions/api/automation_internal/automation_action_adapter.h"
14 #include "chrome/browser/extensions/api/automation_internal/automation_util.h"
15 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
16 #include "chrome/browser/extensions/extension_tab_util.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
20 #include "chrome/common/extensions/api/automation_internal.h"
21 #include "chrome/common/extensions/manifest_handlers/automation.h"
22 #include "content/public/browser/ax_event_notification_details.h"
23 #include "content/public/browser/browser_accessibility_state.h"
24 #include "content/public/browser/render_frame_host.h"
25 #include "content/public/browser/render_process_host.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/browser/render_widget_host.h"
28 #include "content/public/browser/render_widget_host_view.h"
29 #include "content/public/browser/web_contents.h"
30 #include "extensions/common/extension_messages.h"
31 #include "extensions/common/permissions/permissions_data.h"
33 #if defined(OS_CHROMEOS)
34 #include "chrome/browser/ui/ash/accessibility/automation_manager_ash.h"
35 #endif
37 namespace extensions {
38 class AutomationWebContentsObserver;
39 } // namespace extensions
41 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::AutomationWebContentsObserver);
43 namespace extensions {
45 namespace {
46 const int kDesktopTreeID = 0;
47 const char kCannotRequestAutomationOnPage[] =
48 "Cannot request automation tree on url \"*\". "
49 "Extension manifest must request permission to access this host.";
50 const char kRendererDestroyed[] = "The tab was closed.";
51 const char kNoMainFrame[] = "No main frame.";
52 const char kNoDocument[] = "No document.";
53 const char kNodeDestroyed[] =
54 "domQuerySelector sent on node which is no longer in the tree.";
56 // Handles sending and receiving IPCs for a single querySelector request. On
57 // creation, sends the request IPC, and is destroyed either when the response is
58 // received or the renderer is destroyed.
59 class QuerySelectorHandler : public content::WebContentsObserver {
60 public:
61 QuerySelectorHandler(
62 content::WebContents* web_contents,
63 int request_id,
64 int acc_obj_id,
65 const base::string16& query,
66 const extensions::AutomationInternalQuerySelectorFunction::Callback&
67 callback)
68 : content::WebContentsObserver(web_contents),
69 request_id_(request_id),
70 callback_(callback) {
71 content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
73 rvh->Send(new ExtensionMsg_AutomationQuerySelector(
74 rvh->GetRoutingID(), request_id, acc_obj_id, query));
77 ~QuerySelectorHandler() override {}
79 bool OnMessageReceived(const IPC::Message& message) override {
80 if (message.type() != ExtensionHostMsg_AutomationQuerySelector_Result::ID)
81 return false;
83 // There may be several requests in flight; check this response matches.
84 int message_request_id = 0;
85 PickleIterator iter(message);
86 if (!iter.ReadInt(&message_request_id))
87 return false;
89 if (message_request_id != request_id_)
90 return false;
92 IPC_BEGIN_MESSAGE_MAP(QuerySelectorHandler, message)
93 IPC_MESSAGE_HANDLER(ExtensionHostMsg_AutomationQuerySelector_Result,
94 OnQueryResponse)
95 IPC_END_MESSAGE_MAP()
96 return true;
99 void WebContentsDestroyed() override {
100 callback_.Run(kRendererDestroyed, 0);
101 delete this;
104 private:
105 void OnQueryResponse(int request_id,
106 ExtensionHostMsg_AutomationQuerySelector_Error error,
107 int result_acc_obj_id) {
108 std::string error_string;
109 switch (error.value) {
110 case ExtensionHostMsg_AutomationQuerySelector_Error::kNone:
111 error_string = "";
112 break;
113 case ExtensionHostMsg_AutomationQuerySelector_Error::kNoMainFrame:
114 error_string = kNoMainFrame;
115 break;
116 case ExtensionHostMsg_AutomationQuerySelector_Error::kNoDocument:
117 error_string = kNoDocument;
118 break;
119 case ExtensionHostMsg_AutomationQuerySelector_Error::kNodeDestroyed:
120 error_string = kNodeDestroyed;
121 break;
123 callback_.Run(error_string, result_acc_obj_id);
124 delete this;
127 int request_id_;
128 const extensions::AutomationInternalQuerySelectorFunction::Callback callback_;
131 bool CanRequestAutomation(const Extension* extension,
132 const AutomationInfo* automation_info,
133 const content::WebContents* contents) {
134 if (automation_info->desktop)
135 return true;
137 const GURL& url = contents->GetURL();
138 // TODO(aboxhall): check for webstore URL
139 if (automation_info->matches.MatchesURL(url))
140 return true;
142 int tab_id = ExtensionTabUtil::GetTabId(contents);
143 content::RenderProcessHost* process = contents->GetRenderProcessHost();
144 int process_id = process ? process->GetID() : -1;
145 std::string unused_error;
146 return extension->permissions_data()->CanAccessPage(
147 extension, url, url, tab_id, process_id, &unused_error);
150 // Helper class that implements an action adapter for a |RenderFrameHost|.
151 class RenderFrameHostActionAdapter : public AutomationActionAdapter {
152 public:
153 explicit RenderFrameHostActionAdapter(content::RenderFrameHost* rfh)
154 : rfh_(rfh) {}
156 virtual ~RenderFrameHostActionAdapter() {}
158 // AutomationActionAdapter implementation.
159 void DoDefault(int32 id) override { rfh_->AccessibilityDoDefaultAction(id); }
161 void Focus(int32 id) override { rfh_->AccessibilitySetFocus(id); }
163 void MakeVisible(int32 id) override {
164 rfh_->AccessibilityScrollToMakeVisible(id, gfx::Rect());
167 void SetSelection(int32 id, int32 start, int32 end) override {
168 rfh_->AccessibilitySetTextSelection(id, start, end);
171 private:
172 content::RenderFrameHost* rfh_;
174 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostActionAdapter);
177 } // namespace
179 // Helper class that receives accessibility data from |WebContents|.
180 class AutomationWebContentsObserver
181 : public content::WebContentsObserver,
182 public content::WebContentsUserData<AutomationWebContentsObserver> {
183 public:
184 ~AutomationWebContentsObserver() override {}
186 // content::WebContentsObserver overrides.
187 void AccessibilityEventReceived(
188 const std::vector<content::AXEventNotificationDetails>& details)
189 override {
190 automation_util::DispatchAccessibilityEventsToAutomation(
191 details, browser_context_,
192 web_contents()->GetContainerBounds().OffsetFromOrigin());
195 void RenderFrameDeleted(
196 content::RenderFrameHost* render_frame_host) override {
197 automation_util::DispatchTreeDestroyedEventToAutomation(
198 render_frame_host->GetProcess()->GetID(),
199 render_frame_host->GetRoutingID(),
200 browser_context_);
203 private:
204 friend class content::WebContentsUserData<AutomationWebContentsObserver>;
206 AutomationWebContentsObserver(
207 content::WebContents* web_contents)
208 : content::WebContentsObserver(web_contents),
209 browser_context_(web_contents->GetBrowserContext()) {}
211 content::BrowserContext* browser_context_;
213 DISALLOW_COPY_AND_ASSIGN(AutomationWebContentsObserver);
216 ExtensionFunction::ResponseAction
217 AutomationInternalEnableTabFunction::Run() {
218 const AutomationInfo* automation_info = AutomationInfo::Get(extension());
219 EXTENSION_FUNCTION_VALIDATE(automation_info);
221 using api::automation_internal::EnableTab::Params;
222 scoped_ptr<Params> params(Params::Create(*args_));
223 EXTENSION_FUNCTION_VALIDATE(params.get());
224 content::WebContents* contents = NULL;
225 if (params->tab_id.get()) {
226 int tab_id = *params->tab_id;
227 if (!ExtensionTabUtil::GetTabById(tab_id,
228 GetProfile(),
229 include_incognito(),
230 NULL, /* browser out param*/
231 NULL, /* tab_strip out param */
232 &contents,
233 NULL /* tab_index out param */)) {
234 return RespondNow(
235 Error(tabs_constants::kTabNotFoundError, base::IntToString(tab_id)));
237 } else {
238 contents = GetCurrentBrowser()->tab_strip_model()->GetActiveWebContents();
239 if (!contents)
240 return RespondNow(Error("No active tab"));
242 content::RenderFrameHost* rfh = contents->GetMainFrame();
243 if (!rfh)
244 return RespondNow(Error("Could not enable accessibility for active tab"));
246 if (!CanRequestAutomation(extension(), automation_info, contents)) {
247 return RespondNow(
248 Error(kCannotRequestAutomationOnPage, contents->GetURL().spec()));
250 AutomationWebContentsObserver::CreateForWebContents(contents);
251 contents->EnableTreeOnlyAccessibilityMode();
252 int ax_tree_id = AXTreeIDRegistry::GetInstance()->GetOrCreateAXTreeID(
253 rfh->GetProcess()->GetID(), rfh->GetRoutingID());
254 return RespondNow(ArgumentList(
255 api::automation_internal::EnableTab::Results::Create(ax_tree_id)));
258 ExtensionFunction::ResponseAction AutomationInternalEnableFrameFunction::Run() {
259 // TODO(dtseng): Limited to desktop tree for now pending out of proc iframes.
260 #if defined(OS_CHROMEOS)
261 using api::automation_internal::EnableFrame::Params;
263 scoped_ptr<Params> params(Params::Create(*args_));
264 EXTENSION_FUNCTION_VALIDATE(params.get());
265 AXTreeIDRegistry::FrameID frame_id =
266 AXTreeIDRegistry::GetInstance()->GetFrameID(params->tree_id);
267 content::RenderFrameHost* rfh =
268 content::RenderFrameHost::FromID(frame_id.first, frame_id.second);
269 if (!rfh)
270 return RespondNow(Error("unable to load tab"));
272 content::WebContents* contents =
273 content::WebContents::FromRenderFrameHost(rfh);
274 AutomationWebContentsObserver::CreateForWebContents(contents);
275 contents->EnableTreeOnlyAccessibilityMode();
277 return RespondNow(NoArguments());
278 #endif
279 return RespondNow(Error("enableFrame is only supported on Chrome OS"));
282 ExtensionFunction::ResponseAction
283 AutomationInternalPerformActionFunction::Run() {
284 const AutomationInfo* automation_info = AutomationInfo::Get(extension());
285 EXTENSION_FUNCTION_VALIDATE(automation_info && automation_info->interact);
287 using api::automation_internal::PerformAction::Params;
288 scoped_ptr<Params> params(Params::Create(*args_));
289 EXTENSION_FUNCTION_VALIDATE(params.get());
291 if (params->args.tree_id == kDesktopTreeID) {
292 #if defined(OS_CHROMEOS)
293 return RouteActionToAdapter(
294 params.get(), AutomationManagerAsh::GetInstance());
295 #else
296 NOTREACHED();
297 return RespondNow(Error("Unexpected action on desktop automation tree;"
298 " platform does not support desktop automation"));
299 #endif // defined(OS_CHROMEOS)
301 AXTreeIDRegistry::FrameID frame_id =
302 AXTreeIDRegistry::GetInstance()->GetFrameID(params->args.tree_id);
303 content::RenderFrameHost* rfh =
304 content::RenderFrameHost::FromID(frame_id.first, frame_id.second);
305 if (!rfh)
306 return RespondNow(Error("Ignoring action on destroyed node"));
308 const content::WebContents* contents =
309 content::WebContents::FromRenderFrameHost(rfh);
310 if (!CanRequestAutomation(extension(), automation_info, contents)) {
311 return RespondNow(
312 Error(kCannotRequestAutomationOnPage, contents->GetURL().spec()));
315 RenderFrameHostActionAdapter adapter(rfh);
316 return RouteActionToAdapter(params.get(), &adapter);
319 ExtensionFunction::ResponseAction
320 AutomationInternalPerformActionFunction::RouteActionToAdapter(
321 api::automation_internal::PerformAction::Params* params,
322 AutomationActionAdapter* adapter) {
323 int32 automation_id = params->args.automation_node_id;
324 switch (params->args.action_type) {
325 case api::automation_internal::ACTION_TYPE_DODEFAULT:
326 adapter->DoDefault(automation_id);
327 break;
328 case api::automation_internal::ACTION_TYPE_FOCUS:
329 adapter->Focus(automation_id);
330 break;
331 case api::automation_internal::ACTION_TYPE_MAKEVISIBLE:
332 adapter->MakeVisible(automation_id);
333 break;
334 case api::automation_internal::ACTION_TYPE_SETSELECTION: {
335 api::automation_internal::SetSelectionParams selection_params;
336 EXTENSION_FUNCTION_VALIDATE(
337 api::automation_internal::SetSelectionParams::Populate(
338 params->opt_args.additional_properties, &selection_params));
339 adapter->SetSelection(automation_id,
340 selection_params.start_index,
341 selection_params.end_index);
342 break;
344 default:
345 NOTREACHED();
347 return RespondNow(NoArguments());
350 ExtensionFunction::ResponseAction
351 AutomationInternalEnableDesktopFunction::Run() {
352 #if defined(OS_CHROMEOS)
353 const AutomationInfo* automation_info = AutomationInfo::Get(extension());
354 if (!automation_info || !automation_info->desktop)
355 return RespondNow(Error("desktop permission must be requested"));
357 AutomationManagerAsh::GetInstance()->Enable(browser_context());
358 return RespondNow(NoArguments());
359 #else
360 return RespondNow(Error("getDesktop is unsupported by this platform"));
361 #endif // defined(OS_CHROMEOS)
364 // static
365 int AutomationInternalQuerySelectorFunction::query_request_id_counter_ = 0;
367 ExtensionFunction::ResponseAction
368 AutomationInternalQuerySelectorFunction::Run() {
369 const AutomationInfo* automation_info = AutomationInfo::Get(extension());
370 EXTENSION_FUNCTION_VALIDATE(automation_info);
372 using api::automation_internal::QuerySelector::Params;
373 scoped_ptr<Params> params(Params::Create(*args_));
374 EXTENSION_FUNCTION_VALIDATE(params.get());
376 if (params->args.tree_id == kDesktopTreeID) {
377 return RespondNow(
378 Error("domQuerySelector queries may not be used on the desktop."));
380 AXTreeIDRegistry::FrameID frame_id =
381 AXTreeIDRegistry::GetInstance()->GetFrameID(params->args.tree_id);
382 content::RenderFrameHost* rfh =
383 content::RenderFrameHost::FromID(frame_id.first, frame_id.second);
384 if (!rfh)
385 return RespondNow(Error("domQuerySelector query sent on destroyed tree."));
387 content::WebContents* contents =
388 content::WebContents::FromRenderFrameHost(rfh);
390 int request_id = query_request_id_counter_++;
391 base::string16 selector = base::UTF8ToUTF16(params->args.selector);
393 // QuerySelectorHandler handles IPCs and deletes itself on completion.
394 new QuerySelectorHandler(
395 contents, request_id, params->args.automation_node_id, selector,
396 base::Bind(&AutomationInternalQuerySelectorFunction::OnResponse, this));
398 return RespondLater();
401 void AutomationInternalQuerySelectorFunction::OnResponse(
402 const std::string& error,
403 int result_acc_obj_id) {
404 if (!error.empty()) {
405 Respond(Error(error));
406 return;
409 Respond(OneArgument(new base::FundamentalValue(result_acc_obj_id)));
412 } // namespace extensions