Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / extensions / api / automation_internal / automation_internal_api.cc
blobdc10f2cc81904e680512d3781fd30cf64b565361
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/extensions/api/automation_internal/automation_action_adapter.h"
13 #include "chrome/browser/extensions/api/automation_internal/automation_event_router.h"
14 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
15 #include "chrome/browser/extensions/extension_tab_util.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
19 #include "chrome/common/extensions/api/automation_internal.h"
20 #include "chrome/common/extensions/chrome_extension_messages.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/browser_context.h"
25 #include "content/public/browser/browser_plugin_guest_manager.h"
26 #include "content/public/browser/render_frame_host.h"
27 #include "content/public/browser/render_process_host.h"
28 #include "content/public/browser/render_view_host.h"
29 #include "content/public/browser/render_widget_host.h"
30 #include "content/public/browser/render_widget_host_view.h"
31 #include "content/public/browser/web_contents.h"
32 #include "content/public/browser/web_contents_observer.h"
33 #include "content/public/browser/web_contents_user_data.h"
34 #include "extensions/common/extension_messages.h"
35 #include "extensions/common/permissions/permissions_data.h"
37 #if defined(USE_AURA)
38 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
39 #endif
41 namespace extensions {
42 class AutomationWebContentsObserver;
43 } // namespace extensions
45 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::AutomationWebContentsObserver);
47 namespace extensions {
49 namespace {
51 const int kDesktopTreeID = 0;
52 const char kCannotRequestAutomationOnPage[] =
53 "Cannot request automation tree on url \"*\". "
54 "Extension manifest must request permission to access this host.";
55 const char kRendererDestroyed[] = "The tab was closed.";
56 const char kNoMainFrame[] = "No main frame.";
57 const char kNoDocument[] = "No document.";
58 const char kNodeDestroyed[] =
59 "domQuerySelector sent on node which is no longer in the tree.";
61 // Handles sending and receiving IPCs for a single querySelector request. On
62 // creation, sends the request IPC, and is destroyed either when the response is
63 // received or the renderer is destroyed.
64 class QuerySelectorHandler : public content::WebContentsObserver {
65 public:
66 QuerySelectorHandler(
67 content::WebContents* web_contents,
68 int request_id,
69 int acc_obj_id,
70 const base::string16& query,
71 const extensions::AutomationInternalQuerySelectorFunction::Callback&
72 callback)
73 : content::WebContentsObserver(web_contents),
74 request_id_(request_id),
75 callback_(callback) {
76 content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
78 rvh->Send(new ExtensionMsg_AutomationQuerySelector(
79 rvh->GetRoutingID(), request_id, acc_obj_id, query));
82 ~QuerySelectorHandler() override {}
84 bool OnMessageReceived(const IPC::Message& message) override {
85 if (message.type() != ExtensionHostMsg_AutomationQuerySelector_Result::ID)
86 return false;
88 // There may be several requests in flight; check this response matches.
89 int message_request_id = 0;
90 base::PickleIterator iter(message);
91 if (!iter.ReadInt(&message_request_id))
92 return false;
94 if (message_request_id != request_id_)
95 return false;
97 IPC_BEGIN_MESSAGE_MAP(QuerySelectorHandler, message)
98 IPC_MESSAGE_HANDLER(ExtensionHostMsg_AutomationQuerySelector_Result,
99 OnQueryResponse)
100 IPC_END_MESSAGE_MAP()
101 return true;
104 void WebContentsDestroyed() override {
105 callback_.Run(kRendererDestroyed, 0);
106 delete this;
109 private:
110 void OnQueryResponse(int request_id,
111 ExtensionHostMsg_AutomationQuerySelector_Error error,
112 int result_acc_obj_id) {
113 std::string error_string;
114 switch (error.value) {
115 case ExtensionHostMsg_AutomationQuerySelector_Error::kNone:
116 error_string = "";
117 break;
118 case ExtensionHostMsg_AutomationQuerySelector_Error::kNoMainFrame:
119 error_string = kNoMainFrame;
120 break;
121 case ExtensionHostMsg_AutomationQuerySelector_Error::kNoDocument:
122 error_string = kNoDocument;
123 break;
124 case ExtensionHostMsg_AutomationQuerySelector_Error::kNodeDestroyed:
125 error_string = kNodeDestroyed;
126 break;
128 callback_.Run(error_string, result_acc_obj_id);
129 delete this;
132 int request_id_;
133 const extensions::AutomationInternalQuerySelectorFunction::Callback callback_;
136 bool CanRequestAutomation(const Extension* extension,
137 const AutomationInfo* automation_info,
138 const content::WebContents* contents) {
139 if (automation_info->desktop)
140 return true;
142 const GURL& url = contents->GetURL();
143 // TODO(aboxhall): check for webstore URL
144 if (automation_info->matches.MatchesURL(url))
145 return true;
147 int tab_id = ExtensionTabUtil::GetTabId(contents);
148 content::RenderProcessHost* process = contents->GetRenderProcessHost();
149 int process_id = process ? process->GetID() : -1;
150 std::string unused_error;
151 return extension->permissions_data()->CanAccessPage(
152 extension, url, tab_id, process_id, &unused_error);
155 // Helper class that implements an action adapter for a |RenderFrameHost|.
156 class RenderFrameHostActionAdapter : public AutomationActionAdapter {
157 public:
158 explicit RenderFrameHostActionAdapter(content::RenderFrameHost* rfh)
159 : rfh_(rfh) {}
161 virtual ~RenderFrameHostActionAdapter() {}
163 // AutomationActionAdapter implementation.
164 void DoDefault(int32 id) override { rfh_->AccessibilityDoDefaultAction(id); }
166 void Focus(int32 id) override { rfh_->AccessibilitySetFocus(id); }
168 void MakeVisible(int32 id) override {
169 rfh_->AccessibilityScrollToMakeVisible(id, gfx::Rect());
172 void SetSelection(int32 id, int32 start, int32 end) override {
173 rfh_->AccessibilitySetTextSelection(id, start, end);
176 void ShowContextMenu(int32 id) override {
177 rfh_->AccessibilityShowContextMenu(id);
180 private:
181 content::RenderFrameHost* rfh_;
183 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostActionAdapter);
186 } // namespace
188 // Helper class that receives accessibility data from |WebContents|.
189 class AutomationWebContentsObserver
190 : public content::WebContentsObserver,
191 public content::WebContentsUserData<AutomationWebContentsObserver> {
192 public:
193 ~AutomationWebContentsObserver() override {}
195 // content::WebContentsObserver overrides.
196 void AccessibilityEventReceived(
197 const std::vector<content::AXEventNotificationDetails>& details)
198 override {
199 std::vector<content::AXEventNotificationDetails>::const_iterator iter =
200 details.begin();
201 for (; iter != details.end(); ++iter) {
202 const content::AXEventNotificationDetails& event = *iter;
203 ExtensionMsg_AccessibilityEventParams params;
204 params.tree_id = event.ax_tree_id;
205 params.id = event.id;
206 params.event_type = event.event_type;
207 params.update = event.update;
208 params.location_offset =
209 web_contents()->GetContainerBounds().OffsetFromOrigin();
211 AutomationEventRouter* router = AutomationEventRouter::GetInstance();
212 router->DispatchAccessibilityEvent(params);
216 void RenderFrameDeleted(
217 content::RenderFrameHost* render_frame_host) override {
218 int tree_id = render_frame_host->GetAXTreeID();
219 AutomationEventRouter::GetInstance()->DispatchTreeDestroyedEvent(
220 tree_id,
221 browser_context_);
224 private:
225 friend class content::WebContentsUserData<AutomationWebContentsObserver>;
227 explicit AutomationWebContentsObserver(content::WebContents* web_contents)
228 : content::WebContentsObserver(web_contents),
229 browser_context_(web_contents->GetBrowserContext()) {}
231 content::BrowserContext* browser_context_;
233 DISALLOW_COPY_AND_ASSIGN(AutomationWebContentsObserver);
236 ExtensionFunction::ResponseAction
237 AutomationInternalEnableTabFunction::Run() {
238 const AutomationInfo* automation_info = AutomationInfo::Get(extension());
239 EXTENSION_FUNCTION_VALIDATE(automation_info);
241 using api::automation_internal::EnableTab::Params;
242 scoped_ptr<Params> params(Params::Create(*args_));
243 EXTENSION_FUNCTION_VALIDATE(params.get());
244 content::WebContents* contents = NULL;
245 if (params->args.tab_id.get()) {
246 int tab_id = *params->args.tab_id;
247 if (!ExtensionTabUtil::GetTabById(tab_id,
248 GetProfile(),
249 include_incognito(),
250 NULL, /* browser out param*/
251 NULL, /* tab_strip out param */
252 &contents,
253 NULL /* tab_index out param */)) {
254 return RespondNow(
255 Error(tabs_constants::kTabNotFoundError, base::IntToString(tab_id)));
257 } else {
258 contents = GetCurrentBrowser()->tab_strip_model()->GetActiveWebContents();
259 if (!contents)
260 return RespondNow(Error("No active tab"));
263 content::RenderFrameHost* rfh = contents->GetMainFrame();
264 if (!rfh)
265 return RespondNow(Error("Could not enable accessibility for active tab"));
267 if (!CanRequestAutomation(extension(), automation_info, contents)) {
268 return RespondNow(
269 Error(kCannotRequestAutomationOnPage, contents->GetURL().spec()));
272 AutomationWebContentsObserver::CreateForWebContents(contents);
273 contents->EnableTreeOnlyAccessibilityMode();
275 int ax_tree_id = rfh->GetAXTreeID();
277 // This gets removed when the extension process dies.
278 AutomationEventRouter::GetInstance()->RegisterListenerForOneTree(
279 source_process_id(),
280 params->args.routing_id,
281 ax_tree_id);
283 return RespondNow(ArgumentList(
284 api::automation_internal::EnableTab::Results::Create(ax_tree_id)));
287 ExtensionFunction::ResponseAction AutomationInternalEnableFrameFunction::Run() {
288 // TODO(dtseng): Limited to desktop tree for now pending out of proc iframes.
289 using api::automation_internal::EnableFrame::Params;
291 scoped_ptr<Params> params(Params::Create(*args_));
292 EXTENSION_FUNCTION_VALIDATE(params.get());
294 content::RenderFrameHost* rfh =
295 content::RenderFrameHost::FromAXTreeID(params->tree_id);
296 if (!rfh)
297 return RespondNow(Error("unable to load tab"));
299 content::WebContents* contents =
300 content::WebContents::FromRenderFrameHost(rfh);
301 AutomationWebContentsObserver::CreateForWebContents(contents);
302 contents->EnableTreeOnlyAccessibilityMode();
304 return RespondNow(NoArguments());
307 ExtensionFunction::ResponseAction
308 AutomationInternalPerformActionFunction::Run() {
309 const AutomationInfo* automation_info = AutomationInfo::Get(extension());
310 EXTENSION_FUNCTION_VALIDATE(automation_info && automation_info->interact);
312 using api::automation_internal::PerformAction::Params;
313 scoped_ptr<Params> params(Params::Create(*args_));
314 EXTENSION_FUNCTION_VALIDATE(params.get());
316 if (params->args.tree_id == kDesktopTreeID) {
317 #if defined(USE_AURA)
318 return RouteActionToAdapter(params.get(),
319 AutomationManagerAura::GetInstance());
320 #else
321 NOTREACHED();
322 return RespondNow(Error("Unexpected action on desktop automation tree;"
323 " platform does not support desktop automation"));
324 #endif // defined(USE_AURA)
326 content::RenderFrameHost* rfh =
327 content::RenderFrameHost::FromAXTreeID(params->args.tree_id);
328 if (!rfh)
329 return RespondNow(Error("Ignoring action on destroyed node"));
331 const content::WebContents* contents =
332 content::WebContents::FromRenderFrameHost(rfh);
333 if (!CanRequestAutomation(extension(), automation_info, contents)) {
334 return RespondNow(
335 Error(kCannotRequestAutomationOnPage, contents->GetURL().spec()));
338 RenderFrameHostActionAdapter adapter(rfh);
339 return RouteActionToAdapter(params.get(), &adapter);
342 ExtensionFunction::ResponseAction
343 AutomationInternalPerformActionFunction::RouteActionToAdapter(
344 api::automation_internal::PerformAction::Params* params,
345 AutomationActionAdapter* adapter) {
346 int32 automation_id = params->args.automation_node_id;
347 switch (params->args.action_type) {
348 case api::automation_internal::ACTION_TYPE_DODEFAULT:
349 adapter->DoDefault(automation_id);
350 break;
351 case api::automation_internal::ACTION_TYPE_FOCUS:
352 adapter->Focus(automation_id);
353 break;
354 case api::automation_internal::ACTION_TYPE_MAKEVISIBLE:
355 adapter->MakeVisible(automation_id);
356 break;
357 case api::automation_internal::ACTION_TYPE_SETSELECTION: {
358 api::automation_internal::SetSelectionParams selection_params;
359 EXTENSION_FUNCTION_VALIDATE(
360 api::automation_internal::SetSelectionParams::Populate(
361 params->opt_args.additional_properties, &selection_params));
362 adapter->SetSelection(automation_id,
363 selection_params.start_index,
364 selection_params.end_index);
365 break;
367 case api::automation_internal::ACTION_TYPE_SHOWCONTEXTMENU: {
368 adapter->ShowContextMenu(automation_id);
369 break;
371 default:
372 NOTREACHED();
374 return RespondNow(NoArguments());
377 ExtensionFunction::ResponseAction
378 AutomationInternalEnableDesktopFunction::Run() {
379 #if defined(USE_AURA)
380 const AutomationInfo* automation_info = AutomationInfo::Get(extension());
381 if (!automation_info || !automation_info->desktop)
382 return RespondNow(Error("desktop permission must be requested"));
384 using api::automation_internal::EnableDesktop::Params;
385 scoped_ptr<Params> params(Params::Create(*args_));
386 EXTENSION_FUNCTION_VALIDATE(params.get());
388 // This gets removed when the extension process dies.
389 AutomationEventRouter::GetInstance()->RegisterListenerWithDesktopPermission(
390 source_process_id(),
391 params->routing_id);
393 AutomationManagerAura::GetInstance()->Enable(browser_context());
394 return RespondNow(NoArguments());
395 #else
396 return RespondNow(Error("getDesktop is unsupported by this platform"));
397 #endif // defined(USE_AURA)
400 // static
401 int AutomationInternalQuerySelectorFunction::query_request_id_counter_ = 0;
403 ExtensionFunction::ResponseAction
404 AutomationInternalQuerySelectorFunction::Run() {
405 const AutomationInfo* automation_info = AutomationInfo::Get(extension());
406 EXTENSION_FUNCTION_VALIDATE(automation_info);
408 using api::automation_internal::QuerySelector::Params;
409 scoped_ptr<Params> params(Params::Create(*args_));
410 EXTENSION_FUNCTION_VALIDATE(params.get());
412 if (params->args.tree_id == kDesktopTreeID) {
413 return RespondNow(
414 Error("domQuerySelector queries may not be used on the desktop."));
416 content::RenderFrameHost* rfh =
417 content::RenderFrameHost::FromAXTreeID(params->args.tree_id);
418 if (!rfh)
419 return RespondNow(Error("domQuerySelector query sent on destroyed tree."));
421 content::WebContents* contents =
422 content::WebContents::FromRenderFrameHost(rfh);
424 int request_id = query_request_id_counter_++;
425 base::string16 selector = base::UTF8ToUTF16(params->args.selector);
427 // QuerySelectorHandler handles IPCs and deletes itself on completion.
428 new QuerySelectorHandler(
429 contents, request_id, params->args.automation_node_id, selector,
430 base::Bind(&AutomationInternalQuerySelectorFunction::OnResponse, this));
432 return RespondLater();
435 void AutomationInternalQuerySelectorFunction::OnResponse(
436 const std::string& error,
437 int result_acc_obj_id) {
438 if (!error.empty()) {
439 Respond(Error(error));
440 return;
443 Respond(OneArgument(new base::FundamentalValue(result_acc_obj_id)));
446 } // namespace extensions