2ad67fe7b689bbaed560c46861af8e2dd7b98aa5
[platform/framework/web/crosswalk.git] / src / chrome / browser / automation / testing_automation_provider.cc
1 // Copyright 2013 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/automation/testing_automation_provider.h"
6
7 #include <map>
8 #include <set>
9 #include <string>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/command_line.h"
15 #include "base/files/file_path.h"
16 #include "base/json/json_reader.h"
17 #include "base/json/json_writer.h"
18 #include "base/json/string_escape.h"
19 #include "base/path_service.h"
20 #include "base/prefs/pref_service.h"
21 #include "base/process/process.h"
22 #include "base/process/process_iterator.h"
23 #include "base/sequenced_task_runner.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/threading/thread_restrictions.h"
27 #include "base/time/time.h"
28 #include "chrome/app/chrome_command_ids.h"
29 #include "chrome/browser/autocomplete/autocomplete_controller.h"
30 #include "chrome/browser/autocomplete/autocomplete_match.h"
31 #include "chrome/browser/autocomplete/autocomplete_result.h"
32 #include "chrome/browser/automation/automation_browser_tracker.h"
33 #include "chrome/browser/automation/automation_provider_json.h"
34 #include "chrome/browser/automation/automation_provider_list.h"
35 #include "chrome/browser/automation/automation_provider_observers.h"
36 #include "chrome/browser/automation/automation_tab_tracker.h"
37 #include "chrome/browser/automation/automation_util.h"
38 #include "chrome/browser/automation/automation_window_tracker.h"
39 #include "chrome/browser/bookmarks/bookmark_model.h"
40 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
41 #include "chrome/browser/bookmarks/bookmark_storage.h"
42 #include "chrome/browser/browser_process.h"
43 #include "chrome/browser/browser_shutdown.h"
44 #include "chrome/browser/chrome_notification_types.h"
45 #include "chrome/browser/content_settings/host_content_settings_map.h"
46 #include "chrome/browser/devtools/devtools_window.h"
47 #include "chrome/browser/download/download_prefs.h"
48 #include "chrome/browser/download/download_service.h"
49 #include "chrome/browser/download/download_service_factory.h"
50 #include "chrome/browser/download/download_shelf.h"
51 #include "chrome/browser/download/save_package_file_picker.h"
52 #include "chrome/browser/extensions/browser_action_test_util.h"
53 #include "chrome/browser/extensions/crx_installer.h"
54 #include "chrome/browser/extensions/extension_action.h"
55 #include "chrome/browser/extensions/extension_action_manager.h"
56 #include "chrome/browser/extensions/extension_host.h"
57 #include "chrome/browser/extensions/extension_service.h"
58 #include "chrome/browser/extensions/extension_system.h"
59 #include "chrome/browser/extensions/extension_tab_util.h"
60 #include "chrome/browser/extensions/extension_util.h"
61 #include "chrome/browser/extensions/launch_util.h"
62 #include "chrome/browser/extensions/unpacked_installer.h"
63 #include "chrome/browser/extensions/updater/extension_updater.h"
64 #include "chrome/browser/history/history_service_factory.h"
65 #include "chrome/browser/history/top_sites.h"
66 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
67 #include "chrome/browser/infobars/infobar.h"
68 #include "chrome/browser/infobars/infobar_service.h"
69 #include "chrome/browser/lifetime/application_lifetime.h"
70 #include "chrome/browser/notifications/balloon.h"
71 #include "chrome/browser/notifications/balloon_collection.h"
72 #include "chrome/browser/notifications/notification.h"
73 #include "chrome/browser/password_manager/password_store.h"
74 #include "chrome/browser/password_manager/password_store_change.h"
75 #include "chrome/browser/password_manager/password_store_factory.h"
76 #include "chrome/browser/platform_util.h"
77 #include "chrome/browser/plugins/plugin_prefs.h"
78 #include "chrome/browser/profiles/profile.h"
79 #include "chrome/browser/profiles/profile_info_cache.h"
80 #include "chrome/browser/profiles/profile_manager.h"
81 #include "chrome/browser/profiles/profile_window.h"
82 #include "chrome/browser/profiles/profiles_state.h"
83 #include "chrome/browser/search_engines/template_url.h"
84 #include "chrome/browser/search_engines/template_url_service.h"
85 #include "chrome/browser/search_engines/template_url_service_factory.h"
86 #include "chrome/browser/sessions/session_service_factory.h"
87 #include "chrome/browser/sessions/session_tab_helper.h"
88 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h"
89 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
90 #include "chrome/browser/ui/app_modal_dialogs/javascript_app_modal_dialog.h"
91 #include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h"
92 #include "chrome/browser/ui/bookmarks/bookmark_bar.h"
93 #include "chrome/browser/ui/browser_commands.h"
94 #include "chrome/browser/ui/browser_finder.h"
95 #include "chrome/browser/ui/browser_iterator.h"
96 #include "chrome/browser/ui/browser_list.h"
97 #include "chrome/browser/ui/browser_tabstrip.h"
98 #include "chrome/browser/ui/browser_window.h"
99 #include "chrome/browser/ui/extensions/application_launch.h"
100 #include "chrome/browser/ui/find_bar/find_bar.h"
101 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
102 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
103 #include "chrome/browser/ui/fullscreen/fullscreen_exit_bubble_type.h"
104 #include "chrome/browser/ui/host_desktop.h"
105 #include "chrome/browser/ui/login/login_prompt.h"
106 #include "chrome/browser/ui/omnibox/location_bar.h"
107 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
108 #include "chrome/browser/ui/omnibox/omnibox_view.h"
109 #include "chrome/browser/ui/search_engines/keyword_editor_controller.h"
110 #include "chrome/browser/ui/startup/startup_types.h"
111 #include "chrome/common/automation_constants.h"
112 #include "chrome/common/automation_messages.h"
113 #include "chrome/common/chrome_constants.h"
114 #include "chrome/common/chrome_paths.h"
115 #include "chrome/common/chrome_switches.h"
116 #include "chrome/common/extensions/extension_constants.h"
117 #include "chrome/common/extensions/manifest_url_handler.h"
118 #include "chrome/common/pref_names.h"
119 #include "chrome/common/render_messages.h"
120 #include "content/public/browser/browser_child_process_host_iterator.h"
121 #include "content/public/browser/child_process_data.h"
122 #include "content/public/browser/favicon_status.h"
123 #include "content/public/browser/geolocation_provider.h"
124 #include "content/public/browser/interstitial_page.h"
125 #include "content/public/browser/interstitial_page_delegate.h"
126 #include "content/public/browser/navigation_entry.h"
127 #include "content/public/browser/notification_service.h"
128 #include "content/public/browser/plugin_service.h"
129 #include "content/public/browser/render_process_host.h"
130 #include "content/public/browser/render_view_host.h"
131 #include "content/public/browser/render_widget_host_view.h"
132 #include "content/public/browser/web_contents.h"
133 #include "content/public/common/child_process_host.h"
134 #include "content/public/common/common_param_traits.h"
135 #include "content/public/common/drop_data.h"
136 #include "content/public/common/geoposition.h"
137 #include "content/public/common/ssl_status.h"
138 #include "content/public/common/webplugininfo.h"
139 #include "extensions/browser/process_manager.h"
140 #include "extensions/browser/view_type_utils.h"
141 #include "extensions/common/extension.h"
142 #include "extensions/common/extension_set.h"
143 #include "extensions/common/manifest_handlers/background_info.h"
144 #include "extensions/common/permissions/permission_set.h"
145 #include "extensions/common/permissions/permissions_data.h"
146 #include "extensions/common/url_pattern.h"
147 #include "extensions/common/url_pattern_set.h"
148 #include "net/cookies/cookie_store.h"
149 #include "third_party/WebKit/public/web/WebInputEvent.h"
150 #include "ui/base/ui_base_types.h"
151 #include "ui/events/event_constants.h"
152 #include "ui/events/keycodes/keyboard_codes.h"
153
154 #if defined(ENABLE_CONFIGURATION_POLICY)
155 #include "components/policy/core/common/policy_service.h"
156 #endif
157
158 #if defined(OS_CHROMEOS)
159 #include "chromeos/dbus/dbus_thread_manager.h"
160 #else
161 #include "chrome/browser/notifications/balloon_notification_ui_manager.h"
162 #endif
163
164 #if defined(OS_MACOSX)
165 #include <mach/mach.h>
166 #include <mach/mach_vm.h>
167 #endif
168
169 using automation_util::SendErrorIfModalDialogActive;
170 using content::BrowserChildProcessHostIterator;
171 using content::BrowserContext;
172 using content::BrowserThread;
173 using content::ChildProcessHost;
174 using content::DownloadItem;
175 using content::DownloadManager;
176 using content::InterstitialPage;
177 using content::NativeWebKeyboardEvent;
178 using content::NavigationController;
179 using content::NavigationEntry;
180 using content::OpenURLParams;
181 using content::PluginService;
182 using content::Referrer;
183 using content::RenderViewHost;
184 using content::SSLStatus;
185 using content::WebContents;
186 using extensions::Extension;
187 using extensions::ExtensionActionManager;
188 using extensions::ExtensionList;
189 using extensions::Manifest;
190
191 namespace {
192
193 // Helper to reply asynchronously if |automation| is still valid.
194 void SendSuccessReply(base::WeakPtr<AutomationProvider> automation,
195                       IPC::Message* reply_message) {
196   if (automation.get())
197     AutomationJSONReply(automation.get(), reply_message).SendSuccess(NULL);
198 }
199
200 // Helper to process the result of CanEnablePlugin.
201 void DidEnablePlugin(base::WeakPtr<AutomationProvider> automation,
202                      IPC::Message* reply_message,
203                      const base::FilePath::StringType& path,
204                      const std::string& error_msg,
205                      bool did_enable) {
206   if (did_enable) {
207     SendSuccessReply(automation, reply_message);
208   } else {
209     if (automation.get()) {
210       AutomationJSONReply(automation.get(), reply_message)
211           .SendError(base::StringPrintf(error_msg.c_str(), path.c_str()));
212     }
213   }
214 }
215
216 // Helper to resolve the overloading of PostTask.
217 void PostTask(BrowserThread::ID id, const base::Closure& callback) {
218   BrowserThread::PostTask(id, FROM_HERE, callback);
219 }
220
221 class AutomationInterstitialPage : public content::InterstitialPageDelegate {
222  public:
223   AutomationInterstitialPage(WebContents* tab,
224                              const GURL& url,
225                              const std::string& contents)
226       : contents_(contents) {
227     interstitial_page_ = InterstitialPage::Create(tab, true, url, this);
228     interstitial_page_->Show();
229   }
230
231   virtual std::string GetHTMLContents() OVERRIDE { return contents_; }
232
233  private:
234   const std::string contents_;
235   InterstitialPage* interstitial_page_;  // Owns us.
236
237   DISALLOW_COPY_AND_ASSIGN(AutomationInterstitialPage);
238 };
239
240 }  // namespace
241
242 const int TestingAutomationProvider::kSynchronousCommands[] = {
243   IDC_HOME,
244   IDC_SELECT_NEXT_TAB,
245   IDC_SELECT_PREVIOUS_TAB,
246   IDC_SHOW_BOOKMARK_MANAGER,
247 };
248
249 TestingAutomationProvider::TestingAutomationProvider(Profile* profile)
250     : AutomationProvider(profile) {
251   BrowserList::AddObserver(this);
252   registrar_.Add(this, chrome::NOTIFICATION_SESSION_END,
253                  content::NotificationService::AllSources());
254 #if defined(OS_CHROMEOS)
255   AddChromeosObservers();
256 #endif
257 }
258
259 TestingAutomationProvider::~TestingAutomationProvider() {
260 #if defined(OS_CHROMEOS)
261   RemoveChromeosObservers();
262 #endif
263   BrowserList::RemoveObserver(this);
264 }
265
266 IPC::Channel::Mode TestingAutomationProvider::GetChannelMode(
267     bool use_named_interface) {
268   if (use_named_interface)
269 #if defined(OS_POSIX)
270     return IPC::Channel::MODE_OPEN_NAMED_SERVER;
271 #else
272     return IPC::Channel::MODE_NAMED_SERVER;
273 #endif
274   else
275     return IPC::Channel::MODE_CLIENT;
276 }
277
278 void TestingAutomationProvider::OnBrowserAdded(Browser* browser) {
279 }
280
281 void TestingAutomationProvider::OnBrowserRemoved(Browser* browser) {
282 #if !defined(OS_CHROMEOS) && !defined(OS_MACOSX)
283   // For backwards compatibility with the testing automation interface, we
284   // want the automation provider (and hence the process) to go away when the
285   // last browser goes away.
286   // The automation layer doesn't support non-native desktops.
287   if (BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE)->empty() &&
288       !CommandLine::ForCurrentProcess()->HasSwitch(
289           switches::kKeepAliveForTest)) {
290     // If you change this, update Observer for chrome::SESSION_END
291     // below.
292     base::MessageLoop::current()->PostTask(
293         FROM_HERE,
294         base::Bind(&TestingAutomationProvider::OnRemoveProvider, this));
295   }
296 #endif  // !defined(OS_CHROMEOS) && !defined(OS_MACOSX)
297 }
298
299 void TestingAutomationProvider::Observe(
300     int type,
301     const content::NotificationSource& source,
302     const content::NotificationDetails& details) {
303   DCHECK(type == chrome::NOTIFICATION_SESSION_END);
304   // OnBrowserRemoved does a ReleaseLater. When session end is received we exit
305   // before the task runs resulting in this object not being deleted. This
306   // Release balance out the Release scheduled by OnBrowserRemoved.
307   Release();
308 }
309
310 bool TestingAutomationProvider::OnMessageReceived(
311     const IPC::Message& message) {
312   base::ThreadRestrictions::ScopedAllowWait allow_wait;
313   bool handled = true;
314   bool deserialize_success = true;
315   IPC_BEGIN_MESSAGE_MAP_EX(TestingAutomationProvider,
316                            message,
317                            deserialize_success)
318     IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_CloseBrowser, CloseBrowser)
319     IPC_MESSAGE_HANDLER(AutomationMsg_ActivateTab, ActivateTab)
320     IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_AppendTab, AppendTab)
321     IPC_MESSAGE_HANDLER(AutomationMsg_GetMachPortCount, GetMachPortCount)
322     IPC_MESSAGE_HANDLER(AutomationMsg_ActiveTabIndex, GetActiveTabIndex)
323     IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_CloseTab, CloseTab)
324     IPC_MESSAGE_HANDLER(AutomationMsg_GetCookies, GetCookies)
325     IPC_MESSAGE_HANDLER_DELAY_REPLY(
326         AutomationMsg_NavigateToURLBlockUntilNavigationsComplete,
327         NavigateToURLBlockUntilNavigationsComplete)
328     IPC_MESSAGE_HANDLER(AutomationMsg_NavigationAsync, NavigationAsync)
329     IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_Reload, Reload)
330     IPC_MESSAGE_HANDLER(AutomationMsg_BrowserWindowCount, GetBrowserWindowCount)
331     IPC_MESSAGE_HANDLER(AutomationMsg_NormalBrowserWindowCount,
332                         GetNormalBrowserWindowCount)
333     IPC_MESSAGE_HANDLER(AutomationMsg_BrowserWindow, GetBrowserWindow)
334     IPC_MESSAGE_HANDLER(AutomationMsg_WindowExecuteCommandAsync,
335                         ExecuteBrowserCommandAsync)
336     IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WindowExecuteCommand,
337                         ExecuteBrowserCommand)
338     IPC_MESSAGE_HANDLER(AutomationMsg_TerminateSession, TerminateSession)
339     IPC_MESSAGE_HANDLER(AutomationMsg_WindowViewBounds, WindowGetViewBounds)
340     IPC_MESSAGE_HANDLER(AutomationMsg_SetWindowBounds, SetWindowBounds)
341     IPC_MESSAGE_HANDLER(AutomationMsg_TabCount, GetTabCount)
342     IPC_MESSAGE_HANDLER(AutomationMsg_Type, GetType)
343     IPC_MESSAGE_HANDLER(AutomationMsg_Tab, GetTab)
344     IPC_MESSAGE_HANDLER(AutomationMsg_TabTitle, GetTabTitle)
345     IPC_MESSAGE_HANDLER(AutomationMsg_TabIndex, GetTabIndex)
346     IPC_MESSAGE_HANDLER(AutomationMsg_TabURL, GetTabURL)
347     IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_DomOperation,
348                                     ExecuteJavascript)
349     IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_OpenNewBrowserWindowOfType,
350                                     OpenNewBrowserWindowOfType)
351     IPC_MESSAGE_HANDLER(AutomationMsg_WindowForBrowser, GetWindowForBrowser)
352     IPC_MESSAGE_HANDLER(AutomationMsg_GetMetricEventDuration,
353                         GetMetricEventDuration)
354     IPC_MESSAGE_HANDLER(AutomationMsg_BringBrowserToFront, BringBrowserToFront)
355     IPC_MESSAGE_HANDLER(AutomationMsg_FindWindowVisibility,
356                         GetFindWindowVisibility)
357     IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForBookmarkModelToLoad,
358                                     WaitForBookmarkModelToLoad)
359     IPC_MESSAGE_HANDLER_DELAY_REPLY(
360         AutomationMsg_WaitForBrowserWindowCountToBecome,
361         WaitForBrowserWindowCountToBecome)
362     IPC_MESSAGE_HANDLER_DELAY_REPLY(
363         AutomationMsg_GoBackBlockUntilNavigationsComplete,
364         GoBackBlockUntilNavigationsComplete)
365     IPC_MESSAGE_HANDLER_DELAY_REPLY(
366         AutomationMsg_GoForwardBlockUntilNavigationsComplete,
367         GoForwardBlockUntilNavigationsComplete)
368     IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_SendJSONRequest,
369                                     SendJSONRequestWithBrowserIndex)
370     IPC_MESSAGE_HANDLER_DELAY_REPLY(
371         AutomationMsg_SendJSONRequestWithBrowserHandle,
372         SendJSONRequestWithBrowserHandle)
373     IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForTabCountToBecome,
374                                     WaitForTabCountToBecome)
375     IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForInfoBarCount,
376                                     WaitForInfoBarCount)
377     IPC_MESSAGE_HANDLER_DELAY_REPLY(
378         AutomationMsg_WaitForProcessLauncherThreadToGoIdle,
379         WaitForProcessLauncherThreadToGoIdle)
380
381     IPC_MESSAGE_UNHANDLED(
382         handled = AutomationProvider::OnMessageReceived(message))
383   IPC_END_MESSAGE_MAP_EX()
384   if (!deserialize_success)
385     OnMessageDeserializationFailure();
386   return handled;
387 }
388
389 void TestingAutomationProvider::OnChannelError() {
390   if (!reinitialize_on_channel_error_ &&
391       browser_shutdown::GetShutdownType() == browser_shutdown::NOT_VALID) {
392     chrome::AttemptExit();
393   }
394   AutomationProvider::OnChannelError();
395 }
396
397 void TestingAutomationProvider::CloseBrowser(int browser_handle,
398                                              IPC::Message* reply_message) {
399   if (!browser_tracker_->ContainsHandle(browser_handle))
400     return;
401
402   Browser* browser = browser_tracker_->GetResource(browser_handle);
403   new BrowserClosedNotificationObserver(browser, this, reply_message, false);
404   browser->window()->Close();
405 }
406
407 void TestingAutomationProvider::ActivateTab(int handle,
408                                             int at_index,
409                                             int* status) {
410   *status = -1;
411   if (browser_tracker_->ContainsHandle(handle) && at_index > -1) {
412     Browser* browser = browser_tracker_->GetResource(handle);
413     if (at_index >= 0 && at_index < browser->tab_strip_model()->count()) {
414       browser->tab_strip_model()->ActivateTabAt(at_index, true);
415       *status = 0;
416     }
417   }
418 }
419
420 void TestingAutomationProvider::AppendTab(int handle,
421                                           const GURL& url,
422                                           IPC::Message* reply_message) {
423   int append_tab_response = -1;  // -1 is the error code
424   TabAppendedNotificationObserver* observer = NULL;
425
426   if (browser_tracker_->ContainsHandle(handle)) {
427     Browser* browser = browser_tracker_->GetResource(handle);
428     observer = new TabAppendedNotificationObserver(browser, this,
429                                                    reply_message, false);
430     WebContents* contents =
431         chrome::AddSelectedTabWithURL(browser, url,
432                                       content::PAGE_TRANSITION_TYPED);
433     if (contents) {
434       append_tab_response = GetIndexForNavigationController(
435           &contents->GetController(), browser);
436     }
437   }
438
439   if (append_tab_response < 0) {
440     // Appending tab failed. Clean up and send failure response.
441
442     if (observer)
443       delete observer;
444
445     AutomationMsg_AppendTab::WriteReplyParams(reply_message,
446                                               append_tab_response);
447     Send(reply_message);
448   }
449 }
450
451 void TestingAutomationProvider::GetMachPortCount(int* port_count) {
452 #if defined(OS_MACOSX)
453   mach_port_name_array_t names;
454   mach_msg_type_number_t names_count;
455   mach_port_type_array_t types;
456   mach_msg_type_number_t types_count;
457
458   mach_port_t port = mach_task_self();
459
460   // A friendlier interface would allow NULL buffers to only get the counts.
461   kern_return_t kr = mach_port_names(port, &names, &names_count,
462                                      &types, &types_count);
463   if (kr != KERN_SUCCESS) {
464     *port_count = 0;
465     return;
466   }
467
468   // The documentation states this is an invariant.
469   DCHECK_EQ(names_count, types_count);
470   *port_count = names_count;
471
472   mach_vm_deallocate(port, reinterpret_cast<mach_vm_address_t>(names),
473       names_count * sizeof(mach_port_name_array_t));
474   mach_vm_deallocate(port, reinterpret_cast<mach_vm_address_t>(types),
475       types_count * sizeof(mach_port_type_array_t));
476 #else
477   *port_count = 0;
478 #endif
479 }
480
481 void TestingAutomationProvider::GetActiveTabIndex(int handle,
482                                                   int* active_tab_index) {
483   *active_tab_index = -1;  // -1 is the error code
484   if (browser_tracker_->ContainsHandle(handle)) {
485     Browser* browser = browser_tracker_->GetResource(handle);
486     *active_tab_index = browser->tab_strip_model()->active_index();
487   }
488 }
489
490 void TestingAutomationProvider::CloseTab(int tab_handle,
491                                          bool wait_until_closed,
492                                          IPC::Message* reply_message) {
493   if (tab_tracker_->ContainsHandle(tab_handle)) {
494     NavigationController* controller = tab_tracker_->GetResource(tab_handle);
495     Browser* browser = chrome::FindBrowserWithWebContents(
496         controller->GetWebContents());
497     DCHECK(browser);
498     new TabClosedNotificationObserver(this, wait_until_closed, reply_message,
499                                       false);
500     chrome::CloseWebContents(browser, controller->GetWebContents(), false);
501     return;
502   }
503
504   AutomationMsg_CloseTab::WriteReplyParams(reply_message, false);
505   Send(reply_message);
506 }
507
508 void TestingAutomationProvider::GetCookies(const GURL& url, int handle,
509                                            int* value_size,
510                                            std::string* value) {
511   WebContents* contents = tab_tracker_->ContainsHandle(handle) ?
512       tab_tracker_->GetResource(handle)->GetWebContents() : NULL;
513   automation_util::GetCookies(url, contents, value_size, value);
514 }
515
516 void TestingAutomationProvider::NavigateToURLBlockUntilNavigationsComplete(
517     int handle, const GURL& url, int number_of_navigations,
518     IPC::Message* reply_message) {
519   if (tab_tracker_->ContainsHandle(handle)) {
520     NavigationController* tab = tab_tracker_->GetResource(handle);
521
522     // Simulate what a user would do. Activate the tab and then navigate.
523     // We could allow navigating in a background tab in future.
524     Browser* browser = FindAndActivateTab(tab);
525
526     if (browser) {
527       new NavigationNotificationObserver(tab, this, reply_message,
528                                          number_of_navigations, false, false);
529
530       // TODO(darin): avoid conversion to GURL.
531       OpenURLParams params(
532           url, Referrer(), CURRENT_TAB,
533           content::PageTransitionFromInt(
534               content::PAGE_TRANSITION_TYPED |
535               content::PAGE_TRANSITION_FROM_ADDRESS_BAR),
536           false);
537       browser->OpenURL(params);
538       return;
539     }
540   }
541
542   AutomationMsg_NavigateToURLBlockUntilNavigationsComplete::WriteReplyParams(
543       reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
544   Send(reply_message);
545 }
546
547 void TestingAutomationProvider::NavigationAsync(int handle,
548                                                 const GURL& url,
549                                                 bool* status) {
550   *status = false;
551
552   if (tab_tracker_->ContainsHandle(handle)) {
553     NavigationController* tab = tab_tracker_->GetResource(handle);
554
555     // Simulate what a user would do. Activate the tab and then navigate.
556     // We could allow navigating in a background tab in future.
557     Browser* browser = FindAndActivateTab(tab);
558
559     if (browser) {
560       // Don't add any listener unless a callback mechanism is desired.
561       // TODO(vibhor): Do this if such a requirement arises in future.
562       OpenURLParams params(
563           url, Referrer(), CURRENT_TAB,
564           content::PageTransitionFromInt(
565               content::PAGE_TRANSITION_TYPED |
566               content::PAGE_TRANSITION_FROM_ADDRESS_BAR),
567           false);
568       browser->OpenURL(params);
569       *status = true;
570     }
571   }
572 }
573
574 void TestingAutomationProvider::Reload(int handle,
575                                        IPC::Message* reply_message) {
576   if (tab_tracker_->ContainsHandle(handle)) {
577     NavigationController* tab = tab_tracker_->GetResource(handle);
578     Browser* browser = FindAndActivateTab(tab);
579     if (chrome::IsCommandEnabled(browser, IDC_RELOAD)) {
580       new NavigationNotificationObserver(
581           tab, this, reply_message, 1, false, false);
582       chrome::ExecuteCommand(browser, IDC_RELOAD);
583       return;
584     }
585   }
586
587   AutomationMsg_Reload::WriteReplyParams(
588       reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
589   Send(reply_message);
590 }
591
592 void TestingAutomationProvider::GetBrowserWindowCount(int* window_count) {
593   // The automation layer doesn't support non-native desktops.
594   *window_count = static_cast<int>(BrowserList::GetInstance(
595                       chrome::HOST_DESKTOP_TYPE_NATIVE)->size());
596 }
597
598 void TestingAutomationProvider::GetNormalBrowserWindowCount(int* window_count) {
599   *window_count = static_cast<int>(chrome::GetTabbedBrowserCount(
600                       profile_, chrome::HOST_DESKTOP_TYPE_NATIVE));
601 }
602
603 void TestingAutomationProvider::GetBrowserWindow(int index, int* handle) {
604   *handle = 0;
605   Browser* browser = automation_util::GetBrowserAt(index);
606   if (browser)
607     *handle = browser_tracker_->Add(browser);
608 }
609
610 void TestingAutomationProvider::ExecuteBrowserCommandAsync(int handle,
611                                                            int command,
612                                                            bool* success) {
613   *success = false;
614   if (!browser_tracker_->ContainsHandle(handle)) {
615     LOG(WARNING) << "Browser tracker does not contain handle: " << handle;
616     return;
617   }
618   Browser* browser = browser_tracker_->GetResource(handle);
619   if (!chrome::SupportsCommand(browser, command)) {
620     LOG(WARNING) << "Browser does not support command: " << command;
621     return;
622   }
623   if (!chrome::IsCommandEnabled(browser, command)) {
624     LOG(WARNING) << "Browser command not enabled: " << command;
625     return;
626   }
627   chrome::ExecuteCommand(browser, command);
628   *success = true;
629 }
630
631 void TestingAutomationProvider::ExecuteBrowserCommand(
632     int handle, int command, IPC::Message* reply_message) {
633   if (browser_tracker_->ContainsHandle(handle)) {
634     Browser* browser = browser_tracker_->GetResource(handle);
635     if (chrome::SupportsCommand(browser, command) &&
636         chrome::IsCommandEnabled(browser, command)) {
637       // First check if we can handle the command without using an observer.
638       for (size_t i = 0; i < arraysize(kSynchronousCommands); i++) {
639         if (command == kSynchronousCommands[i]) {
640           chrome::ExecuteCommand(browser, command);
641           AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message,
642                                                                true);
643           Send(reply_message);
644           return;
645         }
646       }
647
648       // Use an observer if we have one, otherwise fail.
649       if (ExecuteBrowserCommandObserver::CreateAndRegisterObserver(
650           this, browser, command, reply_message, false)) {
651         chrome::ExecuteCommand(browser, command);
652         return;
653       }
654     }
655   }
656   AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message, false);
657   Send(reply_message);
658 }
659
660 void TestingAutomationProvider::WebkitMouseClick(base::DictionaryValue* args,
661                                                  IPC::Message* reply_message) {
662   if (SendErrorIfModalDialogActive(this, reply_message))
663     return;
664
665   RenderViewHost* view;
666   std::string error;
667   if (!GetRenderViewFromJSONArgs(args, profile(), &view, &error)) {
668     AutomationJSONReply(this, reply_message).SendError(error);
669     return;
670   }
671
672   blink::WebMouseEvent mouse_event;
673   if (!args->GetInteger("x", &mouse_event.x) ||
674       !args->GetInteger("y", &mouse_event.y)) {
675     AutomationJSONReply(this, reply_message)
676         .SendError("(X,Y) coordinates missing or invalid");
677     return;
678   }
679
680   int button;
681   if (!args->GetInteger("button", &button)) {
682     AutomationJSONReply(this, reply_message)
683         .SendError("Mouse button missing or invalid");
684     return;
685   }
686   if (button == automation::kLeftButton) {
687     mouse_event.button = blink::WebMouseEvent::ButtonLeft;
688   } else if (button == automation::kRightButton) {
689     mouse_event.button = blink::WebMouseEvent::ButtonRight;
690   } else if (button == automation::kMiddleButton) {
691     mouse_event.button = blink::WebMouseEvent::ButtonMiddle;
692   } else {
693     AutomationJSONReply(this, reply_message)
694         .SendError("Invalid button press requested");
695     return;
696   }
697
698   mouse_event.type = blink::WebInputEvent::MouseDown;
699   mouse_event.clickCount = 1;
700
701   view->ForwardMouseEvent(mouse_event);
702
703   mouse_event.type = blink::WebInputEvent::MouseUp;
704   new InputEventAckNotificationObserver(this, reply_message, mouse_event.type,
705                                         1);
706   view->ForwardMouseEvent(mouse_event);
707 }
708
709 void TestingAutomationProvider::WebkitMouseMove(
710     base::DictionaryValue* args, IPC::Message* reply_message) {
711   if (SendErrorIfModalDialogActive(this, reply_message))
712     return;
713
714   RenderViewHost* view;
715   std::string error;
716   if (!GetRenderViewFromJSONArgs(args, profile(), &view, &error)) {
717     AutomationJSONReply(this, reply_message).SendError(error);
718     return;
719   }
720
721   blink::WebMouseEvent mouse_event;
722   if (!args->GetInteger("x", &mouse_event.x) ||
723       !args->GetInteger("y", &mouse_event.y)) {
724     AutomationJSONReply(this, reply_message)
725         .SendError("(X,Y) coordinates missing or invalid");
726     return;
727   }
728
729   mouse_event.type = blink::WebInputEvent::MouseMove;
730   new InputEventAckNotificationObserver(this, reply_message, mouse_event.type,
731                                         1);
732   view->ForwardMouseEvent(mouse_event);
733 }
734
735 void TestingAutomationProvider::WebkitMouseDrag(base::DictionaryValue* args,
736                                                 IPC::Message* reply_message) {
737   if (SendErrorIfModalDialogActive(this, reply_message))
738     return;
739
740   RenderViewHost* view;
741   std::string error;
742   if (!GetRenderViewFromJSONArgs(args, profile(), &view, &error)) {
743     AutomationJSONReply(this, reply_message).SendError(error);
744     return;
745   }
746
747   blink::WebMouseEvent mouse_event;
748   int start_x, start_y, end_x, end_y;
749   if (!args->GetInteger("start_x", &start_x) ||
750       !args->GetInteger("start_y", &start_y) ||
751       !args->GetInteger("end_x", &end_x) ||
752       !args->GetInteger("end_y", &end_y)) {
753     AutomationJSONReply(this, reply_message)
754         .SendError("Invalid start/end positions");
755     return;
756   }
757
758   mouse_event.type = blink::WebInputEvent::MouseMove;
759   // Step 1- Move the mouse to the start position.
760   mouse_event.x = start_x;
761   mouse_event.y = start_y;
762   view->ForwardMouseEvent(mouse_event);
763
764   // Step 2- Left click mouse down, the mouse button is fixed.
765   mouse_event.type = blink::WebInputEvent::MouseDown;
766   mouse_event.button = blink::WebMouseEvent::ButtonLeft;
767   mouse_event.clickCount = 1;
768   view->ForwardMouseEvent(mouse_event);
769
770   // Step 3 - Move the mouse to the end position.
771   mouse_event.type = blink::WebInputEvent::MouseMove;
772   mouse_event.x = end_x;
773   mouse_event.y = end_y;
774   mouse_event.clickCount = 0;
775   view->ForwardMouseEvent(mouse_event);
776
777   // Step 4 - Release the left mouse button.
778   mouse_event.type = blink::WebInputEvent::MouseUp;
779   mouse_event.clickCount = 1;
780   new InputEventAckNotificationObserver(this, reply_message, mouse_event.type,
781                                         1);
782   view->ForwardMouseEvent(mouse_event);
783 }
784
785 void TestingAutomationProvider::WebkitMouseButtonDown(
786     base::DictionaryValue* args, IPC::Message* reply_message) {
787   if (SendErrorIfModalDialogActive(this, reply_message))
788     return;
789
790   RenderViewHost* view;
791   std::string error;
792   if (!GetRenderViewFromJSONArgs(args, profile(), &view, &error)) {
793     AutomationJSONReply(this, reply_message).SendError(error);
794     return;
795   }
796
797   blink::WebMouseEvent mouse_event;
798   if (!args->GetInteger("x", &mouse_event.x) ||
799       !args->GetInteger("y", &mouse_event.y)) {
800     AutomationJSONReply(this, reply_message)
801         .SendError("(X,Y) coordinates missing or invalid");
802     return;
803   }
804
805   mouse_event.type = blink::WebInputEvent::MouseDown;
806   mouse_event.button = blink::WebMouseEvent::ButtonLeft;
807   mouse_event.clickCount = 1;
808   new InputEventAckNotificationObserver(this, reply_message, mouse_event.type,
809                                         1);
810   view->ForwardMouseEvent(mouse_event);
811 }
812
813 void TestingAutomationProvider::WebkitMouseButtonUp(
814     base::DictionaryValue* args, IPC::Message* reply_message) {
815   if (SendErrorIfModalDialogActive(this, reply_message))
816     return;
817
818   RenderViewHost* view;
819   std::string error;
820   if (!GetRenderViewFromJSONArgs(args, profile(), &view, &error)) {
821     AutomationJSONReply(this, reply_message).SendError(error);
822     return;
823   }
824
825   blink::WebMouseEvent mouse_event;
826   if (!args->GetInteger("x", &mouse_event.x) ||
827       !args->GetInteger("y", &mouse_event.y)) {
828     AutomationJSONReply(this, reply_message)
829         .SendError("(X,Y) coordinates missing or invalid");
830     return;
831   }
832
833   mouse_event.type = blink::WebInputEvent::MouseUp;
834   mouse_event.button = blink::WebMouseEvent::ButtonLeft;
835   mouse_event.clickCount = 1;
836   new InputEventAckNotificationObserver(this, reply_message, mouse_event.type,
837                                         1);
838   view->ForwardMouseEvent(mouse_event);
839 }
840
841 void TestingAutomationProvider::WebkitMouseDoubleClick(
842     base::DictionaryValue* args, IPC::Message* reply_message) {
843   if (SendErrorIfModalDialogActive(this, reply_message))
844     return;
845
846   RenderViewHost* view;
847   std::string error;
848   if (!GetRenderViewFromJSONArgs(args, profile(), &view, &error)) {
849     AutomationJSONReply(this, reply_message).SendError(error);
850     return;
851   }
852
853   blink::WebMouseEvent mouse_event;
854   if (!args->GetInteger("x", &mouse_event.x) ||
855       !args->GetInteger("y", &mouse_event.y)) {
856     AutomationJSONReply(this, reply_message)
857         .SendError("(X,Y) coordinates missing or invalid");
858     return;
859   }
860
861   mouse_event.type = blink::WebInputEvent::MouseDown;
862   mouse_event.button = blink::WebMouseEvent::ButtonLeft;
863   mouse_event.clickCount = 1;
864   view->ForwardMouseEvent(mouse_event);
865
866   mouse_event.type = blink::WebInputEvent::MouseUp;
867   new InputEventAckNotificationObserver(this, reply_message, mouse_event.type,
868                                         2);
869   view->ForwardMouseEvent(mouse_event);
870
871   mouse_event.type = blink::WebInputEvent::MouseDown;
872   mouse_event.clickCount = 2;
873   view->ForwardMouseEvent(mouse_event);
874
875   mouse_event.type = blink::WebInputEvent::MouseUp;
876   view->ForwardMouseEvent(mouse_event);
877 }
878
879 void TestingAutomationProvider::DragAndDropFilePaths(
880     base::DictionaryValue* args, IPC::Message* reply_message) {
881   if (SendErrorIfModalDialogActive(this, reply_message))
882     return;
883
884   RenderViewHost* view;
885   std::string error;
886   if (!GetRenderViewFromJSONArgs(args, profile(), &view, &error)) {
887     AutomationJSONReply(this, reply_message).SendError(error);
888     return;
889   }
890
891   int x, y;
892   if (!args->GetInteger("x", &x) || !args->GetInteger("y", &y)) {
893     AutomationJSONReply(this, reply_message)
894         .SendError("(X,Y) coordinates missing or invalid");
895     return;
896   }
897
898   base::ListValue* paths = NULL;
899   if (!args->GetList("paths", &paths)) {
900     AutomationJSONReply(this, reply_message)
901         .SendError("'paths' missing or invalid");
902     return;
903   }
904
905   // Emulate drag and drop to set the file paths to the file upload control.
906   content::DropData drop_data;
907   for (size_t path_index = 0; path_index < paths->GetSize(); ++path_index) {
908     base::string16 path;
909     if (!paths->GetString(path_index, &path)) {
910       AutomationJSONReply(this, reply_message)
911           .SendError("'paths' contains a non-string type");
912       return;
913     }
914
915     drop_data.filenames.push_back(
916         content::DropData::FileInfo(path, base::string16()));
917   }
918
919   const gfx::Point client(x, y);
920   // We don't set any values in screen variable because DragTarget*** ignore the
921   // screen argument.
922   const gfx::Point screen;
923
924   int operations = 0;
925   operations |= blink::WebDragOperationCopy;
926   operations |= blink::WebDragOperationLink;
927   operations |= blink::WebDragOperationMove;
928
929   view->DragTargetDragEnter(
930       drop_data, client, screen,
931       static_cast<blink::WebDragOperationsMask>(operations), 0);
932   new DragTargetDropAckNotificationObserver(this, reply_message);
933   view->DragTargetDrop(client, screen, 0);
934 }
935
936 void TestingAutomationProvider::GetTabCount(int handle, int* tab_count) {
937   *tab_count = -1;  // -1 is the error code
938
939   if (browser_tracker_->ContainsHandle(handle)) {
940     Browser* browser = browser_tracker_->GetResource(handle);
941     *tab_count = browser->tab_strip_model()->count();
942   }
943 }
944
945 void TestingAutomationProvider::GetType(int handle, int* type_as_int) {
946   *type_as_int = -1;  // -1 is the error code
947
948   if (browser_tracker_->ContainsHandle(handle)) {
949     Browser* browser = browser_tracker_->GetResource(handle);
950     *type_as_int = static_cast<int>(browser->type());
951   }
952 }
953
954 void TestingAutomationProvider::GetTab(int win_handle,
955                                        int tab_index,
956                                        int* tab_handle) {
957   *tab_handle = 0;
958   if (browser_tracker_->ContainsHandle(win_handle) && (tab_index >= 0)) {
959     Browser* browser = browser_tracker_->GetResource(win_handle);
960     if (tab_index < browser->tab_strip_model()->count()) {
961       WebContents* web_contents =
962           browser->tab_strip_model()->GetWebContentsAt(tab_index);
963       *tab_handle = tab_tracker_->Add(&web_contents->GetController());
964     }
965   }
966 }
967
968 void TestingAutomationProvider::GetTabTitle(int handle,
969                                             int* title_string_size,
970                                             std::wstring* title) {
971   *title_string_size = -1;  // -1 is the error code
972   if (tab_tracker_->ContainsHandle(handle)) {
973     NavigationController* tab = tab_tracker_->GetResource(handle);
974     NavigationEntry* entry = tab->GetActiveEntry();
975     if (entry != NULL) {
976       *title = base::UTF16ToWideHack(entry->GetTitleForDisplay(std::string()));
977     } else {
978       *title = std::wstring();
979     }
980     *title_string_size = static_cast<int>(title->size());
981   }
982 }
983
984 void TestingAutomationProvider::GetTabIndex(int handle, int* tabstrip_index) {
985   *tabstrip_index = -1;  // -1 is the error code
986
987   if (tab_tracker_->ContainsHandle(handle)) {
988     NavigationController* tab = tab_tracker_->GetResource(handle);
989     Browser* browser = chrome::FindBrowserWithWebContents(
990         tab->GetWebContents());
991     *tabstrip_index = browser->tab_strip_model()->GetIndexOfWebContents(
992         tab->GetWebContents());
993   }
994 }
995
996 void TestingAutomationProvider::GetTabURL(int handle,
997                                           bool* success,
998                                           GURL* url) {
999   *success = false;
1000   if (tab_tracker_->ContainsHandle(handle)) {
1001     NavigationController* tab = tab_tracker_->GetResource(handle);
1002     // Return what the user would see in the location bar.
1003     *url = tab->GetActiveEntry()->GetVirtualURL();
1004     *success = true;
1005   }
1006 }
1007
1008 void TestingAutomationProvider::ExecuteJavascriptInRenderViewFrame(
1009     const base::string16& frame_xpath,
1010     const base::string16& script,
1011     IPC::Message* reply_message,
1012     RenderViewHost* render_view_host) {
1013   // Set the routing id of this message with the controller.
1014   // This routing id needs to be remembered for the reverse
1015   // communication while sending back the response of
1016   // this javascript execution.
1017   render_view_host->ExecuteJavascriptInWebFrame(
1018       frame_xpath,
1019       base::ASCIIToUTF16("window.domAutomationController.setAutomationId(0);"));
1020   render_view_host->ExecuteJavascriptInWebFrame(
1021       frame_xpath, script);
1022 }
1023
1024 void TestingAutomationProvider::ExecuteJavascript(
1025     int handle,
1026     const std::wstring& frame_xpath,
1027     const std::wstring& script,
1028     IPC::Message* reply_message) {
1029   WebContents* web_contents = GetWebContentsForHandle(handle, NULL);
1030   if (!web_contents) {
1031     AutomationMsg_DomOperation::WriteReplyParams(reply_message, std::string());
1032     Send(reply_message);
1033     return;
1034   }
1035
1036   new DomOperationMessageSender(this, reply_message, false);
1037   ExecuteJavascriptInRenderViewFrame(base::WideToUTF16Hack(frame_xpath),
1038                                      base::WideToUTF16Hack(script),
1039                                      reply_message,
1040                                      web_contents->GetRenderViewHost());
1041 }
1042
1043 // Sample json input: { "command": "OpenNewBrowserWindowWithNewProfile" }
1044 // Sample output: {}
1045 void TestingAutomationProvider::OpenNewBrowserWindowWithNewProfile(
1046     base::DictionaryValue* args, IPC::Message* reply_message) {
1047   ProfileManager* profile_manager = g_browser_process->profile_manager();
1048   new BrowserOpenedWithNewProfileNotificationObserver(this, reply_message);
1049   profile_manager->CreateMultiProfileAsync(
1050       base::string16(), base::string16(),
1051       ProfileManager::CreateCallback(), std::string());
1052 }
1053
1054 // Sample json input: { "command": "GetMultiProfileInfo" }
1055 // See GetMultiProfileInfo() in pyauto.py for sample output.
1056 void TestingAutomationProvider::GetMultiProfileInfo(
1057     base::DictionaryValue* args, IPC::Message* reply_message) {
1058   scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
1059   ProfileManager* profile_manager = g_browser_process->profile_manager();
1060   const ProfileInfoCache& profile_info_cache =
1061       profile_manager->GetProfileInfoCache();
1062   return_value->SetBoolean("enabled", profiles::IsMultipleProfilesEnabled());
1063
1064   base::ListValue* profiles = new base::ListValue;
1065   for (size_t index = 0; index < profile_info_cache.GetNumberOfProfiles();
1066        ++index) {
1067     base::DictionaryValue* item = new base::DictionaryValue;
1068     item->SetString("name", profile_info_cache.GetNameOfProfileAtIndex(index));
1069     item->SetString("path",
1070                     profile_info_cache.GetPathOfProfileAtIndex(index).value());
1071     profiles->Append(item);
1072   }
1073   return_value->Set("profiles", profiles);
1074   AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
1075 }
1076
1077 void TestingAutomationProvider::OpenNewBrowserWindowOfType(
1078     int type, bool show, IPC::Message* reply_message) {
1079   new BrowserOpenedNotificationObserver(this, reply_message, false);
1080   // We may have no current browser windows open so don't rely on
1081   // asking an existing browser to execute the IDC_NEWWINDOW command.
1082   Browser* browser = new Browser(
1083       Browser::CreateParams(static_cast<Browser::Type>(type), profile_,
1084                             chrome::HOST_DESKTOP_TYPE_NATIVE));
1085   chrome::AddTabAt(browser, GURL(), -1, true);
1086   if (show)
1087     browser->window()->Show();
1088 }
1089
1090 void TestingAutomationProvider::OpenNewBrowserWindow(
1091     base::DictionaryValue* args,
1092     IPC::Message* reply_message) {
1093   bool show;
1094   if (!args->GetBoolean("show", &show)) {
1095     AutomationJSONReply(this, reply_message)
1096         .SendError("'show' missing or invalid.");
1097     return;
1098   }
1099   new BrowserOpenedNotificationObserver(this, reply_message, true);
1100   Browser* browser = new Browser(
1101       Browser::CreateParams(Browser::TYPE_TABBED, profile_,
1102                             chrome::HOST_DESKTOP_TYPE_NATIVE));
1103   chrome::AddTabAt(browser, GURL(), -1, true);
1104   if (show)
1105     browser->window()->Show();
1106 }
1107
1108 void TestingAutomationProvider::GetBrowserWindowCountJSON(
1109     base::DictionaryValue* args,
1110     IPC::Message* reply_message) {
1111   base::DictionaryValue dict;
1112   // The automation layer doesn't support non-native desktops.
1113   dict.SetInteger("count",
1114                   static_cast<int>(BrowserList::GetInstance(
1115                       chrome::HOST_DESKTOP_TYPE_NATIVE)->size()));
1116   AutomationJSONReply(this, reply_message).SendSuccess(&dict);
1117 }
1118
1119 void TestingAutomationProvider::CloseBrowserWindow(
1120     base::DictionaryValue* args,
1121     IPC::Message* reply_message) {
1122   AutomationJSONReply reply(this, reply_message);
1123   Browser* browser;
1124   std::string error_msg;
1125   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
1126     reply.SendError(error_msg);
1127     return;
1128   }
1129   new BrowserClosedNotificationObserver(browser, this, reply_message, true);
1130   browser->window()->Close();
1131 }
1132
1133 void TestingAutomationProvider::OpenProfileWindow(
1134     base::DictionaryValue* args, IPC::Message* reply_message) {
1135   ProfileManager* profile_manager = g_browser_process->profile_manager();
1136   base::FilePath::StringType path;
1137   if (!args->GetString("path", &path)) {
1138     AutomationJSONReply(this, reply_message).SendError(
1139         "Invalid or missing arg: 'path'");
1140     return;
1141   }
1142   Profile* profile = profile_manager->GetProfileByPath(base::FilePath(path));
1143   if (!profile) {
1144     AutomationJSONReply(this, reply_message).SendError(
1145         base::StringPrintf("Invalid profile path: %s", path.c_str()));
1146     return;
1147   }
1148   int num_loads;
1149   if (!args->GetInteger("num_loads", &num_loads)) {
1150     AutomationJSONReply(this, reply_message).SendError(
1151         "Invalid or missing arg: 'num_loads'");
1152     return;
1153   }
1154   new BrowserOpenedWithExistingProfileNotificationObserver(
1155       this, reply_message, num_loads);
1156   profiles::FindOrCreateNewWindowForProfile(
1157       profile,
1158       chrome::startup::IS_NOT_PROCESS_STARTUP,
1159       chrome::startup::IS_NOT_FIRST_RUN,
1160       chrome::HOST_DESKTOP_TYPE_NATIVE,
1161       false);
1162   }
1163
1164 void TestingAutomationProvider::GetWindowForBrowser(int browser_handle,
1165                                                     bool* success,
1166                                                     int* handle) {
1167   *success = false;
1168   *handle = 0;
1169
1170   if (browser_tracker_->ContainsHandle(browser_handle)) {
1171     Browser* browser = browser_tracker_->GetResource(browser_handle);
1172     gfx::NativeWindow win = browser->window()->GetNativeWindow();
1173     // Add() returns the existing handle for the resource if any.
1174     *handle = window_tracker_->Add(win);
1175     *success = true;
1176   }
1177 }
1178
1179 void TestingAutomationProvider::GetMetricEventDuration(
1180     const std::string& event_name,
1181     int* duration_ms) {
1182   *duration_ms = metric_event_duration_observer_->GetEventDurationMs(
1183       event_name);
1184 }
1185
1186 void TestingAutomationProvider::BringBrowserToFront(int browser_handle,
1187                                                     bool* success) {
1188   *success = false;
1189   if (browser_tracker_->ContainsHandle(browser_handle)) {
1190     Browser* browser = browser_tracker_->GetResource(browser_handle);
1191     browser->window()->Activate();
1192     *success = true;
1193   }
1194 }
1195
1196 void TestingAutomationProvider::GetFindWindowVisibility(int handle,
1197                                                         bool* visible) {
1198   *visible = false;
1199   Browser* browser = browser_tracker_->GetResource(handle);
1200   if (browser) {
1201     FindBarTesting* find_bar =
1202         browser->GetFindBarController()->find_bar()->GetFindBarTesting();
1203     find_bar->GetFindBarWindowInfo(NULL, visible);
1204   }
1205 }
1206
1207 // Bookmark bar visibility is based on the pref (e.g. is it in the toolbar).
1208 // Presence in the NTP is signalled in |detached|.
1209 void TestingAutomationProvider::GetBookmarkBarStatus(
1210     base::DictionaryValue* args,
1211     IPC::Message* reply_message) {
1212   AutomationJSONReply reply(this, reply_message);
1213   Browser* browser;
1214   std::string error_msg;
1215   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
1216     reply.SendError(error_msg);
1217     return;
1218   }
1219   // browser->window()->IsBookmarkBarVisible() is not consistent across
1220   // platforms. bookmark_bar_state() also follows prefs::kShowBookmarkBar
1221   // and has a shared implementation on all platforms.
1222   base::DictionaryValue dict;
1223   dict.SetBoolean("visible",
1224                   browser->bookmark_bar_state() == BookmarkBar::SHOW);
1225   dict.SetBoolean("animating", browser->window()->IsBookmarkBarAnimating());
1226   dict.SetBoolean("detached",
1227                   browser->bookmark_bar_state() == BookmarkBar::DETACHED);
1228   reply.SendSuccess(&dict);
1229 }
1230
1231 void TestingAutomationProvider::GetBookmarksAsJSON(
1232     base::DictionaryValue* args,
1233     IPC::Message* reply_message) {
1234   AutomationJSONReply reply(this, reply_message);
1235   Browser* browser;
1236   std::string error_msg, bookmarks_as_json;
1237   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
1238     reply.SendError(error_msg);
1239     return;
1240   }
1241   BookmarkModel* bookmark_model =
1242       BookmarkModelFactory::GetForProfile(browser->profile());
1243   if (!bookmark_model->loaded()) {
1244     reply.SendError("Bookmark model is not loaded");
1245     return;
1246   }
1247   scoped_refptr<BookmarkStorage> storage(
1248       new BookmarkStorage(browser->profile(),
1249                           bookmark_model,
1250                           browser->profile()->GetIOTaskRunner().get()));
1251   if (!storage->SerializeData(&bookmarks_as_json)) {
1252     reply.SendError("Failed to serialize bookmarks");
1253     return;
1254   }
1255   base::DictionaryValue dict;
1256   dict.SetString("bookmarks_as_json", bookmarks_as_json);
1257   reply.SendSuccess(&dict);
1258 }
1259
1260 void TestingAutomationProvider::WaitForBookmarkModelToLoad(
1261     int handle,
1262     IPC::Message* reply_message) {
1263   if (browser_tracker_->ContainsHandle(handle)) {
1264     Browser* browser = browser_tracker_->GetResource(handle);
1265     BookmarkModel* model =
1266         BookmarkModelFactory::GetForProfile(browser->profile());
1267     AutomationProviderBookmarkModelObserver* observer =
1268         new AutomationProviderBookmarkModelObserver(this, reply_message,
1269                                                     model, false);
1270     if (model->loaded()) {
1271       observer->ReleaseReply();
1272       delete observer;
1273       AutomationMsg_WaitForBookmarkModelToLoad::WriteReplyParams(
1274           reply_message, true);
1275       Send(reply_message);
1276     }
1277   }
1278 }
1279
1280 void TestingAutomationProvider::WaitForBookmarkModelToLoadJSON(
1281     base::DictionaryValue* args,
1282     IPC::Message* reply_message) {
1283   Browser* browser;
1284   std::string error_msg;
1285   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
1286     AutomationJSONReply(this, reply_message).SendError(error_msg);
1287     return;
1288   }
1289   BookmarkModel* model =
1290       BookmarkModelFactory::GetForProfile(browser->profile());
1291   AutomationProviderBookmarkModelObserver* observer =
1292       new AutomationProviderBookmarkModelObserver(this, reply_message, model,
1293                                                   true);
1294   if (model->loaded()) {
1295     observer->ReleaseReply();
1296     delete observer;
1297     AutomationJSONReply(this, reply_message).SendSuccess(NULL);
1298     return;
1299   }
1300 }
1301
1302 void TestingAutomationProvider::AddBookmark(
1303     base::DictionaryValue* args,
1304     IPC::Message* reply_message) {
1305   AutomationJSONReply reply(this, reply_message);
1306   Browser* browser;
1307   std::string error_msg, url;
1308   base::string16 title;
1309   int parent_id, index;
1310   bool folder;
1311   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
1312     reply.SendError(error_msg);
1313     return;
1314   }
1315   if (!args->GetBoolean("is_folder", &folder)) {
1316     reply.SendError("'is_folder' missing or invalid");
1317     return;
1318   }
1319   if (!folder && !args->GetString("url", &url)) {
1320     reply.SendError("'url' missing or invalid");
1321     return;
1322   }
1323   if (!args->GetInteger("parent_id", &parent_id)) {
1324     reply.SendError("'parent_id' missing or invalid");
1325     return;
1326   }
1327   if (!args->GetInteger("index", &index)) {
1328     reply.SendError("'index' missing or invalid");
1329     return;
1330   }
1331   if (!args->GetString("title", &title)) {
1332     reply.SendError("'title' missing or invalid");
1333     return;
1334   }
1335   BookmarkModel* model =
1336       BookmarkModelFactory::GetForProfile(browser->profile());
1337   if (!model->loaded()) {
1338     reply.SendError("Bookmark model is not loaded");
1339     return;
1340   }
1341   const BookmarkNode* parent = model->GetNodeByID(parent_id);
1342   if (!parent) {
1343     reply.SendError("Failed to get parent bookmark node");
1344     return;
1345   }
1346   const BookmarkNode* child;
1347   if (folder) {
1348     child = model->AddFolder(parent, index, title);
1349   } else {
1350     child = model->AddURL(parent, index, title, GURL(url));
1351   }
1352   if (!child) {
1353     reply.SendError("Failed to add bookmark");
1354     return;
1355   }
1356   reply.SendSuccess(NULL);
1357 }
1358
1359 void TestingAutomationProvider::ReparentBookmark(base::DictionaryValue* args,
1360                                                  IPC::Message* reply_message) {
1361   AutomationJSONReply reply(this, reply_message);
1362   Browser* browser;
1363   std::string error_msg;
1364   int new_parent_id, id, index;
1365   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
1366     reply.SendError(error_msg);
1367     return;
1368   }
1369   if (!args->GetInteger("id", &id)) {
1370     reply.SendError("'id' missing or invalid");
1371     return;
1372   }
1373   if (!args->GetInteger("new_parent_id", &new_parent_id)) {
1374     reply.SendError("'new_parent_id' missing or invalid");
1375     return;
1376   }
1377   if (!args->GetInteger("index", &index)) {
1378     reply.SendError("'index' missing or invalid");
1379     return;
1380   }
1381   BookmarkModel* model =
1382       BookmarkModelFactory::GetForProfile(browser->profile());
1383   if (!model->loaded()) {
1384     reply.SendError("Bookmark model is not loaded");
1385     return;
1386   }
1387   const BookmarkNode* node = model->GetNodeByID(id);
1388   const BookmarkNode* new_parent = model->GetNodeByID(new_parent_id);
1389   if (!node) {
1390     reply.SendError("Failed to get bookmark node");
1391     return;
1392   }
1393   if (!new_parent) {
1394     reply.SendError("Failed to get new parent bookmark node");
1395     return;
1396   }
1397   model->Move(node, new_parent, index);
1398   reply.SendSuccess(NULL);
1399 }
1400
1401 void TestingAutomationProvider::SetBookmarkTitle(base::DictionaryValue* args,
1402                                                  IPC::Message* reply_message) {
1403   AutomationJSONReply reply(this, reply_message);
1404   Browser* browser;
1405   std::string error_msg;
1406   base::string16 title;
1407   int id;
1408   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
1409     reply.SendError(error_msg);
1410     return;
1411   }
1412   if (!args->GetInteger("id", &id)) {
1413     reply.SendError("'id' missing or invalid");
1414     return;
1415   }
1416   if (!args->GetString("title", &title)) {
1417     reply.SendError("'title' missing or invalid");
1418     return;
1419   }
1420   BookmarkModel* model =
1421       BookmarkModelFactory::GetForProfile(browser->profile());
1422   if (!model->loaded()) {
1423     reply.SendError("Bookmark model is not loaded");
1424     return;
1425   }
1426   const BookmarkNode* node = model->GetNodeByID(id);
1427   if (!node) {
1428     reply.SendError("Failed to get bookmark node");
1429     return;
1430   }
1431   model->SetTitle(node, title);
1432   reply.SendSuccess(NULL);
1433 }
1434
1435 void TestingAutomationProvider::SetBookmarkURL(base::DictionaryValue* args,
1436                                                IPC::Message* reply_message) {
1437   AutomationJSONReply reply(this, reply_message);
1438   Browser* browser;
1439   std::string error_msg, url;
1440   int id;
1441   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
1442     reply.SendError(error_msg);
1443     return;
1444   }
1445   if (!args->GetInteger("id", &id)) {
1446     reply.SendError("'id' missing or invalid");
1447     return;
1448   }
1449   if (!args->GetString("url", &url)) {
1450     reply.SendError("'url' missing or invalid");
1451     return;
1452   }
1453   BookmarkModel* model =
1454       BookmarkModelFactory::GetForProfile(browser->profile());
1455   if (!model->loaded()) {
1456     reply.SendError("Bookmark model is not loaded");
1457     return;
1458   }
1459   const BookmarkNode* node = model->GetNodeByID(id);
1460   if (!node) {
1461     reply.SendError("Failed to get bookmark node");
1462     return;
1463   }
1464   model->SetURL(node, GURL(url));
1465   reply.SendSuccess(NULL);
1466 }
1467
1468 void TestingAutomationProvider::RemoveBookmark(base::DictionaryValue* args,
1469                                                IPC::Message* reply_message) {
1470   AutomationJSONReply reply(this, reply_message);
1471   Browser* browser;
1472   std::string error_msg;
1473   int id;
1474   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
1475     reply.SendError(error_msg);
1476     return;
1477   }
1478   if (!args->GetInteger("id", &id)) {
1479     reply.SendError("'id' missing or invalid");
1480     return;
1481   }
1482   BookmarkModel* model =
1483       BookmarkModelFactory::GetForProfile(browser->profile());
1484   if (!model->loaded()) {
1485     reply.SendError("Bookmark model is not loaded");
1486     return;
1487   }
1488   const BookmarkNode* node = model->GetNodeByID(id);
1489   if (!node) {
1490     reply.SendError("Failed to get bookmark node");
1491     return;
1492   }
1493   const BookmarkNode* parent = node->parent();
1494   if (!parent) {
1495     reply.SendError("Failed to get parent bookmark node");
1496     return;
1497   }
1498   model->Remove(parent, parent->GetIndexOf(node));
1499   reply.SendSuccess(NULL);
1500 }
1501
1502 void TestingAutomationProvider::WaitForBrowserWindowCountToBecome(
1503     int target_count,
1504     IPC::Message* reply_message) {
1505   // The automation layer doesn't support non-native desktops.
1506   int current_count = static_cast<int>(BrowserList::GetInstance(
1507                           chrome::HOST_DESKTOP_TYPE_NATIVE)->size());
1508   if (current_count == target_count) {
1509     AutomationMsg_WaitForBrowserWindowCountToBecome::WriteReplyParams(
1510         reply_message, true);
1511     Send(reply_message);
1512     return;
1513   }
1514
1515   // Set up an observer (it will delete itself).
1516   new BrowserCountChangeNotificationObserver(target_count, this, reply_message);
1517 }
1518
1519 void TestingAutomationProvider::GoBackBlockUntilNavigationsComplete(
1520     int handle, int number_of_navigations, IPC::Message* reply_message) {
1521   if (tab_tracker_->ContainsHandle(handle)) {
1522     NavigationController* tab = tab_tracker_->GetResource(handle);
1523     Browser* browser = FindAndActivateTab(tab);
1524     if (chrome::IsCommandEnabled(browser, IDC_BACK)) {
1525       new NavigationNotificationObserver(tab, this, reply_message,
1526                                          number_of_navigations, false, false);
1527       chrome::ExecuteCommand(browser, IDC_BACK);
1528       return;
1529     }
1530   }
1531
1532   AutomationMsg_GoBackBlockUntilNavigationsComplete::WriteReplyParams(
1533       reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
1534   Send(reply_message);
1535 }
1536
1537 void TestingAutomationProvider::GoForwardBlockUntilNavigationsComplete(
1538     int handle, int number_of_navigations, IPC::Message* reply_message) {
1539   if (tab_tracker_->ContainsHandle(handle)) {
1540     NavigationController* tab = tab_tracker_->GetResource(handle);
1541     Browser* browser = FindAndActivateTab(tab);
1542     if (chrome::IsCommandEnabled(browser, IDC_FORWARD)) {
1543       new NavigationNotificationObserver(tab, this, reply_message,
1544                                          number_of_navigations, false, false);
1545       chrome::ExecuteCommand(browser, IDC_FORWARD);
1546       return;
1547     }
1548   }
1549
1550   AutomationMsg_GoForwardBlockUntilNavigationsComplete::WriteReplyParams(
1551       reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
1552   Send(reply_message);
1553 }
1554
1555 void TestingAutomationProvider::BuildJSONHandlerMaps() {
1556   // Map json commands to their handlers.
1557   handler_map_["ApplyAccelerator"] =
1558       &TestingAutomationProvider::ExecuteBrowserCommandAsyncJSON;
1559   handler_map_["RunCommand"] =
1560       &TestingAutomationProvider::ExecuteBrowserCommandJSON;
1561   handler_map_["IsMenuCommandEnabled"] =
1562       &TestingAutomationProvider::IsMenuCommandEnabledJSON;
1563   handler_map_["ActivateTab"] =
1564       &TestingAutomationProvider::ActivateTabJSON;
1565   handler_map_["BringBrowserToFront"] =
1566       &TestingAutomationProvider::BringBrowserToFrontJSON;
1567   handler_map_["GetIndicesFromTab"] =
1568       &TestingAutomationProvider::GetIndicesFromTab;
1569   handler_map_["NavigateToURL"] =
1570       &TestingAutomationProvider::NavigateToURL;
1571   handler_map_["GetActiveTabIndex"] =
1572       &TestingAutomationProvider::GetActiveTabIndexJSON;
1573   handler_map_["AppendTab"] =
1574       &TestingAutomationProvider::AppendTabJSON;
1575   handler_map_["OpenNewBrowserWindow"] =
1576       &TestingAutomationProvider::OpenNewBrowserWindow;
1577   handler_map_["CloseBrowserWindow"] =
1578       &TestingAutomationProvider::CloseBrowserWindow;
1579   handler_map_["WaitUntilNavigationCompletes"] =
1580       &TestingAutomationProvider::WaitUntilNavigationCompletes;
1581   handler_map_["GetLocalStatePrefsInfo"] =
1582       &TestingAutomationProvider::GetLocalStatePrefsInfo;
1583   handler_map_["SetLocalStatePrefs"] =
1584       &TestingAutomationProvider::SetLocalStatePrefs;
1585   handler_map_["GetPrefsInfo"] = &TestingAutomationProvider::GetPrefsInfo;
1586   handler_map_["SetPrefs"] = &TestingAutomationProvider::SetPrefs;
1587   handler_map_["ExecuteJavascript"] =
1588       &TestingAutomationProvider::ExecuteJavascriptJSON;
1589   handler_map_["AddDomEventObserver"] =
1590       &TestingAutomationProvider::AddDomEventObserver;
1591   handler_map_["RemoveEventObserver"] =
1592       &TestingAutomationProvider::RemoveEventObserver;
1593   handler_map_["GetNextEvent"] =
1594       &TestingAutomationProvider::GetNextEvent;
1595   handler_map_["ClearEventQueue"] =
1596       &TestingAutomationProvider::ClearEventQueue;
1597   handler_map_["ExecuteJavascriptInRenderView"] =
1598       &TestingAutomationProvider::ExecuteJavascriptInRenderView;
1599   handler_map_["GoForward"] =
1600       &TestingAutomationProvider::GoForward;
1601   handler_map_["GoBack"] =
1602       &TestingAutomationProvider::GoBack;
1603   handler_map_["Reload"] =
1604       &TestingAutomationProvider::ReloadJSON;
1605   handler_map_["OpenFindInPage"] =
1606       &TestingAutomationProvider::OpenFindInPage;
1607   handler_map_["IsFindInPageVisible"] =
1608       &TestingAutomationProvider::IsFindInPageVisible;
1609   handler_map_["SetDownloadShelfVisible"] =
1610       &TestingAutomationProvider::SetDownloadShelfVisibleJSON;
1611   handler_map_["IsDownloadShelfVisible"] =
1612       &TestingAutomationProvider::IsDownloadShelfVisibleJSON;
1613   handler_map_["GetDownloadDirectory"] =
1614       &TestingAutomationProvider::GetDownloadDirectoryJSON;
1615   handler_map_["GetCookies"] =
1616       &TestingAutomationProvider::GetCookiesJSON;
1617   handler_map_["DeleteCookie"] =
1618       &TestingAutomationProvider::DeleteCookieJSON;
1619   handler_map_["SetCookie"] =
1620       &TestingAutomationProvider::SetCookieJSON;
1621   handler_map_["GetCookiesInBrowserContext"] =
1622       &TestingAutomationProvider::GetCookiesInBrowserContext;
1623   handler_map_["DeleteCookieInBrowserContext"] =
1624       &TestingAutomationProvider::DeleteCookieInBrowserContext;
1625   handler_map_["SetCookieInBrowserContext"] =
1626       &TestingAutomationProvider::SetCookieInBrowserContext;
1627
1628   handler_map_["WaitForBookmarkModelToLoad"] =
1629       &TestingAutomationProvider::WaitForBookmarkModelToLoadJSON;
1630   handler_map_["GetBookmarksAsJSON"] =
1631       &TestingAutomationProvider::GetBookmarksAsJSON;
1632   handler_map_["GetBookmarkBarStatus"] =
1633       &TestingAutomationProvider::GetBookmarkBarStatus;
1634   handler_map_["AddBookmark"] =
1635       &TestingAutomationProvider::AddBookmark;
1636   handler_map_["ReparentBookmark"] =
1637       &TestingAutomationProvider::ReparentBookmark;
1638   handler_map_["SetBookmarkTitle"] =
1639       &TestingAutomationProvider::SetBookmarkTitle;
1640   handler_map_["SetBookmarkURL"] =
1641       &TestingAutomationProvider::SetBookmarkURL;
1642   handler_map_["RemoveBookmark"] =
1643       &TestingAutomationProvider::RemoveBookmark;
1644
1645   handler_map_["GetTabIds"] =
1646       &TestingAutomationProvider::GetTabIds;
1647   handler_map_["IsTabIdValid"] =
1648       &TestingAutomationProvider::IsTabIdValid;
1649   handler_map_["CloseTab"] =
1650       &TestingAutomationProvider::CloseTabJSON;
1651   handler_map_["SetViewBounds"] =
1652       &TestingAutomationProvider::SetViewBounds;
1653   handler_map_["MaximizeView"] =
1654       &TestingAutomationProvider::MaximizeView;
1655   handler_map_["WebkitMouseMove"] =
1656       &TestingAutomationProvider::WebkitMouseMove;
1657   handler_map_["WebkitMouseClick"] =
1658       &TestingAutomationProvider::WebkitMouseClick;
1659   handler_map_["WebkitMouseDrag"] =
1660       &TestingAutomationProvider::WebkitMouseDrag;
1661   handler_map_["WebkitMouseButtonUp"] =
1662       &TestingAutomationProvider::WebkitMouseButtonUp;
1663   handler_map_["WebkitMouseButtonDown"] =
1664       &TestingAutomationProvider::WebkitMouseButtonDown;
1665   handler_map_["WebkitMouseDoubleClick"] =
1666       &TestingAutomationProvider::WebkitMouseDoubleClick;
1667   handler_map_["DragAndDropFilePaths"] =
1668       &TestingAutomationProvider::DragAndDropFilePaths;
1669   handler_map_["SendWebkitKeyEvent"] =
1670       &TestingAutomationProvider::SendWebkitKeyEvent;
1671   handler_map_["ActivateTab"] =
1672       &TestingAutomationProvider::ActivateTabJSON;
1673   handler_map_["GetAppModalDialogMessage"] =
1674       &TestingAutomationProvider::GetAppModalDialogMessage;
1675   handler_map_["AcceptOrDismissAppModalDialog"] =
1676       &TestingAutomationProvider::AcceptOrDismissAppModalDialog;
1677   handler_map_["ActionOnSSLBlockingPage"] =
1678       &TestingAutomationProvider::ActionOnSSLBlockingPage;
1679   handler_map_["GetSecurityState"] =
1680       &TestingAutomationProvider::GetSecurityState;
1681   handler_map_["IsPageActionVisible"] =
1682       &TestingAutomationProvider::IsPageActionVisible;
1683   handler_map_["CreateNewAutomationProvider"] =
1684       &TestingAutomationProvider::CreateNewAutomationProvider;
1685   handler_map_["GetBrowserWindowCount"] =
1686       &TestingAutomationProvider::GetBrowserWindowCountJSON;
1687   handler_map_["GetBrowserInfo"] =
1688       &TestingAutomationProvider::GetBrowserInfo;
1689   handler_map_["GetTabInfo"] =
1690       &TestingAutomationProvider::GetTabInfo;
1691   handler_map_["GetTabCount"] =
1692       &TestingAutomationProvider::GetTabCountJSON;
1693   handler_map_["OpenNewBrowserWindowWithNewProfile"] =
1694       &TestingAutomationProvider::OpenNewBrowserWindowWithNewProfile;
1695   handler_map_["GetMultiProfileInfo"] =
1696       &TestingAutomationProvider::GetMultiProfileInfo;
1697   handler_map_["OpenProfileWindow"] =
1698       &TestingAutomationProvider::OpenProfileWindow;
1699   handler_map_["GetProcessInfo"] =
1700       &TestingAutomationProvider::GetProcessInfo;
1701   handler_map_["RefreshPolicies"] =
1702       &TestingAutomationProvider::RefreshPolicies;
1703   handler_map_["InstallExtension"] =
1704       &TestingAutomationProvider::InstallExtension;
1705   handler_map_["GetExtensionsInfo"] =
1706       &TestingAutomationProvider::GetExtensionsInfo;
1707   handler_map_["UninstallExtensionById"] =
1708       &TestingAutomationProvider::UninstallExtensionById;
1709   handler_map_["SetExtensionStateById"] =
1710       &TestingAutomationProvider::SetExtensionStateById;
1711   handler_map_["TriggerPageActionById"] =
1712       &TestingAutomationProvider::TriggerPageActionById;
1713   handler_map_["TriggerBrowserActionById"] =
1714       &TestingAutomationProvider::TriggerBrowserActionById;
1715   handler_map_["UpdateExtensionsNow"] =
1716       &TestingAutomationProvider::UpdateExtensionsNow;
1717   handler_map_["OverrideGeoposition"] =
1718       &TestingAutomationProvider::OverrideGeoposition;
1719   handler_map_["SimulateAsanMemoryBug"] =
1720       &TestingAutomationProvider::SimulateAsanMemoryBug;
1721
1722 #if defined(OS_CHROMEOS)
1723   handler_map_["AcceptOOBENetworkScreen"] =
1724       &TestingAutomationProvider::AcceptOOBENetworkScreen;
1725   handler_map_["AcceptOOBEEula"] = &TestingAutomationProvider::AcceptOOBEEula;
1726   handler_map_["CancelOOBEUpdate"] =
1727       &TestingAutomationProvider::CancelOOBEUpdate;
1728   handler_map_["PickUserImage"] = &TestingAutomationProvider::PickUserImage;
1729   handler_map_["SkipToLogin"] = &TestingAutomationProvider::SkipToLogin;
1730   handler_map_["GetOOBEScreenInfo"] =
1731       &TestingAutomationProvider::GetOOBEScreenInfo;
1732
1733   handler_map_["GetLoginInfo"] = &TestingAutomationProvider::GetLoginInfo;
1734   handler_map_["ShowCreateAccountUI"] =
1735       &TestingAutomationProvider::ShowCreateAccountUI;
1736   handler_map_["ExecuteJavascriptInOOBEWebUI"] =
1737       &TestingAutomationProvider::ExecuteJavascriptInOOBEWebUI;
1738   handler_map_["LoginAsGuest"] = &TestingAutomationProvider::LoginAsGuest;
1739   handler_map_["SubmitLoginForm"] =
1740       &TestingAutomationProvider::SubmitLoginForm;
1741   handler_map_["AddLoginEventObserver"] =
1742       &TestingAutomationProvider::AddLoginEventObserver;
1743   handler_map_["SignOut"] = &TestingAutomationProvider::SignOut;
1744
1745   handler_map_["LockScreen"] = &TestingAutomationProvider::LockScreen;
1746   handler_map_["UnlockScreen"] = &TestingAutomationProvider::UnlockScreen;
1747   handler_map_["SignoutInScreenLocker"] =
1748       &TestingAutomationProvider::SignoutInScreenLocker;
1749
1750   handler_map_["GetBatteryInfo"] = &TestingAutomationProvider::GetBatteryInfo;
1751
1752   handler_map_["EnableSpokenFeedback"] =
1753       &TestingAutomationProvider::EnableSpokenFeedback;
1754   handler_map_["IsSpokenFeedbackEnabled"] =
1755       &TestingAutomationProvider::IsSpokenFeedbackEnabled;
1756
1757   handler_map_["GetTimeInfo"] = &TestingAutomationProvider::GetTimeInfo;
1758   handler_map_["SetTimezone"] = &TestingAutomationProvider::SetTimezone;
1759
1760   handler_map_["UpdateCheck"] = &TestingAutomationProvider::UpdateCheck;
1761
1762   handler_map_["GetVolumeInfo"] = &TestingAutomationProvider::GetVolumeInfo;
1763   handler_map_["SetVolume"] = &TestingAutomationProvider::SetVolume;
1764   handler_map_["SetMute"] = &TestingAutomationProvider::SetMute;
1765
1766   handler_map_["OpenCrosh"] = &TestingAutomationProvider::OpenCrosh;
1767
1768   browser_handler_map_["GetTimeInfo"] =
1769       &TestingAutomationProvider::GetTimeInfo;
1770 #endif  // defined(OS_CHROMEOS)
1771
1772   browser_handler_map_["DisablePlugin"] =
1773       &TestingAutomationProvider::DisablePlugin;
1774   browser_handler_map_["EnablePlugin"] =
1775       &TestingAutomationProvider::EnablePlugin;
1776   browser_handler_map_["GetPluginsInfo"] =
1777       &TestingAutomationProvider::GetPluginsInfo;
1778
1779   browser_handler_map_["GetNavigationInfo"] =
1780       &TestingAutomationProvider::GetNavigationInfo;
1781
1782   browser_handler_map_["PerformActionOnInfobar"] =
1783       &TestingAutomationProvider::PerformActionOnInfobar;
1784
1785   browser_handler_map_["GetHistoryInfo"] =
1786       &TestingAutomationProvider::GetHistoryInfo;
1787
1788   browser_handler_map_["GetOmniboxInfo"] =
1789       &TestingAutomationProvider::GetOmniboxInfo;
1790   browser_handler_map_["SetOmniboxText"] =
1791       &TestingAutomationProvider::SetOmniboxText;
1792   browser_handler_map_["OmniboxAcceptInput"] =
1793       &TestingAutomationProvider::OmniboxAcceptInput;
1794   browser_handler_map_["OmniboxMovePopupSelection"] =
1795       &TestingAutomationProvider::OmniboxMovePopupSelection;
1796
1797   browser_handler_map_["LoadSearchEngineInfo"] =
1798       &TestingAutomationProvider::LoadSearchEngineInfo;
1799   browser_handler_map_["GetSearchEngineInfo"] =
1800       &TestingAutomationProvider::GetSearchEngineInfo;
1801   browser_handler_map_["AddOrEditSearchEngine"] =
1802       &TestingAutomationProvider::AddOrEditSearchEngine;
1803   browser_handler_map_["PerformActionOnSearchEngine"] =
1804       &TestingAutomationProvider::PerformActionOnSearchEngine;
1805
1806   browser_handler_map_["SetWindowDimensions"] =
1807       &TestingAutomationProvider::SetWindowDimensions;
1808
1809   browser_handler_map_["GetDownloadsInfo"] =
1810       &TestingAutomationProvider::GetDownloadsInfo;
1811   browser_handler_map_["WaitForAllDownloadsToComplete"] =
1812       &TestingAutomationProvider::WaitForAllDownloadsToComplete;
1813   browser_handler_map_["PerformActionOnDownload"] =
1814       &TestingAutomationProvider::PerformActionOnDownload;
1815
1816   browser_handler_map_["GetInitialLoadTimes"] =
1817       &TestingAutomationProvider::GetInitialLoadTimes;
1818
1819   browser_handler_map_["SaveTabContents"] =
1820       &TestingAutomationProvider::SaveTabContents;
1821
1822   browser_handler_map_["AddSavedPassword"] =
1823       &TestingAutomationProvider::AddSavedPassword;
1824   browser_handler_map_["RemoveSavedPassword"] =
1825       &TestingAutomationProvider::RemoveSavedPassword;
1826   browser_handler_map_["GetSavedPasswords"] =
1827       &TestingAutomationProvider::GetSavedPasswords;
1828
1829   browser_handler_map_["FindInPage"] = &TestingAutomationProvider::FindInPage;
1830
1831   browser_handler_map_["GetNTPInfo"] =
1832       &TestingAutomationProvider::GetNTPInfo;
1833   browser_handler_map_["RemoveNTPMostVisitedThumbnail"] =
1834       &TestingAutomationProvider::RemoveNTPMostVisitedThumbnail;
1835   browser_handler_map_["RestoreAllNTPMostVisitedThumbnails"] =
1836       &TestingAutomationProvider::RestoreAllNTPMostVisitedThumbnails;
1837
1838   browser_handler_map_["KillRendererProcess"] =
1839       &TestingAutomationProvider::KillRendererProcess;
1840
1841   browser_handler_map_["LaunchApp"] = &TestingAutomationProvider::LaunchApp;
1842   browser_handler_map_["SetAppLaunchType"] =
1843       &TestingAutomationProvider::SetAppLaunchType;
1844
1845   browser_handler_map_["GetV8HeapStats"] =
1846       &TestingAutomationProvider::GetV8HeapStats;
1847   browser_handler_map_["GetFPS"] =
1848       &TestingAutomationProvider::GetFPS;
1849
1850   browser_handler_map_["IsFullscreenForBrowser"] =
1851       &TestingAutomationProvider::IsFullscreenForBrowser;
1852   browser_handler_map_["IsFullscreenForTab"] =
1853       &TestingAutomationProvider::IsFullscreenForTab;
1854   browser_handler_map_["IsMouseLocked"] =
1855       &TestingAutomationProvider::IsMouseLocked;
1856   browser_handler_map_["IsMouseLockPermissionRequested"] =
1857       &TestingAutomationProvider::IsMouseLockPermissionRequested;
1858   browser_handler_map_["IsFullscreenPermissionRequested"] =
1859       &TestingAutomationProvider::IsFullscreenPermissionRequested;
1860   browser_handler_map_["IsFullscreenBubbleDisplayed"] =
1861       &TestingAutomationProvider::IsFullscreenBubbleDisplayed;
1862   browser_handler_map_["IsFullscreenBubbleDisplayingButtons"] =
1863       &TestingAutomationProvider::IsFullscreenBubbleDisplayingButtons;
1864   browser_handler_map_["AcceptCurrentFullscreenOrMouseLockRequest"] =
1865       &TestingAutomationProvider::AcceptCurrentFullscreenOrMouseLockRequest;
1866   browser_handler_map_["DenyCurrentFullscreenOrMouseLockRequest"] =
1867       &TestingAutomationProvider::DenyCurrentFullscreenOrMouseLockRequest;
1868 }
1869
1870 scoped_ptr<base::DictionaryValue>
1871 TestingAutomationProvider::ParseJSONRequestCommand(
1872     const std::string& json_request,
1873     std::string* command,
1874     std::string* error) {
1875   scoped_ptr<base::DictionaryValue> dict_value;
1876   scoped_ptr<base::Value> values(
1877       base::JSONReader::ReadAndReturnError(json_request,
1878           base::JSON_ALLOW_TRAILING_COMMAS, NULL, error));
1879   if (values.get()) {
1880     // Make sure input is a dict with a string command.
1881     if (values->GetType() != base::Value::TYPE_DICTIONARY) {
1882       *error = "Command dictionary is not a dictionary.";
1883     } else {
1884       dict_value.reset(static_cast<base::DictionaryValue*>(values.release()));
1885       if (!dict_value->GetStringASCII("command", command)) {
1886         *error = "Command key string missing from dictionary.";
1887         dict_value.reset(NULL);
1888       }
1889     }
1890   }
1891   return dict_value.Pass();
1892 }
1893
1894 void TestingAutomationProvider::SendJSONRequestWithBrowserHandle(
1895     int handle,
1896     const std::string& json_request,
1897     IPC::Message* reply_message) {
1898   Browser* browser = NULL;
1899   if (browser_tracker_->ContainsHandle(handle))
1900     browser = browser_tracker_->GetResource(handle);
1901   if (browser || handle < 0) {
1902     SendJSONRequest(browser, json_request, reply_message);
1903   } else {
1904     AutomationJSONReply(this, reply_message).SendError(
1905         "The browser window does not exist.");
1906   }
1907 }
1908
1909 void TestingAutomationProvider::SendJSONRequestWithBrowserIndex(
1910     int index,
1911     const std::string& json_request,
1912     IPC::Message* reply_message) {
1913   Browser* browser = index < 0 ? NULL : automation_util::GetBrowserAt(index);
1914   if (!browser && index >= 0) {
1915     AutomationJSONReply(this, reply_message).SendError(base::StringPrintf(
1916         "Browser window with index=%d does not exist.", index));
1917   } else {
1918     SendJSONRequest(browser, json_request, reply_message);
1919   }
1920 }
1921
1922 void TestingAutomationProvider::SendJSONRequest(Browser* browser,
1923                                                 const std::string& json_request,
1924                                                 IPC::Message* reply_message) {
1925   std::string command, error_string;
1926   scoped_ptr<base::DictionaryValue> dict_value(
1927       ParseJSONRequestCommand(json_request, &command, &error_string));
1928   if (!dict_value.get() || command.empty()) {
1929     AutomationJSONReply(this, reply_message).SendError(error_string);
1930     return;
1931   }
1932
1933   if (handler_map_.empty() || browser_handler_map_.empty())
1934     BuildJSONHandlerMaps();
1935
1936   // Look for command in handlers that take a Browser.
1937   if (browser_handler_map_.find(std::string(command)) !=
1938       browser_handler_map_.end() && browser) {
1939     (this->*browser_handler_map_[command])(browser, dict_value.get(),
1940                                            reply_message);
1941   // Look for command in handlers that don't take a Browser.
1942   } else if (handler_map_.find(std::string(command)) != handler_map_.end()) {
1943     (this->*handler_map_[command])(dict_value.get(), reply_message);
1944   // Command has no handler.
1945   } else {
1946     error_string = base::StringPrintf("Unknown command '%s'. Options: ",
1947                                       command.c_str());
1948     for (std::map<std::string, JsonHandler>::const_iterator it =
1949          handler_map_.begin(); it != handler_map_.end(); ++it) {
1950       error_string += it->first + ", ";
1951     }
1952     for (std::map<std::string, BrowserJsonHandler>::const_iterator it =
1953          browser_handler_map_.begin(); it != browser_handler_map_.end(); ++it) {
1954       error_string += it->first + ", ";
1955     }
1956     AutomationJSONReply(this, reply_message).SendError(error_string);
1957   }
1958 }
1959
1960 void TestingAutomationProvider::BringBrowserToFrontJSON(
1961     base::DictionaryValue* args,
1962     IPC::Message* reply_message) {
1963   AutomationJSONReply reply(this, reply_message);
1964   Browser* browser;
1965   std::string error_msg;
1966   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
1967     reply.SendError(error_msg);
1968     return;
1969   }
1970   browser->window()->Activate();
1971   reply.SendSuccess(NULL);
1972 }
1973
1974 // Sample json input: { "command": "SetWindowDimensions",
1975 //                      "x": 20,         # optional
1976 //                      "y": 20,         # optional
1977 //                      "width": 800,    # optional
1978 //                      "height": 600 }  # optional
1979 void TestingAutomationProvider::SetWindowDimensions(
1980     Browser* browser,
1981     base::DictionaryValue* args,
1982     IPC::Message* reply_message) {
1983   gfx::Rect rect = browser->window()->GetRestoredBounds();
1984   int x, y, width, height;
1985   if (args->GetInteger("x", &x))
1986     rect.set_x(x);
1987   if (args->GetInteger("y", &y))
1988     rect.set_y(y);
1989   if (args->GetInteger("width", &width))
1990     rect.set_width(width);
1991   if (args->GetInteger("height", &height))
1992     rect.set_height(height);
1993   browser->window()->SetBounds(rect);
1994   AutomationJSONReply(this, reply_message).SendSuccess(NULL);
1995 }
1996
1997 base::ListValue* TestingAutomationProvider::GetInfobarsInfo(WebContents* wc) {
1998   // Each infobar may have different properties depending on the type.
1999   base::ListValue* infobars = new base::ListValue;
2000   InfoBarService* infobar_service = InfoBarService::FromWebContents(wc);
2001   for (size_t i = 0; i < infobar_service->infobar_count(); ++i) {
2002     base::DictionaryValue* infobar_item = new base::DictionaryValue;
2003     InfoBarDelegate* infobar = infobar_service->infobar_at(i)->delegate();
2004     switch (infobar->GetInfoBarAutomationType()) {
2005       case InfoBarDelegate::CONFIRM_INFOBAR:
2006         infobar_item->SetString("type", "confirm_infobar");
2007         break;
2008       case InfoBarDelegate::PASSWORD_INFOBAR:
2009         infobar_item->SetString("type", "password_infobar");
2010         break;
2011       case InfoBarDelegate::RPH_INFOBAR:
2012         infobar_item->SetString("type", "rph_infobar");
2013         break;
2014       case InfoBarDelegate::UNKNOWN_INFOBAR:
2015         infobar_item->SetString("type", "unknown_infobar");
2016         break;
2017     }
2018     if (infobar->AsConfirmInfoBarDelegate()) {
2019       // Also covers ThemeInstalledInfoBarDelegate.
2020       ConfirmInfoBarDelegate* confirm_infobar =
2021         infobar->AsConfirmInfoBarDelegate();
2022       infobar_item->SetString("text", confirm_infobar->GetMessageText());
2023       infobar_item->SetString("link_text", confirm_infobar->GetLinkText());
2024       base::ListValue* buttons_list = new base::ListValue;
2025       int buttons = confirm_infobar->GetButtons();
2026       if (buttons & ConfirmInfoBarDelegate::BUTTON_OK) {
2027         base::StringValue* button_label = new base::StringValue(
2028             confirm_infobar->GetButtonLabel(
2029               ConfirmInfoBarDelegate::BUTTON_OK));
2030         buttons_list->Append(button_label);
2031       }
2032       if (buttons & ConfirmInfoBarDelegate::BUTTON_CANCEL) {
2033         base::StringValue* button_label = new base::StringValue(
2034             confirm_infobar->GetButtonLabel(
2035               ConfirmInfoBarDelegate::BUTTON_CANCEL));
2036         buttons_list->Append(button_label);
2037       }
2038       infobar_item->Set("buttons", buttons_list);
2039     } else if (infobar->AsExtensionInfoBarDelegate()) {
2040       infobar_item->SetString("type", "extension_infobar");
2041     } else {
2042       infobar_item->SetString("type", "unknown_infobar");
2043     }
2044     infobars->Append(infobar_item);
2045   }
2046   return infobars;
2047 }
2048
2049 // Sample json input: { "command": "PerformActionOnInfobar",
2050 //                      "action": "dismiss",
2051 //                      "infobar_index": 0,
2052 //                      "tab_index": 0 }
2053 // Sample output: {}
2054 void TestingAutomationProvider::PerformActionOnInfobar(
2055     Browser* browser,
2056     base::DictionaryValue* args,
2057     IPC::Message* reply_message) {
2058   AutomationJSONReply reply(this, reply_message);
2059   int tab_index;
2060   int infobar_index_int;
2061   std::string action;
2062   if (!args->GetInteger("tab_index", &tab_index) ||
2063       !args->GetInteger("infobar_index", &infobar_index_int) ||
2064       !args->GetString("action", &action)) {
2065     reply.SendError("Invalid or missing args");
2066     return;
2067   }
2068   size_t infobar_index = static_cast<size_t>(infobar_index_int);
2069
2070   WebContents* web_contents =
2071       browser->tab_strip_model()->GetWebContentsAt(tab_index);
2072   if (!web_contents) {
2073     reply.SendError(base::StringPrintf("No such tab at index %d", tab_index));
2074     return;
2075   }
2076
2077   InfoBarService* infobar_service =
2078       InfoBarService::FromWebContents(web_contents);
2079   if (infobar_index >= infobar_service->infobar_count()) {
2080     reply.SendError(base::StringPrintf("No such infobar at index %" PRIuS,
2081                                        infobar_index));
2082     return;
2083   }
2084   InfoBar* infobar = infobar_service->infobar_at(infobar_index);
2085   InfoBarDelegate* infobar_delegate = infobar->delegate();
2086
2087   if (action == "dismiss") {
2088     infobar_delegate->InfoBarDismissed();
2089     infobar_service->RemoveInfoBar(infobar);
2090     reply.SendSuccess(NULL);
2091     return;
2092   }
2093   if ((action == "accept") || (action == "cancel")) {
2094     ConfirmInfoBarDelegate* confirm_infobar_delegate =
2095         infobar_delegate->AsConfirmInfoBarDelegate();
2096     if (!confirm_infobar_delegate) {
2097       reply.SendError("Not a confirm infobar");
2098       return;
2099     }
2100     if ((action == "accept") ?
2101         confirm_infobar_delegate->Accept() : confirm_infobar_delegate->Cancel())
2102       infobar_service->RemoveInfoBar(infobar);
2103     reply.SendSuccess(NULL);
2104     return;
2105   }
2106
2107   reply.SendError("Invalid action");
2108 }
2109
2110 namespace {
2111
2112 // Gets info about BrowserChildProcessHost. Must run on IO thread to
2113 // honor the semantics of BrowserChildProcessHostIterator.
2114 // Used by AutomationProvider::GetBrowserInfo().
2115 void GetChildProcessHostInfo(base::ListValue* child_processes) {
2116   for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
2117     // Only add processes which are already started, since we need their handle.
2118     if (iter.GetData().handle == base::kNullProcessHandle)
2119       continue;
2120     base::DictionaryValue* item = new base::DictionaryValue;
2121     item->SetString("name", iter.GetData().name);
2122     item->SetString(
2123         "type",
2124         content::GetProcessTypeNameInEnglish(iter.GetData().process_type));
2125     item->SetInteger("pid", base::GetProcId(iter.GetData().handle));
2126     child_processes->Append(item);
2127   }
2128 }
2129
2130 }  // namespace
2131
2132 // Sample json input: { "command": "GetBrowserInfo" }
2133 // Refer to GetBrowserInfo() in chrome/test/pyautolib/pyauto.py for
2134 // sample json output.
2135 void TestingAutomationProvider::GetBrowserInfo(
2136     base::DictionaryValue* args,
2137     IPC::Message* reply_message) {
2138   base::ThreadRestrictions::ScopedAllowIO allow_io;  // needed for PathService
2139   base::DictionaryValue* properties = new base::DictionaryValue;
2140   properties->SetString("ChromeVersion", chrome::kChromeVersion);
2141   properties->SetString("BrowserProcessExecutableName",
2142                         chrome::kBrowserProcessExecutableName);
2143   properties->SetString("HelperProcessExecutableName",
2144                         chrome::kHelperProcessExecutableName);
2145   properties->SetString("BrowserProcessExecutablePath",
2146                         chrome::kBrowserProcessExecutablePath);
2147   properties->SetString("HelperProcessExecutablePath",
2148                         chrome::kHelperProcessExecutablePath);
2149   properties->SetString("command_line_string",
2150       CommandLine::ForCurrentProcess()->GetCommandLineString());
2151   base::FilePath dumps_path;
2152   PathService::Get(chrome::DIR_CRASH_DUMPS, &dumps_path);
2153   properties->SetString("DIR_CRASH_DUMPS", dumps_path.value());
2154 #if defined(USE_AURA)
2155   properties->SetBoolean("aura", true);
2156 #else
2157   properties->SetBoolean("aura", false);
2158 #endif
2159
2160   std::string branding;
2161 #if defined(GOOGLE_CHROME_BUILD)
2162   branding = "Google Chrome";
2163 #elif defined(CHROMIUM_BUILD)
2164   branding = "Chromium";
2165 #else
2166   branding = "Unknown Branding";
2167 #endif
2168   properties->SetString("branding", branding);
2169
2170   bool is_official_build = false;
2171 #if defined(OFFICIAL_BUILD)
2172   is_official_build = true;
2173 #endif
2174   properties->SetBoolean("is_official", is_official_build);
2175
2176   scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
2177   return_value->Set("properties", properties);
2178
2179   return_value->SetInteger("browser_pid", base::GetCurrentProcId());
2180   // Add info about all windows in a list of dictionaries, one dictionary
2181   // item per window.
2182   base::ListValue* windows = new base::ListValue;
2183   int windex = 0;
2184
2185   for (chrome::BrowserIterator it; !it.done(); it.Next(), ++windex) {
2186     base::DictionaryValue* browser_item = new base::DictionaryValue;
2187     Browser* browser = *it;
2188     browser_item->SetInteger("index", windex);
2189     // Window properties
2190     gfx::Rect rect = browser->window()->GetRestoredBounds();
2191     browser_item->SetInteger("x", rect.x());
2192     browser_item->SetInteger("y", rect.y());
2193     browser_item->SetInteger("width", rect.width());
2194     browser_item->SetInteger("height", rect.height());
2195     browser_item->SetBoolean("fullscreen",
2196                              browser->window()->IsFullscreen());
2197     base::ListValue* visible_page_actions = new base::ListValue;
2198     // Add info about all visible page actions. Skipped on panels, which do not
2199     // have a location bar.
2200     LocationBar* loc_bar = browser->window()->GetLocationBar();
2201     if (loc_bar) {
2202       LocationBarTesting* loc_bar_test =
2203           loc_bar->GetLocationBarForTesting();
2204       size_t page_action_visible_count =
2205           static_cast<size_t>(loc_bar_test->PageActionVisibleCount());
2206       for (size_t i = 0; i < page_action_visible_count; ++i) {
2207         base::StringValue* extension_id = new base::StringValue(
2208             loc_bar_test->GetVisiblePageAction(i)->extension_id());
2209         visible_page_actions->Append(extension_id);
2210       }
2211     }
2212     browser_item->Set("visible_page_actions", visible_page_actions);
2213     browser_item->SetInteger("selected_tab",
2214                              browser->tab_strip_model()->active_index());
2215     browser_item->SetBoolean("incognito",
2216                              browser->profile()->IsOffTheRecord());
2217     browser_item->SetString("profile_path",
2218         browser->profile()->GetPath().BaseName().MaybeAsASCII());
2219     std::string type;
2220     switch (browser->type()) {
2221       case Browser::TYPE_TABBED:
2222         type = "tabbed";
2223         break;
2224       case Browser::TYPE_POPUP:
2225         type = "popup";
2226         break;
2227       default:
2228         type = "unknown";
2229         break;
2230     }
2231     browser_item->SetString("type", type);
2232     // For each window, add info about all tabs in a list of dictionaries,
2233     // one dictionary item per tab.
2234     base::ListValue* tabs = new base::ListValue;
2235     for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
2236       WebContents* wc = browser->tab_strip_model()->GetWebContentsAt(i);
2237       base::DictionaryValue* tab = new base::DictionaryValue;
2238       tab->SetInteger("index", i);
2239       tab->SetString("url", wc->GetURL().spec());
2240       tab->SetInteger("renderer_pid",
2241                       base::GetProcId(wc->GetRenderProcessHost()->GetHandle()));
2242       tab->Set("infobars", GetInfobarsInfo(wc));
2243       tab->SetBoolean("pinned", browser->tab_strip_model()->IsTabPinned(i));
2244       tabs->Append(tab);
2245     }
2246     browser_item->Set("tabs", tabs);
2247
2248     windows->Append(browser_item);
2249   }
2250   return_value->Set("windows", windows);
2251
2252 #if defined(OS_LINUX)
2253   int flags = ChildProcessHost::CHILD_ALLOW_SELF;
2254 #else
2255   int flags = ChildProcessHost::CHILD_NORMAL;
2256 #endif
2257
2258   // Add all extension processes in a list of dictionaries, one dictionary
2259   // item per extension process.
2260   base::ListValue* extension_views = new base::ListValue;
2261   ProfileManager* profile_manager = g_browser_process->profile_manager();
2262   std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
2263   for (size_t i = 0; i < profiles.size(); ++i) {
2264     extensions::ProcessManager* process_manager =
2265         extensions::ExtensionSystem::Get(profiles[i])->process_manager();
2266     if (!process_manager)
2267       continue;
2268     const extensions::ProcessManager::ViewSet view_set =
2269         process_manager->GetAllViews();
2270     for (extensions::ProcessManager::ViewSet::const_iterator jt =
2271              view_set.begin();
2272          jt != view_set.end(); ++jt) {
2273       content::RenderViewHost* render_view_host = *jt;
2274       // Don't add dead extension processes.
2275       if (!render_view_host->IsRenderViewLive())
2276         continue;
2277       // Don't add views for which we can't obtain an extension.
2278       // TODO(benwells): work out why this happens. It only happens for one
2279       // test, and only on the bots.
2280       const Extension* extension =
2281           process_manager->GetExtensionForRenderViewHost(render_view_host);
2282       if (!extension)
2283         continue;
2284       base::DictionaryValue* item = new base::DictionaryValue;
2285       item->SetString("name", extension->name());
2286       item->SetString("extension_id", extension->id());
2287       item->SetInteger(
2288           "pid",
2289           base::GetProcId(render_view_host->GetProcess()->GetHandle()));
2290       base::DictionaryValue* view = new base::DictionaryValue;
2291       view->SetInteger(
2292           "render_process_id",
2293           render_view_host->GetProcess()->GetID());
2294       view->SetInteger(
2295           "render_view_id",
2296           render_view_host->GetRoutingID());
2297       item->Set("view", view);
2298       std::string type;
2299       WebContents* web_contents =
2300           WebContents::FromRenderViewHost(render_view_host);
2301       extensions::ViewType view_type = extensions::GetViewType(web_contents);
2302       switch (view_type) {
2303         case extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE:
2304           type = "EXTENSION_BACKGROUND_PAGE";
2305           break;
2306         case extensions::VIEW_TYPE_EXTENSION_POPUP:
2307           type = "EXTENSION_POPUP";
2308           break;
2309         case extensions::VIEW_TYPE_EXTENSION_INFOBAR:
2310           type = "EXTENSION_INFOBAR";
2311           break;
2312         case extensions::VIEW_TYPE_EXTENSION_DIALOG:
2313           type = "EXTENSION_DIALOG";
2314           break;
2315         case extensions::VIEW_TYPE_APP_SHELL:
2316           type = "APP_SHELL";
2317           break;
2318         case extensions::VIEW_TYPE_PANEL:
2319           type = "PANEL";
2320           break;
2321         default:
2322           type = "unknown";
2323           break;
2324       }
2325       item->SetString("view_type", type);
2326       item->SetString("url", web_contents->GetURL().spec());
2327       item->SetBoolean("loaded", !render_view_host->IsLoading());
2328       extension_views->Append(item);
2329     }
2330   }
2331   return_value->Set("extension_views", extension_views);
2332
2333   return_value->SetString("child_process_path",
2334                           ChildProcessHost::GetChildPath(flags).value());
2335   // Child processes are the processes for plugins and other workers.
2336   // Add all child processes in a list of dictionaries, one dictionary item
2337   // per child process.
2338   base::ListValue* child_processes = new base::ListValue;
2339   return_value->Set("child_processes", child_processes);
2340   BrowserThread::PostTaskAndReply(
2341       BrowserThread::IO, FROM_HERE,
2342       base::Bind(&GetChildProcessHostInfo, child_processes),
2343       base::Bind(&AutomationJSONReply::SendSuccess,
2344                  base::Owned(new AutomationJSONReply(this, reply_message)),
2345                  base::Owned(return_value.release())));
2346 }
2347
2348 // Sample json input: { "command": "GetProcessInfo" }
2349 // Refer to GetProcessInfo() in chrome/test/pyautolib/pyauto.py for
2350 // sample json output.
2351 void TestingAutomationProvider::GetProcessInfo(
2352     base::DictionaryValue* args,
2353     IPC::Message* reply_message) {
2354   scoped_refptr<ProcessInfoObserver>
2355       proc_observer(new ProcessInfoObserver(this, reply_message));
2356   // TODO(jamescook): Maybe this shouldn't update UMA stats?
2357   proc_observer->StartFetch(MemoryDetails::UPDATE_USER_METRICS);
2358 }
2359
2360 // Sample json input: { "command": "GetNavigationInfo" }
2361 // Refer to GetNavigationInfo() in chrome/test/pyautolib/pyauto.py for
2362 // sample json output.
2363 void TestingAutomationProvider::GetNavigationInfo(
2364     Browser* browser,
2365     base::DictionaryValue* args,
2366     IPC::Message* reply_message) {
2367   AutomationJSONReply reply(this, reply_message);
2368   int tab_index;
2369   WebContents* web_contents = NULL;
2370   if (!args->GetInteger("tab_index", &tab_index) ||
2371       !(web_contents =
2372             browser->tab_strip_model()->GetWebContentsAt(tab_index))) {
2373     reply.SendError("tab_index missing or invalid.");
2374     return;
2375   }
2376   scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
2377   const NavigationController& controller = web_contents->GetController();
2378   NavigationEntry* nav_entry = controller.GetActiveEntry();
2379   DCHECK(nav_entry);
2380
2381   // Security info.
2382   base::DictionaryValue* ssl = new base::DictionaryValue;
2383   std::map<content::SecurityStyle, std::string> style_to_string;
2384   style_to_string[content::SECURITY_STYLE_UNKNOWN] = "SECURITY_STYLE_UNKNOWN";
2385   style_to_string[content::SECURITY_STYLE_UNAUTHENTICATED] =
2386       "SECURITY_STYLE_UNAUTHENTICATED";
2387   style_to_string[content::SECURITY_STYLE_AUTHENTICATION_BROKEN] =
2388       "SECURITY_STYLE_AUTHENTICATION_BROKEN";
2389   style_to_string[content::SECURITY_STYLE_AUTHENTICATED] =
2390       "SECURITY_STYLE_AUTHENTICATED";
2391
2392   SSLStatus ssl_status = nav_entry->GetSSL();
2393   ssl->SetString("security_style",
2394                  style_to_string[ssl_status.security_style]);
2395   ssl->SetBoolean("ran_insecure_content",
2396       !!(ssl_status.content_status & SSLStatus::RAN_INSECURE_CONTENT));
2397   ssl->SetBoolean("displayed_insecure_content",
2398       !!(ssl_status.content_status & SSLStatus::DISPLAYED_INSECURE_CONTENT));
2399   return_value->Set("ssl", ssl);
2400
2401   // Page type.
2402   std::map<content::PageType, std::string> pagetype_to_string;
2403   pagetype_to_string[content::PAGE_TYPE_NORMAL] = "NORMAL_PAGE";
2404   pagetype_to_string[content::PAGE_TYPE_ERROR] = "ERROR_PAGE";
2405   pagetype_to_string[content::PAGE_TYPE_INTERSTITIAL] =
2406       "INTERSTITIAL_PAGE";
2407   return_value->SetString("page_type",
2408                           pagetype_to_string[nav_entry->GetPageType()]);
2409
2410   return_value->SetString("favicon_url", nav_entry->GetFavicon().url.spec());
2411   reply.SendSuccess(return_value.get());
2412 }
2413
2414 // Sample json input: { "command": "GetHistoryInfo",
2415 //                      "search_text": "some text" }
2416 // Refer chrome/test/pyautolib/history_info.py for sample json output.
2417 void TestingAutomationProvider::GetHistoryInfo(Browser* browser,
2418                                                base::DictionaryValue* args,
2419                                                IPC::Message* reply_message) {
2420   consumer_.CancelAllRequests();
2421
2422   base::string16 search_text;
2423   args->GetString("search_text", &search_text);
2424
2425   // Fetch history.
2426   HistoryService* hs = HistoryServiceFactory::GetForProfile(
2427       browser->profile(), Profile::EXPLICIT_ACCESS);
2428   history::QueryOptions options;
2429   // The observer owns itself.  It deletes itself after it fetches history.
2430   AutomationProviderHistoryObserver* history_observer =
2431       new AutomationProviderHistoryObserver(this, reply_message);
2432   hs->QueryHistory(
2433       search_text,
2434       options,
2435       &consumer_,
2436       base::Bind(&AutomationProviderHistoryObserver::HistoryQueryComplete,
2437                  base::Unretained(history_observer)));
2438 }
2439
2440 // Sample json input: { "command": "GetDownloadsInfo" }
2441 // Refer chrome/test/pyautolib/download_info.py for sample json output.
2442 void TestingAutomationProvider::GetDownloadsInfo(Browser* browser,
2443                                                  base::DictionaryValue* args,
2444                                                  IPC::Message* reply_message) {
2445   AutomationJSONReply reply(this, reply_message);
2446   scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
2447   base::ListValue* list_of_downloads = new base::ListValue;
2448
2449   DownloadService* download_service(
2450       DownloadServiceFactory::GetForBrowserContext(browser->profile()));
2451
2452   if (download_service->HasCreatedDownloadManager()) {
2453     std::vector<DownloadItem*> downloads;
2454     BrowserContext::GetDownloadManager(browser->profile())->GetAllDownloads(
2455         &downloads);
2456
2457     for (std::vector<DownloadItem*>::iterator it = downloads.begin();
2458          it != downloads.end();
2459          it++) {  // Fill info about each download item.
2460       list_of_downloads->Append(GetDictionaryFromDownloadItem(
2461           *it, browser->profile()->IsOffTheRecord()));
2462     }
2463   }
2464   return_value->Set("downloads", list_of_downloads);
2465   reply.SendSuccess(return_value.get());
2466 }
2467
2468 void TestingAutomationProvider::WaitForAllDownloadsToComplete(
2469     Browser* browser,
2470     base::DictionaryValue* args,
2471     IPC::Message* reply_message) {
2472   base::ListValue* pre_download_ids = NULL;
2473
2474   if (!args->GetList("pre_download_ids", &pre_download_ids)) {
2475     AutomationJSONReply(this, reply_message)
2476         .SendError(
2477             base::StringPrintf("List of IDs of previous downloads required."));
2478     return;
2479   }
2480
2481   DownloadService* download_service =
2482       DownloadServiceFactory::GetForBrowserContext(browser->profile());
2483   if (!download_service->HasCreatedDownloadManager()) {
2484     // No download manager, so no downloads to wait for.
2485     AutomationJSONReply(this, reply_message).SendSuccess(NULL);
2486     return;
2487   }
2488
2489   // This observer will delete itself.
2490   new AllDownloadsCompleteObserver(
2491       this, reply_message,
2492       BrowserContext::GetDownloadManager(browser->profile()),
2493       pre_download_ids);
2494 }
2495
2496 // See PerformActionOnDownload() in chrome/test/pyautolib/pyauto.py for sample
2497 // json input and output.
2498 void TestingAutomationProvider::PerformActionOnDownload(
2499     Browser* browser,
2500     base::DictionaryValue* args,
2501     IPC::Message* reply_message) {
2502   int id;
2503   std::string action;
2504
2505   DownloadService* download_service =
2506       DownloadServiceFactory::GetForBrowserContext(browser->profile());
2507   if (!download_service->HasCreatedDownloadManager()) {
2508     AutomationJSONReply(this, reply_message).SendError("No download manager.");
2509     return;
2510   }
2511   if (!args->GetInteger("id", &id) || !args->GetString("action", &action)) {
2512     AutomationJSONReply(this, reply_message)
2513         .SendError("Must include int id and string action.");
2514     return;
2515   }
2516
2517   DownloadManager* download_manager =
2518       BrowserContext::GetDownloadManager(browser->profile());
2519   DownloadItem* selected_item = download_manager->GetDownload(id);
2520   if (!selected_item) {
2521     AutomationJSONReply(this, reply_message)
2522         .SendError(base::StringPrintf("No download with an id of %d\n", id));
2523     return;
2524   }
2525
2526   DownloadItem::DownloadState download_state = selected_item->GetState();
2527
2528   // We need to be IN_PROGRESS for these actions.
2529   if ((action == "pause" || action == "resume" || action == "cancel") &&
2530       download_state != DownloadItem::IN_PROGRESS) {
2531     AutomationJSONReply(this, reply_message)
2532         .SendError(base::StringPrintf(
2533             "Action '%s' called on download that is not in progress.",
2534             action.c_str()));
2535     return;
2536   }
2537
2538   if (action == "open") {
2539     selected_item->AddObserver(
2540         new AutomationProviderDownloadUpdatedObserver(
2541             this, reply_message, true, browser->profile()->IsOffTheRecord()));
2542     selected_item->OpenDownload();
2543   } else if (action == "toggle_open_files_like_this") {
2544     DownloadPrefs* prefs =
2545         DownloadPrefs::FromBrowserContext(selected_item->GetBrowserContext());
2546     base::FilePath path = selected_item->GetTargetFilePath();
2547     if (!selected_item->ShouldOpenFileBasedOnExtension())
2548       prefs->EnableAutoOpenBasedOnExtension(path);
2549     else
2550       prefs->DisableAutoOpenBasedOnExtension(path);
2551     AutomationJSONReply(this, reply_message).SendSuccess(NULL);
2552   } else if (action == "remove") {
2553     new AutomationProviderDownloadModelChangedObserver(
2554         this, reply_message, download_manager);
2555     selected_item->Remove();
2556   } else if (action == "decline_dangerous_download") {
2557     new AutomationProviderDownloadModelChangedObserver(
2558         this, reply_message, download_manager);
2559     selected_item->Remove();
2560   } else if (action == "save_dangerous_download") {
2561     selected_item->AddObserver(new AutomationProviderDownloadUpdatedObserver(
2562         this, reply_message, false, browser->profile()->IsOffTheRecord()));
2563     selected_item->ValidateDangerousDownload();
2564   } else if (action == "pause") {
2565     if (selected_item->IsPaused()) {
2566       // Action would be a no-op; respond right from here.  No-op implies
2567       // the test is poorly written or failing, so make it an error return.
2568       AutomationJSONReply(this, reply_message)
2569           .SendError("Action 'pause' called on already paused download.");
2570     } else {
2571       selected_item->AddObserver(new AutomationProviderDownloadUpdatedObserver(
2572           this, reply_message, false, browser->profile()->IsOffTheRecord()));
2573       selected_item->Pause();
2574     }
2575   } else if (action == "resume") {
2576     if (!selected_item->IsPaused()) {
2577       // Action would be a no-op; respond right from here.  No-op implies
2578       // the test is poorly written or failing, so make it an error return.
2579       AutomationJSONReply(this, reply_message)
2580           .SendError("Action 'resume' called on unpaused download.");
2581     } else {
2582       selected_item->AddObserver(new AutomationProviderDownloadUpdatedObserver(
2583           this, reply_message, false, browser->profile()->IsOffTheRecord()));
2584       selected_item->Resume();
2585     }
2586   } else if (action == "cancel") {
2587     selected_item->AddObserver(new AutomationProviderDownloadUpdatedObserver(
2588         this, reply_message, false, browser->profile()->IsOffTheRecord()));
2589     selected_item->Cancel(true);
2590   } else {
2591     AutomationJSONReply(this, reply_message)
2592         .SendError(
2593             base::StringPrintf("Invalid action '%s' given.", action.c_str()));
2594   }
2595 }
2596
2597 void TestingAutomationProvider::SetDownloadShelfVisibleJSON(
2598     base::DictionaryValue* args,
2599     IPC::Message* reply_message) {
2600   AutomationJSONReply reply(this, reply_message);
2601   Browser* browser;
2602   std::string error_msg;
2603   bool is_visible;
2604   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
2605     reply.SendError(error_msg);
2606     return;
2607   }
2608   if (!args->GetBoolean("is_visible", &is_visible)) {
2609     reply.SendError("'is_visible' missing or invalid.");
2610     return;
2611   }
2612   if (is_visible) {
2613     browser->window()->GetDownloadShelf()->Show();
2614   } else {
2615     browser->window()->GetDownloadShelf()->Close(DownloadShelf::AUTOMATIC);
2616   }
2617   reply.SendSuccess(NULL);
2618 }
2619
2620 void TestingAutomationProvider::IsDownloadShelfVisibleJSON(
2621     base::DictionaryValue* args,
2622     IPC::Message* reply_message) {
2623   AutomationJSONReply reply(this, reply_message);
2624   Browser* browser;
2625   std::string error_msg;
2626   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
2627     reply.SendError(error_msg);
2628     return;
2629   }
2630   base::DictionaryValue dict;
2631   dict.SetBoolean("is_visible", browser->window()->IsDownloadShelfVisible());
2632   reply.SendSuccess(&dict);
2633 }
2634
2635 void TestingAutomationProvider::GetDownloadDirectoryJSON(
2636     base::DictionaryValue* args,
2637     IPC::Message* reply_message) {
2638   AutomationJSONReply reply(this, reply_message);
2639   WebContents* web_contents;
2640   std::string error;
2641   if (!GetTabFromJSONArgs(args, &web_contents, &error)) {
2642     reply.SendError(error);
2643     return;
2644   }
2645   DownloadManager* dlm =
2646       BrowserContext::GetDownloadManager(
2647           web_contents->GetController().GetBrowserContext());
2648   base::DictionaryValue dict;
2649   dict.SetString("path",
2650       DownloadPrefs::FromDownloadManager(dlm)->DownloadPath().value());
2651   reply.SendSuccess(&dict);
2652 }
2653
2654 // Sample JSON input { "command": "LoadSearchEngineInfo" }
2655 void TestingAutomationProvider::LoadSearchEngineInfo(
2656     Browser* browser,
2657     base::DictionaryValue* args,
2658     IPC::Message* reply_message) {
2659   TemplateURLService* url_model =
2660       TemplateURLServiceFactory::GetForProfile(browser->profile());
2661   if (url_model->loaded()) {
2662     AutomationJSONReply(this, reply_message).SendSuccess(NULL);
2663     return;
2664   }
2665   url_model->AddObserver(new AutomationProviderSearchEngineObserver(
2666       this, browser->profile(), reply_message));
2667   url_model->Load();
2668 }
2669
2670 // Sample JSON input { "command": "GetSearchEngineInfo" }
2671 // Refer to pyauto.py for sample output.
2672 void TestingAutomationProvider::GetSearchEngineInfo(
2673     Browser* browser,
2674     base::DictionaryValue* args,
2675     IPC::Message* reply_message) {
2676   TemplateURLService* url_model =
2677       TemplateURLServiceFactory::GetForProfile(browser->profile());
2678   scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
2679   base::ListValue* search_engines = new base::ListValue;
2680   TemplateURLService::TemplateURLVector template_urls =
2681       url_model->GetTemplateURLs();
2682   for (TemplateURLService::TemplateURLVector::const_iterator it =
2683        template_urls.begin(); it != template_urls.end(); ++it) {
2684     base::DictionaryValue* search_engine = new base::DictionaryValue;
2685     search_engine->SetString("short_name",
2686                              base::UTF16ToUTF8((*it)->short_name()));
2687     search_engine->SetString("keyword", base::UTF16ToUTF8((*it)->keyword()));
2688     search_engine->SetBoolean("in_default_list", (*it)->ShowInDefaultList());
2689     search_engine->SetBoolean("is_default",
2690         (*it) == url_model->GetDefaultSearchProvider());
2691     search_engine->SetBoolean("is_valid", (*it)->url_ref().IsValid());
2692     search_engine->SetBoolean("supports_replacement",
2693                               (*it)->url_ref().SupportsReplacement());
2694     search_engine->SetString("url", (*it)->url());
2695     search_engine->SetString("host", (*it)->url_ref().GetHost());
2696     search_engine->SetString("path", (*it)->url_ref().GetPath());
2697     search_engine->SetString("display_url",
2698                              base::UTF16ToUTF8((*it)->url_ref().DisplayURL()));
2699     search_engines->Append(search_engine);
2700   }
2701   return_value->Set("search_engines", search_engines);
2702   AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
2703 }
2704
2705 // Refer to pyauto.py for sample JSON input.
2706 void TestingAutomationProvider::AddOrEditSearchEngine(
2707     Browser* browser,
2708     base::DictionaryValue* args,
2709     IPC::Message* reply_message) {
2710   TemplateURLService* url_model =
2711       TemplateURLServiceFactory::GetForProfile(browser->profile());
2712   base::string16 new_title;
2713   base::string16 new_keyword;
2714   std::string new_url;
2715   std::string keyword;
2716   if (!args->GetString("new_title", &new_title) ||
2717       !args->GetString("new_keyword", &new_keyword) ||
2718       !args->GetString("new_url", &new_url)) {
2719     AutomationJSONReply(this, reply_message).SendError(
2720         "One or more inputs invalid");
2721     return;
2722   }
2723   std::string new_ref_url = TemplateURLRef::DisplayURLToURLRef(
2724       base::UTF8ToUTF16(new_url));
2725   scoped_ptr<KeywordEditorController> controller(
2726       new KeywordEditorController(browser->profile()));
2727   if (args->GetString("keyword", &keyword)) {
2728     TemplateURL* template_url =
2729         url_model->GetTemplateURLForKeyword(base::UTF8ToUTF16(keyword));
2730     if (template_url == NULL) {
2731       AutomationJSONReply(this, reply_message).SendError(
2732           "No match for keyword: " + keyword);
2733       return;
2734     }
2735     url_model->AddObserver(new AutomationProviderSearchEngineObserver(
2736         this, browser->profile(), reply_message));
2737     controller->ModifyTemplateURL(template_url, new_title, new_keyword,
2738                                   new_ref_url);
2739   } else {
2740     url_model->AddObserver(new AutomationProviderSearchEngineObserver(
2741         this, browser->profile(), reply_message));
2742     controller->AddTemplateURL(new_title, new_keyword, new_ref_url);
2743   }
2744 }
2745
2746 // Sample json input: { "command": "PerformActionOnSearchEngine",
2747 //                      "keyword": keyword, "action": action }
2748 void TestingAutomationProvider::PerformActionOnSearchEngine(
2749     Browser* browser,
2750     base::DictionaryValue* args,
2751     IPC::Message* reply_message) {
2752   TemplateURLService* url_model =
2753       TemplateURLServiceFactory::GetForProfile(browser->profile());
2754   std::string keyword;
2755   std::string action;
2756   if (!args->GetString("keyword", &keyword) ||
2757       !args->GetString("action", &action)) {
2758     AutomationJSONReply(this, reply_message).SendError(
2759         "One or more inputs invalid");
2760     return;
2761   }
2762   TemplateURL* template_url =
2763       url_model->GetTemplateURLForKeyword(base::UTF8ToUTF16(keyword));
2764   if (template_url == NULL) {
2765     AutomationJSONReply(this, reply_message).SendError(
2766         "No match for keyword: " + keyword);
2767     return;
2768   }
2769   if (action == "delete") {
2770     url_model->AddObserver(new AutomationProviderSearchEngineObserver(
2771       this, browser->profile(), reply_message));
2772     url_model->Remove(template_url);
2773   } else if (action == "default") {
2774     url_model->AddObserver(new AutomationProviderSearchEngineObserver(
2775       this, browser->profile(), reply_message));
2776     url_model->SetDefaultSearchProvider(template_url);
2777   } else {
2778     AutomationJSONReply(this, reply_message).SendError(
2779         "Invalid action: " + action);
2780   }
2781 }
2782
2783 // Sample json input: { "command": "GetLocalStatePrefsInfo" }
2784 // Refer chrome/test/pyautolib/prefs_info.py for sample json output.
2785 void TestingAutomationProvider::GetLocalStatePrefsInfo(
2786     base::DictionaryValue* args,
2787     IPC::Message* reply_message) {
2788   scoped_ptr<base::DictionaryValue> items(
2789       g_browser_process->local_state()->GetPreferenceValues());
2790   scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
2791   return_value->Set("prefs", items.release());  // return_value owns items.
2792   AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
2793 }
2794
2795 // Sample json input: { "command": "SetLocalStatePrefs", "path": path,
2796 //                      "value": value }
2797 void TestingAutomationProvider::SetLocalStatePrefs(
2798     base::DictionaryValue* args,
2799     IPC::Message* reply_message) {
2800   std::string path;
2801   base::Value* val = NULL;
2802   AutomationJSONReply reply(this, reply_message);
2803   if (args->GetString("path", &path) && args->Get("value", &val)) {
2804     PrefService* pref_service = g_browser_process->local_state();
2805     const PrefService::Preference* pref =
2806         pref_service->FindPreference(path.c_str());
2807     if (!pref) {  // Not a registered pref.
2808       reply.SendError("pref not registered.");
2809       return;
2810     } else if (pref->IsManaged()) {  // Do not attempt to change a managed pref.
2811       reply.SendError("pref is managed. cannot be changed.");
2812       return;
2813     } else {  // Set the pref.
2814       pref_service->Set(path.c_str(), *val);
2815     }
2816   } else {
2817     reply.SendError("no pref path or value given.");
2818     return;
2819   }
2820
2821   reply.SendSuccess(NULL);
2822 }
2823
2824 // Sample json input: { "command": "GetPrefsInfo", "windex": 0 }
2825 // Refer chrome/test/pyautolib/prefs_info.py for sample json output.
2826 void TestingAutomationProvider::GetPrefsInfo(base::DictionaryValue* args,
2827                                              IPC::Message* reply_message) {
2828   AutomationJSONReply reply(this, reply_message);
2829   Browser* browser;
2830   std::string error_msg;
2831   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
2832     reply.SendError(error_msg);
2833     return;
2834   }
2835   scoped_ptr<base::DictionaryValue> items(
2836       browser->profile()->GetPrefs()->GetPreferenceValues());
2837
2838   scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
2839   return_value->Set("prefs", items.release());  // return_value owns items.
2840   reply.SendSuccess(return_value.get());
2841 }
2842
2843 // Sample json input:
2844 // { "command": "SetPrefs",
2845 //   "windex": 0,
2846 //   "path": path,
2847 //   "value": value }
2848 void TestingAutomationProvider::SetPrefs(base::DictionaryValue* args,
2849                                          IPC::Message* reply_message) {
2850   AutomationJSONReply reply(this, reply_message);
2851   Browser* browser;
2852   std::string error_msg;
2853   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
2854     reply.SendError(error_msg);
2855     return;
2856   }
2857   std::string path;
2858   base::Value* val = NULL;
2859   if (args->GetString("path", &path) && args->Get("value", &val)) {
2860     PrefService* pref_service = browser->profile()->GetPrefs();
2861     const PrefService::Preference* pref =
2862         pref_service->FindPreference(path.c_str());
2863     if (!pref) {  // Not a registered pref.
2864       reply.SendError("pref not registered.");
2865       return;
2866     } else if (pref->IsManaged()) {  // Do not attempt to change a managed pref.
2867       reply.SendError("pref is managed. cannot be changed.");
2868       return;
2869     } else {  // Set the pref.
2870       pref_service->Set(path.c_str(), *val);
2871     }
2872   } else {
2873     reply.SendError("no pref path or value given.");
2874     return;
2875   }
2876
2877   reply.SendSuccess(NULL);
2878 }
2879
2880 // Sample json input: { "command": "GetOmniboxInfo" }
2881 // Refer chrome/test/pyautolib/omnibox_info.py for sample json output.
2882 void TestingAutomationProvider::GetOmniboxInfo(Browser* browser,
2883                                                base::DictionaryValue* args,
2884                                                IPC::Message* reply_message) {
2885   scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
2886   AutomationJSONReply reply(this, reply_message);
2887
2888   LocationBar* loc_bar = browser->window()->GetLocationBar();
2889   if (!loc_bar) {
2890     reply.SendError("The specified browser does not have a location bar.");
2891     return;
2892   }
2893   const OmniboxView* omnibox_view = loc_bar->GetOmniboxView();
2894   const OmniboxEditModel* model = omnibox_view->model();
2895
2896   // Fill up matches.
2897   base::ListValue* matches = new base::ListValue;
2898   const AutocompleteResult& result = model->result();
2899   for (AutocompleteResult::const_iterator i(result.begin()); i != result.end();
2900        ++i) {
2901     const AutocompleteMatch& match = *i;
2902     base::DictionaryValue* item =
2903         new base::DictionaryValue;  // owned by return_value
2904     item->SetString("type", AutocompleteMatchType::ToString(match.type));
2905     item->SetBoolean("starred", match.starred);
2906     item->SetString("destination_url", match.destination_url.spec());
2907     item->SetString("contents", match.contents);
2908     item->SetString("description", match.description);
2909     matches->Append(item);
2910   }
2911   return_value->Set("matches", matches);
2912
2913   // Fill up other properties.
2914   base::DictionaryValue* properties =
2915       new base::DictionaryValue;  // owned by return_value
2916   properties->SetBoolean("has_focus", model->has_focus());
2917   properties->SetBoolean("query_in_progress",
2918                          !model->autocomplete_controller()->done());
2919   properties->SetString("keyword", model->keyword());
2920   properties->SetString("text", omnibox_view->GetText());
2921   return_value->Set("properties", properties);
2922
2923   reply.SendSuccess(return_value.get());
2924 }
2925
2926 // Sample json input: { "command": "SetOmniboxText",
2927 //                      "text": "goog" }
2928 void TestingAutomationProvider::SetOmniboxText(Browser* browser,
2929                                                base::DictionaryValue* args,
2930                                                IPC::Message* reply_message) {
2931   base::string16 text;
2932   AutomationJSONReply reply(this, reply_message);
2933   if (!args->GetString("text", &text)) {
2934     reply.SendError("text missing");
2935     return;
2936   }
2937   chrome::FocusLocationBar(browser);
2938   LocationBar* loc_bar = browser->window()->GetLocationBar();
2939   if (!loc_bar) {
2940     reply.SendError("The specified browser does not have a location bar.");
2941     return;
2942   }
2943   OmniboxView* omnibox_view = loc_bar->GetOmniboxView();
2944   omnibox_view->model()->OnSetFocus(false);
2945   omnibox_view->SetUserText(text);
2946   reply.SendSuccess(NULL);
2947 }
2948
2949 // Sample json input: { "command": "OmniboxMovePopupSelection",
2950 //                      "count": 1 }
2951 // Negative count implies up, positive implies down. Count values will be
2952 // capped by the size of the popup list.
2953 void TestingAutomationProvider::OmniboxMovePopupSelection(
2954     Browser* browser,
2955     base::DictionaryValue* args,
2956     IPC::Message* reply_message) {
2957   int count;
2958   AutomationJSONReply reply(this, reply_message);
2959   if (!args->GetInteger("count", &count)) {
2960     reply.SendError("count missing");
2961     return;
2962   }
2963   LocationBar* loc_bar = browser->window()->GetLocationBar();
2964   if (!loc_bar) {
2965     reply.SendError("The specified browser does not have a location bar.");
2966     return;
2967   }
2968   loc_bar->GetOmniboxView()->model()->OnUpOrDownKeyPressed(count);
2969   reply.SendSuccess(NULL);
2970 }
2971
2972 // Sample json input: { "command": "OmniboxAcceptInput" }
2973 void TestingAutomationProvider::OmniboxAcceptInput(
2974     Browser* browser,
2975     base::DictionaryValue* args,
2976     IPC::Message* reply_message) {
2977   NavigationController& controller =
2978       browser->tab_strip_model()->GetActiveWebContents()->GetController();
2979   LocationBar* loc_bar = browser->window()->GetLocationBar();
2980   if (!loc_bar) {
2981     AutomationJSONReply(this, reply_message).SendError(
2982         "The specified browser does not have a location bar.");
2983     return;
2984   }
2985   new OmniboxAcceptNotificationObserver(&controller, this, reply_message);
2986   loc_bar->AcceptInput();
2987 }
2988
2989 // Sample json input: { "command": "GetInitialLoadTimes" }
2990 // Refer to InitialLoadObserver::GetTimingInformation() for sample output.
2991 void TestingAutomationProvider::GetInitialLoadTimes(
2992     Browser*,
2993     base::DictionaryValue*,
2994     IPC::Message* reply_message) {
2995   scoped_ptr<base::DictionaryValue> return_value(
2996       initial_load_observer_->GetTimingInformation());
2997
2998   std::string json_return;
2999   base::JSONWriter::Write(return_value.get(), &json_return);
3000   AutomationMsg_SendJSONRequest::WriteReplyParams(
3001       reply_message, json_return, true);
3002   Send(reply_message);
3003 }
3004
3005 // Sample json input: { "command": "GetPluginsInfo" }
3006 // Refer chrome/test/pyautolib/plugins_info.py for sample json output.
3007 void TestingAutomationProvider::GetPluginsInfo(
3008     Browser* browser,
3009     base::DictionaryValue* args,
3010     IPC::Message* reply_message) {
3011   PluginService::GetInstance()->GetPlugins(
3012       base::Bind(&TestingAutomationProvider::GetPluginsInfoCallback,
3013           this, browser, args, reply_message));
3014 }
3015
3016 void TestingAutomationProvider::GetPluginsInfoCallback(
3017     Browser* browser,
3018     base::DictionaryValue* args,
3019     IPC::Message* reply_message,
3020     const std::vector<content::WebPluginInfo>& plugins) {
3021   PluginPrefs* plugin_prefs =
3022       PluginPrefs::GetForProfile(browser->profile()).get();
3023   base::ListValue* items = new base::ListValue;
3024   for (std::vector<content::WebPluginInfo>::const_iterator it =
3025            plugins.begin();
3026        it != plugins.end();
3027        ++it) {
3028     base::DictionaryValue* item = new base::DictionaryValue;
3029     item->SetString("name", it->name);
3030     item->SetString("path", it->path.value());
3031     item->SetString("version", it->version);
3032     item->SetString("desc", it->desc);
3033     item->SetBoolean("enabled", plugin_prefs->IsPluginEnabled(*it));
3034     // Add info about mime types.
3035     base::ListValue* mime_types = new base::ListValue();
3036     for (std::vector<content::WebPluginMimeType>::const_iterator type_it =
3037              it->mime_types.begin();
3038          type_it != it->mime_types.end();
3039          ++type_it) {
3040       base::DictionaryValue* mime_type = new base::DictionaryValue();
3041       mime_type->SetString("mimeType", type_it->mime_type);
3042       mime_type->SetString("description", type_it->description);
3043
3044       base::ListValue* file_extensions = new base::ListValue();
3045       for (std::vector<std::string>::const_iterator ext_it =
3046                type_it->file_extensions.begin();
3047            ext_it != type_it->file_extensions.end();
3048            ++ext_it) {
3049         file_extensions->Append(new base::StringValue(*ext_it));
3050       }
3051       mime_type->Set("fileExtensions", file_extensions);
3052
3053       mime_types->Append(mime_type);
3054     }
3055     item->Set("mimeTypes", mime_types);
3056     items->Append(item);
3057   }
3058   scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
3059   return_value->Set("plugins", items);  // return_value owns items.
3060
3061   AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
3062 }
3063
3064 // Sample json input:
3065 //    { "command": "EnablePlugin",
3066 //      "path": "/Library/Internet Plug-Ins/Flash Player.plugin" }
3067 void TestingAutomationProvider::EnablePlugin(Browser* browser,
3068                                              base::DictionaryValue* args,
3069                                              IPC::Message* reply_message) {
3070   base::FilePath::StringType path;
3071   if (!args->GetString("path", &path)) {
3072     AutomationJSONReply(this, reply_message).SendError("path not specified.");
3073     return;
3074   }
3075   PluginPrefs* plugin_prefs =
3076       PluginPrefs::GetForProfile(browser->profile()).get();
3077   plugin_prefs->EnablePlugin(
3078       true,
3079       base::FilePath(path),
3080       base::Bind(&DidEnablePlugin,
3081                  AsWeakPtr(),
3082                  reply_message,
3083                  path,
3084                  "Could not enable plugin for path %s."));
3085 }
3086
3087 // Sample json input:
3088 //    { "command": "DisablePlugin",
3089 //      "path": "/Library/Internet Plug-Ins/Flash Player.plugin" }
3090 void TestingAutomationProvider::DisablePlugin(Browser* browser,
3091                                               base::DictionaryValue* args,
3092                                               IPC::Message* reply_message) {
3093   base::FilePath::StringType path;
3094   if (!args->GetString("path", &path)) {
3095     AutomationJSONReply(this, reply_message).SendError("path not specified.");
3096     return;
3097   }
3098   PluginPrefs* plugin_prefs =
3099       PluginPrefs::GetForProfile(browser->profile()).get();
3100   plugin_prefs->EnablePlugin(
3101       false,
3102       base::FilePath(path),
3103       base::Bind(&DidEnablePlugin,
3104                  AsWeakPtr(),
3105                  reply_message,
3106                  path,
3107                  "Could not disable plugin for path %s."));
3108 }
3109
3110 // Sample json input:
3111 //    { "command": "SaveTabContents",
3112 //      "tab_index": 0,
3113 //      "filename": <a full pathname> }
3114 // Sample json output:
3115 //    {}
3116 void TestingAutomationProvider::SaveTabContents(
3117     Browser* browser,
3118     base::DictionaryValue* args,
3119     IPC::Message* reply_message) {
3120   int tab_index = 0;
3121   base::FilePath::StringType filename;
3122   base::FilePath::StringType parent_directory;
3123   WebContents* web_contents = NULL;
3124
3125   if (!args->GetInteger("tab_index", &tab_index) ||
3126       !args->GetString("filename", &filename)) {
3127     AutomationJSONReply(this, reply_message)
3128         .SendError("tab_index or filename param missing");
3129     return;
3130   } else {
3131     web_contents = browser->tab_strip_model()->GetWebContentsAt(tab_index);
3132     if (!web_contents) {
3133       AutomationJSONReply(this, reply_message).SendError("no tab at tab_index");
3134       return;
3135     }
3136   }
3137   // We're doing a SAVE_AS_ONLY_HTML so the the directory path isn't
3138   // used.  Nevertheless, SavePackage requires it be valid.  Sigh.
3139   parent_directory = base::FilePath(filename).DirName().value();
3140   if (!web_contents->SavePage(
3141           base::FilePath(filename),
3142           base::FilePath(parent_directory),
3143           content::SAVE_PAGE_TYPE_AS_ONLY_HTML)) {
3144     AutomationJSONReply(this, reply_message).SendError(
3145         "Could not initiate SavePage");
3146     return;
3147   }
3148   // The observer will delete itself when done.
3149   new SavePackageNotificationObserver(
3150       BrowserContext::GetDownloadManager(browser->profile()),
3151       this, reply_message);
3152 }
3153
3154 namespace {
3155
3156 // Translates a dictionary password to a PasswordForm struct.
3157 autofill::PasswordForm GetPasswordFormFromDict(
3158     const base::DictionaryValue& password_dict) {
3159
3160   // If the time is specified, change time to the specified time.
3161   base::Time time = base::Time::Now();
3162   int it;
3163   double dt;
3164   if (password_dict.GetInteger("time", &it))
3165     time = base::Time::FromTimeT(it);
3166   else if (password_dict.GetDouble("time", &dt))
3167     time = base::Time::FromDoubleT(dt);
3168
3169   std::string signon_realm;
3170   base::string16 username_value;
3171   base::string16 password_value;
3172   base::string16 origin_url_text;
3173   base::string16 username_element;
3174   base::string16 password_element;
3175   base::string16 submit_element;
3176   base::string16 action_target_text;
3177   bool blacklist;
3178   base::string16 old_password_element;
3179   base::string16 old_password_value;
3180
3181   // We don't care if any of these fail - they are either optional or checked
3182   // before this function is called.
3183   password_dict.GetString("signon_realm", &signon_realm);
3184   password_dict.GetString("username_value", &username_value);
3185   password_dict.GetString("password_value", &password_value);
3186   password_dict.GetString("origin_url", &origin_url_text);
3187   password_dict.GetString("username_element", &username_element);
3188   password_dict.GetString("password_element", &password_element);
3189   password_dict.GetString("submit_element", &submit_element);
3190   password_dict.GetString("action_target", &action_target_text);
3191   if (!password_dict.GetBoolean("blacklist", &blacklist))
3192     blacklist = false;
3193
3194   GURL origin_gurl(origin_url_text);
3195   GURL action_target(action_target_text);
3196
3197   autofill::PasswordForm password_form;
3198   password_form.signon_realm = signon_realm;
3199   password_form.username_value = username_value;
3200   password_form.password_value = password_value;
3201   password_form.origin = origin_gurl;
3202   password_form.username_element = username_element;
3203   password_form.password_element = password_element;
3204   password_form.submit_element = submit_element;
3205   password_form.action = action_target;
3206   password_form.blacklisted_by_user = blacklist;
3207   password_form.date_created = time;
3208
3209   return password_form;
3210 }
3211
3212 }  // namespace
3213
3214 // See AddSavedPassword() in chrome/test/functional/pyauto.py for sample json
3215 // input.
3216 // Sample json output: { "password_added": true }
3217 void TestingAutomationProvider::AddSavedPassword(
3218     Browser* browser,
3219     base::DictionaryValue* args,
3220     IPC::Message* reply_message) {
3221   base::DictionaryValue* password_dict = NULL;
3222   if (!args->GetDictionary("password", &password_dict)) {
3223     AutomationJSONReply(this, reply_message).SendError(
3224         "Must specify a password dictionary.");
3225     return;
3226   }
3227
3228   // The "signon realm" is effectively the primary key and must be included.
3229   // Check here before calling GetPasswordFormFromDict.
3230   if (!password_dict->HasKey("signon_realm")) {
3231     AutomationJSONReply(this, reply_message).SendError(
3232         "Password must include a value for 'signon_realm.'");
3233     return;
3234   }
3235
3236   autofill::PasswordForm new_password =
3237       GetPasswordFormFromDict(*password_dict);
3238
3239   // Use IMPLICIT_ACCESS since new passwords aren't added in incognito mode.
3240   PasswordStore* password_store = PasswordStoreFactory::GetForProfile(
3241       browser->profile(), Profile::IMPLICIT_ACCESS).get();
3242
3243   // The password store does not exist for an incognito window.
3244   if (password_store == NULL) {
3245     scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
3246     return_value->SetBoolean("password_added", false);
3247     AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
3248     return;
3249   }
3250
3251   // This observer will delete itself.
3252   PasswordStoreLoginsChangedObserver* observer =
3253       new PasswordStoreLoginsChangedObserver(this, reply_message,
3254                                              PasswordStoreChange::ADD,
3255                                              "password_added");
3256   observer->Init();
3257   password_store->AddLogin(new_password);
3258 }
3259
3260 // See RemoveSavedPassword() in chrome/test/functional/pyauto.py for sample
3261 // json input.
3262 // Sample json output: {}
3263 void TestingAutomationProvider::RemoveSavedPassword(
3264     Browser* browser,
3265     base::DictionaryValue* args,
3266     IPC::Message* reply_message) {
3267   base::DictionaryValue* password_dict = NULL;
3268
3269   if (!args->GetDictionary("password", &password_dict)) {
3270     AutomationJSONReply(this, reply_message).SendError(
3271         "Must specify a password dictionary.");
3272     return;
3273   }
3274
3275   // The "signon realm" is effectively the primary key and must be included.
3276   // Check here before calling GetPasswordFormFromDict.
3277   if (!password_dict->HasKey("signon_realm")) {
3278     AutomationJSONReply(this, reply_message).SendError(
3279         "Password must include a value for 'signon_realm.'");
3280     return;
3281   }
3282   autofill::PasswordForm to_remove =
3283       GetPasswordFormFromDict(*password_dict);
3284
3285   // Use EXPLICIT_ACCESS since passwords can be removed in incognito mode.
3286   PasswordStore* password_store = PasswordStoreFactory::GetForProfile(
3287       browser->profile(), Profile::EXPLICIT_ACCESS).get();
3288   if (password_store == NULL) {
3289     AutomationJSONReply(this, reply_message).SendError(
3290         "Unable to get password store.");
3291     return;
3292   }
3293
3294   // This observer will delete itself.
3295   PasswordStoreLoginsChangedObserver* observer =
3296       new PasswordStoreLoginsChangedObserver(
3297           this, reply_message, PasswordStoreChange::REMOVE, std::string());
3298   observer->Init();
3299
3300   password_store->RemoveLogin(to_remove);
3301 }
3302
3303 // Sample json input: { "command": "GetSavedPasswords" }
3304 // Refer to GetSavedPasswords() in chrome/test/pyautolib/pyauto.py for sample
3305 // json output.
3306 void TestingAutomationProvider::GetSavedPasswords(
3307     Browser* browser,
3308     base::DictionaryValue* args,
3309     IPC::Message* reply_message) {
3310   // Use EXPLICIT_ACCESS since saved passwords can be retrieved in
3311   // incognito mode.
3312   PasswordStore* password_store = PasswordStoreFactory::GetForProfile(
3313       browser->profile(), Profile::EXPLICIT_ACCESS).get();
3314
3315   if (password_store == NULL) {
3316     AutomationJSONReply reply(this, reply_message);
3317     reply.SendError("Unable to get password store.");
3318     return;
3319   }
3320   password_store->GetAutofillableLogins(
3321       new AutomationProviderGetPasswordsObserver(this, reply_message));
3322   // Observer deletes itself after sending the result.
3323 }
3324
3325 namespace {
3326
3327 // Get the WebContents from a dictionary of arguments.
3328 WebContents* GetWebContentsFromDict(const Browser* browser,
3329                                     const base::DictionaryValue* args,
3330                                     std::string* error_message) {
3331   int tab_index;
3332   if (!args->GetInteger("tab_index", &tab_index)) {
3333     *error_message = "Must include tab_index.";
3334     return NULL;
3335   }
3336
3337   WebContents* web_contents =
3338       browser->tab_strip_model()->GetWebContentsAt(tab_index);
3339   if (!web_contents) {
3340     *error_message = base::StringPrintf("No tab at index %d.", tab_index);
3341     return NULL;
3342   }
3343   return web_contents;
3344 }
3345
3346 }  // namespace
3347
3348 void TestingAutomationProvider::FindInPage(
3349     Browser* browser,
3350     base::DictionaryValue* args,
3351     IPC::Message* reply_message) {
3352   std::string error_message;
3353   WebContents* web_contents =
3354       GetWebContentsFromDict(browser, args, &error_message);
3355   if (!web_contents) {
3356     AutomationJSONReply(this, reply_message).SendError(error_message);
3357     return;
3358   }
3359   base::string16 search_string;
3360   bool forward;
3361   bool match_case;
3362   bool find_next;
3363   if (!args->GetString("search_string", &search_string)) {
3364     AutomationJSONReply(this, reply_message).
3365         SendError("Must include search_string string.");
3366     return;
3367   }
3368   if (!args->GetBoolean("forward", &forward)) {
3369     AutomationJSONReply(this, reply_message).
3370         SendError("Must include forward boolean.");
3371     return;
3372   }
3373   if (!args->GetBoolean("match_case", &match_case)) {
3374     AutomationJSONReply(this, reply_message).
3375         SendError("Must include match_case boolean.");
3376     return;
3377   }
3378   if (!args->GetBoolean("find_next", &find_next)) {
3379     AutomationJSONReply(this, reply_message).
3380         SendError("Must include find_next boolean.");
3381     return;
3382   }
3383   SendFindRequest(web_contents,
3384                   true,
3385                   search_string,
3386                   forward,
3387                   match_case,
3388                   find_next,
3389                   reply_message);
3390 }
3391
3392 void TestingAutomationProvider::OpenFindInPage(
3393     base::DictionaryValue* args,
3394     IPC::Message* reply_message) {
3395   AutomationJSONReply reply(this, reply_message);
3396   Browser* browser;
3397   std::string error_msg;
3398   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
3399     reply.SendError(error_msg);
3400     return;
3401   }
3402   chrome::FindInPage(browser, false, false);
3403   reply.SendSuccess(NULL);
3404 }
3405
3406 void TestingAutomationProvider::IsFindInPageVisible(
3407     base::DictionaryValue* args,
3408     IPC::Message* reply_message) {
3409   AutomationJSONReply reply(this, reply_message);
3410   bool visible;
3411   Browser* browser;
3412   std::string error_msg;
3413   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
3414     reply.SendError(error_msg);
3415     return;
3416   }
3417   FindBarTesting* find_bar =
3418       browser->GetFindBarController()->find_bar()->GetFindBarTesting();
3419   find_bar->GetFindBarWindowInfo(NULL, &visible);
3420   base::DictionaryValue dict;
3421   dict.SetBoolean("is_visible", visible);
3422   reply.SendSuccess(&dict);
3423 }
3424
3425 void TestingAutomationProvider::InstallExtension(
3426     base::DictionaryValue* args, IPC::Message* reply_message) {
3427   base::FilePath::StringType path_string;
3428   bool with_ui;
3429   bool from_webstore = false;
3430   Browser* browser;
3431   content::WebContents* tab;
3432   std::string error_msg;
3433   if (!GetBrowserAndTabFromJSONArgs(args, &browser, &tab, &error_msg)) {
3434     AutomationJSONReply(this, reply_message).SendError(error_msg);
3435     return;
3436   }
3437   if (!args->GetString("path", &path_string)) {
3438     AutomationJSONReply(this, reply_message).SendError(
3439         "Missing or invalid 'path'");
3440     return;
3441   }
3442   if (!args->GetBoolean("with_ui", &with_ui)) {
3443     AutomationJSONReply(this, reply_message).SendError(
3444         "Missing or invalid 'with_ui'");
3445     return;
3446   }
3447   args->GetBoolean("from_webstore", &from_webstore);
3448
3449   extensions::ExtensionSystem* system =
3450       extensions::ExtensionSystem::Get(browser->profile());
3451   ExtensionService* service = system->extension_service();
3452   if (service) {
3453     // The observer will delete itself when done.
3454     new ExtensionReadyNotificationObserver(
3455         system,
3456         this,
3457         reply_message);
3458
3459     base::FilePath extension_path(path_string);
3460     // If the given path has a 'crx' extension, assume it is a packed extension
3461     // and install it. Otherwise load it as an unpacked extension.
3462     if (extension_path.MatchesExtension(FILE_PATH_LITERAL(".crx"))) {
3463       scoped_ptr<ExtensionInstallPrompt> client(
3464           with_ui ? new ExtensionInstallPrompt(tab) : NULL);
3465       scoped_refptr<extensions::CrxInstaller> installer(
3466           extensions::CrxInstaller::Create(service, client.Pass()));
3467       if (!with_ui)
3468         installer->set_allow_silent_install(true);
3469       installer->set_install_cause(extension_misc::INSTALL_CAUSE_AUTOMATION);
3470       if (from_webstore)
3471         installer->set_creation_flags(Extension::FROM_WEBSTORE);
3472       installer->InstallCrx(extension_path);
3473     } else {
3474       scoped_refptr<extensions::UnpackedInstaller> installer(
3475           extensions::UnpackedInstaller::Create(service));
3476       installer->set_prompt_for_plugins(with_ui);
3477       installer->Load(extension_path);
3478     }
3479   } else {
3480     AutomationJSONReply(this, reply_message).SendError(
3481         "Extensions service is not available");
3482   }
3483 }
3484
3485 namespace {
3486
3487 base::ListValue* GetHostPermissions(const Extension* ext, bool effective_perm) {
3488   extensions::URLPatternSet pattern_set;
3489   if (effective_perm) {
3490     pattern_set =
3491         extensions::PermissionsData::GetEffectiveHostPermissions(ext);
3492   } else {
3493     pattern_set = ext->GetActivePermissions()->explicit_hosts();
3494   }
3495
3496   base::ListValue* permissions = new base::ListValue;
3497   for (extensions::URLPatternSet::const_iterator perm = pattern_set.begin();
3498        perm != pattern_set.end(); ++perm) {
3499     permissions->Append(new base::StringValue(perm->GetAsString()));
3500   }
3501
3502   return permissions;
3503 }
3504
3505 base::ListValue* GetAPIPermissions(const Extension* ext) {
3506   base::ListValue* permissions = new base::ListValue;
3507   std::set<std::string> perm_list =
3508       ext->GetActivePermissions()->GetAPIsAsStrings();
3509   for (std::set<std::string>::const_iterator perm = perm_list.begin();
3510        perm != perm_list.end(); ++perm) {
3511     permissions->Append(new base::StringValue(perm->c_str()));
3512   }
3513   return permissions;
3514 }
3515
3516 }  // namespace
3517
3518 // Sample json input: { "command": "GetExtensionsInfo" }
3519 // See GetExtensionsInfo() in chrome/test/pyautolib/pyauto.py for sample json
3520 // output.
3521 void TestingAutomationProvider::GetExtensionsInfo(base::DictionaryValue* args,
3522                                                   IPC::Message* reply_message) {
3523   AutomationJSONReply reply(this, reply_message);
3524   Browser* browser;
3525   std::string error_msg;
3526   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
3527     reply.SendError(error_msg);
3528     return;
3529   }
3530   ExtensionService* service = extensions::ExtensionSystem::Get(
3531       browser->profile())->extension_service();
3532   if (!service) {
3533     reply.SendError("No extensions service.");
3534     return;
3535   }
3536   scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
3537   base::ListValue* extensions_values = new base::ListValue;
3538   const extensions::ExtensionSet* extensions = service->extensions();
3539   const extensions::ExtensionSet* disabled_extensions =
3540       service->disabled_extensions();
3541   ExtensionList all;
3542   all.insert(all.end(),
3543              extensions->begin(),
3544              extensions->end());
3545   all.insert(all.end(),
3546              disabled_extensions->begin(),
3547              disabled_extensions->end());
3548   ExtensionActionManager* extension_action_manager =
3549       ExtensionActionManager::Get(browser->profile());
3550   for (ExtensionList::const_iterator it = all.begin();
3551        it != all.end(); ++it) {
3552     const Extension* extension = it->get();
3553     std::string id = extension->id();
3554     base::DictionaryValue* extension_value = new base::DictionaryValue;
3555     extension_value->SetString("id", id);
3556     extension_value->SetString("version", extension->VersionString());
3557     extension_value->SetString("name", extension->name());
3558     extension_value->SetString("public_key", extension->public_key());
3559     extension_value->SetString("description", extension->description());
3560     extension_value->SetString(
3561         "background_url",
3562         extensions::BackgroundInfo::GetBackgroundURL(extension).spec());
3563     extension_value->SetString("options_url",
3564         extensions::ManifestURL::GetOptionsPage(extension).spec());
3565     extension_value->Set("host_permissions",
3566                          GetHostPermissions(extension, false));
3567     extension_value->Set("effective_host_permissions",
3568                          GetHostPermissions(extension, true));
3569     extension_value->Set("api_permissions", GetAPIPermissions(extension));
3570     Manifest::Location location = extension->location();
3571     extension_value->SetBoolean("is_component",
3572                                 location == Manifest::COMPONENT);
3573     extension_value->SetBoolean("is_internal",
3574                                 location == Manifest::INTERNAL);
3575     extension_value->SetBoolean("is_user_installed",
3576         location == Manifest::INTERNAL ||
3577         Manifest::IsUnpackedLocation(location));
3578     extension_value->SetBoolean("is_enabled", service->IsExtensionEnabled(id));
3579     extension_value->SetBoolean("allowed_in_incognito",
3580         extensions::util::IsIncognitoEnabled(id, browser->profile()));
3581     extension_value->SetBoolean(
3582         "has_page_action",
3583         extension_action_manager->GetPageAction(*extension) != NULL);
3584     extensions_values->Append(extension_value);
3585   }
3586   return_value->Set("extensions", extensions_values);
3587   reply.SendSuccess(return_value.get());
3588 }
3589
3590 // See UninstallExtensionById() in chrome/test/pyautolib/pyauto.py for sample
3591 // json input.
3592 // Sample json output: {}
3593 void TestingAutomationProvider::UninstallExtensionById(
3594     base::DictionaryValue* args,
3595     IPC::Message* reply_message) {
3596   const Extension* extension;
3597   std::string error;
3598   Browser* browser;
3599   if (!GetBrowserFromJSONArgs(args, &browser, &error)) {
3600     AutomationJSONReply(this, reply_message).SendError(error);
3601     return;
3602   }
3603   if (!GetExtensionFromJSONArgs(
3604           args, "id", browser->profile(), &extension, &error)) {
3605     AutomationJSONReply(this, reply_message).SendError(error);
3606     return;
3607   }
3608   ExtensionService* service = extensions::ExtensionSystem::Get(
3609       browser->profile())->extension_service();
3610   if (!service) {
3611     AutomationJSONReply(this, reply_message).SendError(
3612         "No extensions service.");
3613     return;
3614   }
3615
3616   // Wait for a notification indicating that the extension with the given ID
3617   // has been uninstalled.  This observer will delete itself.
3618   new ExtensionUninstallObserver(this, reply_message, extension->id());
3619   service->UninstallExtension(extension->id(), false, NULL);
3620 }
3621
3622 // See SetExtensionStateById() in chrome/test/pyautolib/pyauto.py
3623 // for sample json input.
3624 void TestingAutomationProvider::SetExtensionStateById(
3625     base::DictionaryValue* args,
3626     IPC::Message* reply_message) {
3627   const Extension* extension;
3628   std::string error;
3629   Browser* browser;
3630   if (!GetBrowserFromJSONArgs(args, &browser, &error)) {
3631     AutomationJSONReply(this, reply_message).SendError(error);
3632     return;
3633   }
3634   if (!GetExtensionFromJSONArgs(
3635           args, "id", browser->profile(), &extension, &error)) {
3636     AutomationJSONReply(this, reply_message).SendError(error);
3637     return;
3638   }
3639
3640   bool enable;
3641   if (!args->GetBoolean("enable", &enable)) {
3642     AutomationJSONReply(this, reply_message)
3643         .SendError("Missing or invalid key: enable");
3644     return;
3645   }
3646
3647   bool allow_in_incognito;
3648   if (!args->GetBoolean("allow_in_incognito", &allow_in_incognito)) {
3649     AutomationJSONReply(this, reply_message)
3650         .SendError("Missing or invalid key: allow_in_incognito");
3651     return;
3652   }
3653
3654   if (allow_in_incognito && !enable) {
3655     AutomationJSONReply(this, reply_message)
3656         .SendError("Invalid state: Disabled extension "
3657                     "cannot be allowed in incognito mode.");
3658     return;
3659   }
3660
3661   extensions::ExtensionSystem* system =
3662       extensions::ExtensionSystem::Get(browser->profile());
3663   ExtensionService* service = system->extension_service();
3664   if (!service) {
3665     AutomationJSONReply(this, reply_message)
3666         .SendError("No extensions service.");
3667     return;
3668   }
3669
3670   if (enable) {
3671     if (!service->IsExtensionEnabled(extension->id())) {
3672       new ExtensionReadyNotificationObserver(
3673           system,
3674           this,
3675           reply_message);
3676       service->EnableExtension(extension->id());
3677     } else {
3678       AutomationJSONReply(this, reply_message).SendSuccess(NULL);
3679     }
3680   } else {
3681     service->DisableExtension(extension->id(),
3682                               Extension::DISABLE_USER_ACTION);
3683     AutomationJSONReply(this, reply_message).SendSuccess(NULL);
3684   }
3685
3686   extensions::util::SetIsIncognitoEnabled(
3687       extension->id(), browser->profile(), allow_in_incognito);
3688 }
3689
3690 // See TriggerPageActionById() in chrome/test/pyautolib/pyauto.py
3691 // for sample json input.
3692 void TestingAutomationProvider::TriggerPageActionById(
3693     base::DictionaryValue* args,
3694     IPC::Message* reply_message) {
3695   std::string error;
3696   Browser* browser;
3697   WebContents* tab;
3698   if (!GetBrowserAndTabFromJSONArgs(args, &browser, &tab, &error)) {
3699     AutomationJSONReply(this, reply_message).SendError(error);
3700     return;
3701   }
3702   const Extension* extension;
3703   if (!GetEnabledExtensionFromJSONArgs(
3704           args, "id", browser->profile(), &extension, &error)) {
3705     AutomationJSONReply(this, reply_message).SendError(error);
3706     return;
3707   }
3708   ExtensionAction* page_action =
3709       ExtensionActionManager::Get(browser->profile())->
3710       GetPageAction(*extension);
3711   if (!page_action) {
3712     AutomationJSONReply(this, reply_message).SendError(
3713         "Extension doesn't have any page action.");
3714     return;
3715   }
3716   EnsureTabSelected(browser, tab);
3717
3718   bool pressed = false;
3719   LocationBarTesting* loc_bar =
3720       browser->window()->GetLocationBar()->GetLocationBarForTesting();
3721   size_t page_action_visible_count =
3722       static_cast<size_t>(loc_bar->PageActionVisibleCount());
3723   for (size_t i = 0; i < page_action_visible_count; ++i) {
3724     if (loc_bar->GetVisiblePageAction(i) == page_action) {
3725       loc_bar->TestPageActionPressed(i);
3726       pressed = true;
3727       break;
3728     }
3729   }
3730   if (!pressed) {
3731     AutomationJSONReply(this, reply_message).SendError(
3732         "Extension's page action is not visible.");
3733     return;
3734   }
3735
3736   if (page_action->HasPopup(extensions::ExtensionTabUtil::GetTabId(tab))) {
3737     // This observer will delete itself.
3738     new ExtensionPopupObserver(
3739         this, reply_message, extension->id());
3740   } else {
3741     AutomationJSONReply(this, reply_message).SendSuccess(NULL);
3742   }
3743 }
3744
3745 // See TriggerBrowserActionById() in chrome/test/pyautolib/pyauto.py
3746 // for sample json input.
3747 void TestingAutomationProvider::TriggerBrowserActionById(
3748     base::DictionaryValue* args,
3749     IPC::Message* reply_message) {
3750   std::string error;
3751   Browser* browser;
3752   WebContents* tab;
3753   if (!GetBrowserAndTabFromJSONArgs(args, &browser, &tab, &error)) {
3754     AutomationJSONReply(this, reply_message).SendError(error);
3755     return;
3756   }
3757   const Extension* extension;
3758   if (!GetEnabledExtensionFromJSONArgs(
3759           args, "id", browser->profile(), &extension, &error)) {
3760     AutomationJSONReply(this, reply_message).SendError(error);
3761     return;
3762   }
3763   ExtensionAction* action = ExtensionActionManager::Get(browser->profile())->
3764       GetBrowserAction(*extension);
3765   if (!action) {
3766     AutomationJSONReply(this, reply_message).SendError(
3767         "Extension doesn't have any browser action.");
3768     return;
3769   }
3770   EnsureTabSelected(browser, tab);
3771
3772   BrowserActionTestUtil browser_actions(browser);
3773   int num_browser_actions = browser_actions.NumberOfBrowserActions();
3774   int action_index = -1;
3775 #if defined(TOOLKIT_VIEWS)
3776   for (int i = 0; i < num_browser_actions; ++i) {
3777     if (extension->id() == browser_actions.GetExtensionId(i)) {
3778       action_index = i;
3779       break;
3780     }
3781   }
3782 #else
3783   // TODO(kkania): Implement the platform-specific GetExtensionId() in
3784   // BrowserActionTestUtil.
3785   if (num_browser_actions != 1) {
3786     AutomationJSONReply(this, reply_message).SendError(base::StringPrintf(
3787         "Found %d browser actions. Only one browser action must be active.",
3788         num_browser_actions));
3789     return;
3790   }
3791   // This extension has a browser action, and there's only one action, so this
3792   // must be the first one.
3793   action_index = 0;
3794 #endif
3795   if (action_index == -1) {
3796     AutomationJSONReply(this, reply_message).SendError(
3797         "Extension's browser action is not visible.");
3798     return;
3799   }
3800   browser_actions.Press(action_index);
3801
3802   if (action->HasPopup(extensions::ExtensionTabUtil::GetTabId(tab))) {
3803     // This observer will delete itself.
3804     new ExtensionPopupObserver(
3805         this, reply_message, extension->id());
3806   } else {
3807     AutomationJSONReply(this, reply_message).SendSuccess(NULL);
3808   }
3809 }
3810
3811 void TestingAutomationProvider::ActionOnSSLBlockingPage(
3812     base::DictionaryValue* args,
3813     IPC::Message* reply_message) {
3814   WebContents* web_contents;
3815   bool proceed;
3816   std::string error;
3817   if (!GetTabFromJSONArgs(args, &web_contents, &error)) {
3818     AutomationJSONReply(this, reply_message).SendError(error);
3819     return;
3820   }
3821   if (!args->GetBoolean("proceed", &proceed)) {
3822     AutomationJSONReply(this, reply_message).SendError(
3823         "'proceed' is missing or invalid");
3824     return;
3825   }
3826   NavigationController& controller = web_contents->GetController();
3827   NavigationEntry* entry = controller.GetActiveEntry();
3828   if (entry->GetPageType() == content::PAGE_TYPE_INTERSTITIAL) {
3829     InterstitialPage* ssl_blocking_page =
3830         InterstitialPage::GetInterstitialPage(web_contents);
3831     if (ssl_blocking_page) {
3832       if (proceed) {
3833         new NavigationNotificationObserver(&controller, this, reply_message, 1,
3834                                            false, true);
3835         ssl_blocking_page->Proceed();
3836         return;
3837       }
3838       ssl_blocking_page->DontProceed();
3839       AutomationJSONReply(this, reply_message).SendSuccess(NULL);
3840       return;
3841     }
3842   }
3843   AutomationJSONReply(this, reply_message).SendError(error);
3844 }
3845
3846 void TestingAutomationProvider::GetSecurityState(base::DictionaryValue* args,
3847                                                  IPC::Message* reply_message) {
3848   AutomationJSONReply reply(this, reply_message);
3849   WebContents* web_contents;
3850   std::string error;
3851   if (!GetTabFromJSONArgs(args, &web_contents, &error)) {
3852     reply.SendError(error);
3853     return;
3854   }
3855   NavigationEntry* entry = web_contents->GetController().GetActiveEntry();
3856   base::DictionaryValue dict;
3857   dict.SetInteger("security_style",
3858                   static_cast<int>(entry->GetSSL().security_style));
3859   dict.SetInteger("ssl_cert_status",
3860                   static_cast<int>(entry->GetSSL().cert_status));
3861   dict.SetInteger("insecure_content_status",
3862                   static_cast<int>(entry->GetSSL().content_status));
3863   reply.SendSuccess(&dict);
3864 }
3865
3866 // Sample json input: { "command": "UpdateExtensionsNow" }
3867 // Sample json output: {}
3868 void TestingAutomationProvider::UpdateExtensionsNow(
3869     base::DictionaryValue* args,
3870     IPC::Message* reply_message) {
3871   std::string error;
3872   Browser* browser;
3873   if (!GetBrowserFromJSONArgs(args, &browser, &error)) {
3874     AutomationJSONReply(this, reply_message).SendError(error);
3875     return;
3876   }
3877   ExtensionService* service = extensions::ExtensionSystem::Get(
3878       browser->profile())->extension_service();
3879   if (!service) {
3880     AutomationJSONReply(this, reply_message).SendError(
3881         "No extensions service.");
3882     return;
3883   }
3884
3885   extensions::ExtensionUpdater* updater = service->updater();
3886   if (!updater) {
3887     AutomationJSONReply(this, reply_message).SendError(
3888         "No updater for extensions service.");
3889     return;
3890   }
3891
3892   extensions::ProcessManager* manager =
3893       extensions::ExtensionSystem::Get(browser->profile())->process_manager();
3894   if (!manager) {
3895     AutomationJSONReply(this, reply_message).SendError(
3896         "No extension process manager.");
3897     return;
3898   }
3899
3900   // Create a new observer that waits until the extensions have been fully
3901   // updated (we should not send the reply until after all extensions have
3902   // been updated).  This observer will delete itself.
3903   ExtensionsUpdatedObserver* observer = new ExtensionsUpdatedObserver(
3904       manager, this, reply_message);
3905   extensions::ExtensionUpdater::CheckParams params;
3906   params.install_immediately = true;
3907   params.callback = base::Bind(&ExtensionsUpdatedObserver::UpdateCheckFinished,
3908                                base::Unretained(observer));
3909   updater->CheckNow(params);
3910 }
3911
3912 namespace {
3913
3914 void SendSuccessIfAlive(
3915     base::WeakPtr<AutomationProvider> provider,
3916     IPC::Message* reply_message) {
3917   if (provider.get())
3918     AutomationJSONReply(provider.get(), reply_message).SendSuccess(NULL);
3919 }
3920
3921 }  // namespace
3922
3923 void TestingAutomationProvider::OverrideGeoposition(
3924     base::DictionaryValue* args,
3925     IPC::Message* reply_message) {
3926   double latitude, longitude, altitude;
3927   if (!args->GetDouble("latitude", &latitude) ||
3928       !args->GetDouble("longitude", &longitude) ||
3929       !args->GetDouble("altitude", &altitude)) {
3930     AutomationJSONReply(this, reply_message).SendError(
3931         "Missing or invalid geolocation parameters");
3932     return;
3933   }
3934   content::Geoposition position;
3935   position.latitude = latitude;
3936   position.longitude = longitude;
3937   position.altitude = altitude;
3938   position.accuracy = 0.;
3939   position.timestamp = base::Time::Now();
3940
3941   content::GeolocationProvider::OverrideLocationForTesting(
3942       position,
3943       base::Bind(&SendSuccessIfAlive, AsWeakPtr(), reply_message));
3944 }
3945
3946 // Sample JSON input: { "command": "GetNTPInfo" }
3947 // For output, refer to chrome/test/pyautolib/ntp_model.py.
3948 void TestingAutomationProvider::GetNTPInfo(
3949     Browser* browser,
3950     base::DictionaryValue* args,
3951     IPC::Message* reply_message) {
3952   // This observer will delete itself.
3953   new NTPInfoObserver(this, reply_message);
3954 }
3955
3956 void TestingAutomationProvider::RemoveNTPMostVisitedThumbnail(
3957     Browser* browser,
3958     base::DictionaryValue* args,
3959     IPC::Message* reply_message) {
3960   AutomationJSONReply reply(this, reply_message);
3961   std::string url;
3962   if (!args->GetString("url", &url)) {
3963     reply.SendError("Missing or invalid 'url' key.");
3964     return;
3965   }
3966   history::TopSites* top_sites = browser->profile()->GetTopSites();
3967   if (!top_sites) {
3968     reply.SendError("TopSites service is not initialized.");
3969     return;
3970   }
3971   top_sites->AddBlacklistedURL(GURL(url));
3972   reply.SendSuccess(NULL);
3973 }
3974
3975 void TestingAutomationProvider::RestoreAllNTPMostVisitedThumbnails(
3976     Browser* browser,
3977     base::DictionaryValue* args,
3978     IPC::Message* reply_message) {
3979   AutomationJSONReply reply(this, reply_message);
3980   history::TopSites* top_sites = browser->profile()->GetTopSites();
3981   if (!top_sites) {
3982     reply.SendError("TopSites service is not initialized.");
3983     return;
3984   }
3985   top_sites->ClearBlacklistedURLs();
3986   reply.SendSuccess(NULL);
3987 }
3988
3989 void TestingAutomationProvider::KillRendererProcess(
3990     Browser* browser,
3991     base::DictionaryValue* args,
3992     IPC::Message* reply_message) {
3993   int pid;
3994   uint32 kAccessFlags = base::kProcessAccessTerminate |
3995                         base::kProcessAccessWaitForTermination |
3996                         base::kProcessAccessQueryInformation;
3997
3998   if (!args->GetInteger("pid", &pid)) {
3999     AutomationJSONReply(this, reply_message)
4000         .SendError("'pid' key missing or invalid.");
4001     return;
4002   }
4003   base::ProcessHandle process;
4004   if (!base::OpenProcessHandleWithAccess(static_cast<base::ProcessId>(pid),
4005                                          kAccessFlags,
4006                                          &process)) {
4007     AutomationJSONReply(this, reply_message).SendError(base::StringPrintf(
4008         "Failed to open process handle for pid %d", pid));
4009     return;
4010   }
4011   new RendererProcessClosedObserver(this, reply_message);
4012   base::KillProcess(process, 0, false);
4013   base::CloseProcessHandle(process);
4014 }
4015
4016 bool TestingAutomationProvider::BuildWebKeyEventFromArgs(
4017     base::DictionaryValue* args,
4018     std::string* error,
4019     NativeWebKeyboardEvent* event) {
4020   int type, modifiers;
4021   bool is_system_key;
4022   base::string16 unmodified_text, text;
4023   std::string key_identifier;
4024   if (!args->GetInteger("type", &type)) {
4025     *error = "'type' missing or invalid.";
4026     return false;
4027   }
4028   if (!args->GetBoolean("isSystemKey", &is_system_key)) {
4029     *error = "'isSystemKey' missing or invalid.";
4030     return false;
4031   }
4032   if (!args->GetString("unmodifiedText", &unmodified_text)) {
4033     *error = "'unmodifiedText' missing or invalid.";
4034     return false;
4035   }
4036   if (!args->GetString("text", &text)) {
4037     *error = "'text' missing or invalid.";
4038     return false;
4039   }
4040   if (!args->GetInteger("nativeKeyCode", &event->nativeKeyCode)) {
4041     *error = "'nativeKeyCode' missing or invalid.";
4042     return false;
4043   }
4044   if (!args->GetInteger("windowsKeyCode", &event->windowsKeyCode)) {
4045     *error = "'windowsKeyCode' missing or invalid.";
4046     return false;
4047   }
4048   if (!args->GetInteger("modifiers", &modifiers)) {
4049     *error = "'modifiers' missing or invalid.";
4050     return false;
4051   }
4052   if (args->GetString("keyIdentifier", &key_identifier)) {
4053     base::strlcpy(event->keyIdentifier,
4054                   key_identifier.c_str(),
4055                   blink::WebKeyboardEvent::keyIdentifierLengthCap);
4056   } else {
4057     *error = "'keyIdentifier' missing or invalid.";
4058     return false;
4059   }
4060
4061   if (type == automation::kRawKeyDownType) {
4062     event->type = blink::WebInputEvent::RawKeyDown;
4063   } else if (type == automation::kKeyDownType) {
4064     event->type = blink::WebInputEvent::KeyDown;
4065   } else if (type == automation::kKeyUpType) {
4066     event->type = blink::WebInputEvent::KeyUp;
4067   } else if (type == automation::kCharType) {
4068     event->type = blink::WebInputEvent::Char;
4069   } else {
4070     *error = "'type' refers to an unrecognized keyboard event type";
4071     return false;
4072   }
4073
4074   base::string16 unmodified_text_truncated = unmodified_text.substr(
4075       0, blink::WebKeyboardEvent::textLengthCap - 1);
4076   memcpy(event->unmodifiedText,
4077          unmodified_text_truncated.c_str(),
4078          unmodified_text_truncated.length() + 1);
4079   base::string16 text_truncated = text.substr(
4080       0, blink::WebKeyboardEvent::textLengthCap - 1);
4081   memcpy(event->text, text_truncated.c_str(), text_truncated.length() + 1);
4082
4083   event->modifiers = 0;
4084   if (modifiers & automation::kShiftKeyMask)
4085     event->modifiers |= blink::WebInputEvent::ShiftKey;
4086   if (modifiers & automation::kControlKeyMask)
4087     event->modifiers |= blink::WebInputEvent::ControlKey;
4088   if (modifiers & automation::kAltKeyMask)
4089     event->modifiers |= blink::WebInputEvent::AltKey;
4090   if (modifiers & automation::kMetaKeyMask)
4091     event->modifiers |= blink::WebInputEvent::MetaKey;
4092
4093   event->isSystemKey = is_system_key;
4094   event->timeStampSeconds = base::Time::Now().ToDoubleT();
4095   event->skip_in_browser = true;
4096   return true;
4097 }
4098
4099 void TestingAutomationProvider::SendWebkitKeyEvent(
4100     base::DictionaryValue* args,
4101     IPC::Message* reply_message) {
4102   if (SendErrorIfModalDialogActive(this, reply_message))
4103     return;
4104
4105   NativeWebKeyboardEvent event;
4106   // In the event of an error, BuildWebKeyEventFromArgs handles telling what
4107   // went wrong and sending the reply message; if it fails, we just have to
4108   // stop here.
4109   std::string error;
4110   if (!BuildWebKeyEventFromArgs(args, &error, &event)) {
4111     AutomationJSONReply(this, reply_message).SendError(error);
4112     return;
4113   }
4114
4115   RenderViewHost* view;
4116   if (!GetRenderViewFromJSONArgs(args, profile(), &view, &error)) {
4117     AutomationJSONReply(this, reply_message).SendError(error);
4118     return;
4119   }
4120   new InputEventAckNotificationObserver(this, reply_message, event.type, 1);
4121   view->ForwardKeyboardEvent(event);
4122 }
4123
4124 namespace {
4125
4126 // Gets the active JavaScript modal dialog, or NULL if none.
4127 JavaScriptAppModalDialog* GetActiveJavaScriptModalDialog(
4128     std::string* error_msg) {
4129   AppModalDialogQueue* dialog_queue = AppModalDialogQueue::GetInstance();
4130   if (!dialog_queue->HasActiveDialog() ||
4131       !dialog_queue->active_dialog()->IsJavaScriptModalDialog()) {
4132     *error_msg = "No JavaScriptModalDialog open";
4133     return NULL;
4134   }
4135   return static_cast<JavaScriptAppModalDialog*>(dialog_queue->active_dialog());
4136 }
4137
4138 }  // namespace
4139
4140 void TestingAutomationProvider::GetAppModalDialogMessage(
4141     base::DictionaryValue* args, IPC::Message* reply_message) {
4142   AutomationJSONReply reply(this, reply_message);
4143   std::string error_msg;
4144   JavaScriptAppModalDialog* dialog = GetActiveJavaScriptModalDialog(&error_msg);
4145   if (!dialog) {
4146     reply.SendError(error_msg);
4147     return;
4148   }
4149   base::DictionaryValue result_dict;
4150   result_dict.SetString("message", base::UTF16ToUTF8(dialog->message_text()));
4151   reply.SendSuccess(&result_dict);
4152 }
4153
4154 void TestingAutomationProvider::AcceptOrDismissAppModalDialog(
4155     base::DictionaryValue* args, IPC::Message* reply_message) {
4156   AutomationJSONReply reply(this, reply_message);
4157   bool accept;
4158   if (!args->GetBoolean("accept", &accept)) {
4159     reply.SendError("Missing or invalid 'accept'");
4160     return;
4161   }
4162
4163   std::string error_msg;
4164   JavaScriptAppModalDialog* dialog = GetActiveJavaScriptModalDialog(&error_msg);
4165   if (!dialog) {
4166     reply.SendError(error_msg);
4167     return;
4168   }
4169   if (accept) {
4170     std::string prompt_text;
4171     if (args->GetString("prompt_text", &prompt_text))
4172       dialog->SetOverridePromptText(base::UTF8ToUTF16(prompt_text));
4173     dialog->native_dialog()->AcceptAppModalDialog();
4174   } else {
4175     dialog->native_dialog()->CancelAppModalDialog();
4176   }
4177   reply.SendSuccess(NULL);
4178 }
4179
4180 // Sample JSON input: { "command": "LaunchApp",
4181 //                      "id": "ahfgeienlihckogmohjhadlkjgocpleb" }
4182 // Sample JSON output: {}
4183 void TestingAutomationProvider::LaunchApp(
4184     Browser* browser,
4185     base::DictionaryValue* args,
4186     IPC::Message* reply_message) {
4187   std::string id;
4188   if (!args->GetString("id", &id)) {
4189     AutomationJSONReply(this, reply_message).SendError(
4190         "Must include string id.");
4191     return;
4192   }
4193
4194   ExtensionService* service = extensions::ExtensionSystem::Get(
4195       browser->profile())->extension_service();
4196   if (!service) {
4197     AutomationJSONReply(this, reply_message).SendError(
4198         "No extensions service.");
4199     return;
4200   }
4201
4202   const Extension* extension = service->GetExtensionById(
4203       id, false  /* do not include disabled extensions */);
4204   if (!extension) {
4205     AutomationJSONReply(this, reply_message).SendError(
4206         base::StringPrintf(
4207             "Extension with ID '%s' doesn't exist or is disabled.",
4208             id.c_str()));
4209     return;
4210   }
4211
4212   WebContents* old_contents =
4213       browser->tab_strip_model()->GetActiveWebContents();
4214   if (!old_contents) {
4215     AutomationJSONReply(this, reply_message).SendError(
4216         "Cannot identify selected tab contents.");
4217     return;
4218   }
4219
4220   AppLaunchParams launch_params(profile(), extension, CURRENT_TAB);
4221   // This observer will delete itself.
4222   new AppLaunchObserver(&old_contents->GetController(), this, reply_message,
4223                         launch_params.container);
4224   OpenApplication(launch_params);
4225 }
4226
4227 // Sample JSON input: { "command": "SetAppLaunchType",
4228 //                      "id": "ahfgeienlihckogmohjhadlkjgocpleb",
4229 //                      "launch_type": "pinned" }
4230 // Sample JSON output: {}
4231 void TestingAutomationProvider::SetAppLaunchType(
4232     Browser* browser,
4233     base::DictionaryValue* args,
4234     IPC::Message* reply_message) {
4235   AutomationJSONReply reply(this, reply_message);
4236
4237   std::string id;
4238   if (!args->GetString("id", &id)) {
4239     reply.SendError("Must include string id.");
4240     return;
4241   }
4242
4243   std::string launch_type_str;
4244   if (!args->GetString("launch_type", &launch_type_str)) {
4245     reply.SendError("Must specify app launch type.");
4246     return;
4247   }
4248
4249   ExtensionService* service = extensions::ExtensionSystem::Get(
4250       browser->profile())->extension_service();
4251   if (!service) {
4252     reply.SendError("No extensions service.");
4253     return;
4254   }
4255
4256   const Extension* extension = service->GetExtensionById(
4257       id, true  /* include disabled extensions */);
4258   if (!extension) {
4259     reply.SendError(base::StringPrintf(
4260         "Extension with ID '%s' doesn't exist.", id.c_str()));
4261     return;
4262   }
4263
4264   extensions::LaunchType launch_type;
4265   if (launch_type_str == "pinned") {
4266     launch_type = extensions::LAUNCH_TYPE_PINNED;
4267   } else if (launch_type_str == "regular") {
4268     launch_type = extensions::LAUNCH_TYPE_REGULAR;
4269   } else if (launch_type_str == "fullscreen") {
4270     launch_type = extensions::LAUNCH_TYPE_FULLSCREEN;
4271   } else if (launch_type_str == "window") {
4272     launch_type = extensions::LAUNCH_TYPE_WINDOW;
4273   } else {
4274     reply.SendError(base::StringPrintf(
4275         "Unexpected launch type '%s'.", launch_type_str.c_str()));
4276     return;
4277   }
4278
4279   extensions::SetLaunchType(service, extension->id(), launch_type);
4280   reply.SendSuccess(NULL);
4281 }
4282
4283 // Sample json input: { "command": "GetV8HeapStats",
4284 //                      "tab_index": 0 }
4285 // Refer to GetV8HeapStats() in chrome/test/pyautolib/pyauto.py for
4286 // sample json output.
4287 void TestingAutomationProvider::GetV8HeapStats(
4288     Browser* browser,
4289     base::DictionaryValue* args,
4290     IPC::Message* reply_message) {
4291   WebContents* web_contents;
4292   int tab_index;
4293
4294   if (!args->GetInteger("tab_index", &tab_index)) {
4295     AutomationJSONReply(this, reply_message).SendError(
4296         "Missing 'tab_index' argument.");
4297     return;
4298   }
4299
4300   web_contents = browser->tab_strip_model()->GetWebContentsAt(tab_index);
4301   if (!web_contents) {
4302     AutomationJSONReply(this, reply_message).SendError(base::StringPrintf(
4303         "Could not get WebContents at tab index %d", tab_index));
4304     return;
4305   }
4306
4307   RenderViewHost* render_view = web_contents->GetRenderViewHost();
4308
4309   // This observer will delete itself.
4310   new V8HeapStatsObserver(
4311       this, reply_message,
4312       base::GetProcId(render_view->GetProcess()->GetHandle()));
4313   render_view->Send(new ChromeViewMsg_GetV8HeapStats);
4314 }
4315
4316 // Sample json input: { "command": "GetFPS",
4317 //                      "tab_index": 0 }
4318 // Refer to GetFPS() in chrome/test/pyautolib/pyauto.py for
4319 // sample json output.
4320 void TestingAutomationProvider::GetFPS(
4321     Browser* browser,
4322     base::DictionaryValue* args,
4323     IPC::Message* reply_message) {
4324   WebContents* web_contents;
4325   int tab_index;
4326
4327   if (!args->GetInteger("tab_index", &tab_index)) {
4328     AutomationJSONReply(this, reply_message).SendError(
4329         "Missing 'tab_index' argument.");
4330     return;
4331   }
4332
4333   web_contents = browser->tab_strip_model()->GetWebContentsAt(tab_index);
4334   if (!web_contents) {
4335     AutomationJSONReply(this, reply_message).SendError(base::StringPrintf(
4336         "Could not get WebContents at tab index %d", tab_index));
4337     return;
4338   }
4339
4340   RenderViewHost* render_view = web_contents->GetRenderViewHost();
4341   int routing_id = render_view->GetRoutingID();
4342
4343   // This observer will delete itself.
4344   new FPSObserver(
4345       this, reply_message,
4346       base::GetProcId(render_view->GetProcess()->GetHandle()),
4347       routing_id);
4348   render_view->Send(new ChromeViewMsg_GetFPS(routing_id));
4349 }
4350
4351 void TestingAutomationProvider::IsFullscreenForBrowser(Browser* browser,
4352     base::DictionaryValue* args,
4353     IPC::Message* reply_message) {
4354   base::DictionaryValue dict;
4355   dict.SetBoolean("result",
4356                   browser->fullscreen_controller()->IsFullscreenForBrowser());
4357   AutomationJSONReply(this, reply_message).SendSuccess(&dict);
4358 }
4359
4360 void TestingAutomationProvider::IsFullscreenForTab(Browser* browser,
4361     base::DictionaryValue* args,
4362     IPC::Message* reply_message) {
4363   base::DictionaryValue dict;
4364   dict.SetBoolean("result",
4365       browser->fullscreen_controller()->IsFullscreenForTabOrPending());
4366   AutomationJSONReply(this, reply_message).SendSuccess(&dict);
4367 }
4368
4369 void TestingAutomationProvider::IsMouseLocked(Browser* browser,
4370     base::DictionaryValue* args,
4371     IPC::Message* reply_message) {
4372   base::DictionaryValue dict;
4373   dict.SetBoolean("result", browser->tab_strip_model()->GetActiveWebContents()->
4374       GetRenderViewHost()->GetView()->IsMouseLocked());
4375   AutomationJSONReply(this, reply_message).SendSuccess(&dict);
4376 }
4377
4378 void TestingAutomationProvider::IsMouseLockPermissionRequested(
4379     Browser* browser,
4380     base::DictionaryValue* args,
4381     IPC::Message* reply_message) {
4382   FullscreenExitBubbleType type =
4383       browser->fullscreen_controller()->GetFullscreenExitBubbleType();
4384   bool mouse_lock = false;
4385   fullscreen_bubble::PermissionRequestedByType(type, NULL, &mouse_lock);
4386   base::DictionaryValue dict;
4387   dict.SetBoolean("result", mouse_lock);
4388   AutomationJSONReply(this, reply_message).SendSuccess(&dict);
4389 }
4390
4391 void TestingAutomationProvider::IsFullscreenPermissionRequested(
4392     Browser* browser,
4393     base::DictionaryValue* args,
4394     IPC::Message* reply_message) {
4395   FullscreenExitBubbleType type =
4396       browser->fullscreen_controller()->GetFullscreenExitBubbleType();
4397   bool fullscreen = false;
4398   fullscreen_bubble::PermissionRequestedByType(type, &fullscreen, NULL);
4399   base::DictionaryValue dict;
4400   dict.SetBoolean("result", fullscreen);
4401   AutomationJSONReply(this, reply_message).SendSuccess(&dict);
4402 }
4403
4404 void TestingAutomationProvider::IsFullscreenBubbleDisplayed(Browser* browser,
4405     base::DictionaryValue* args,
4406     IPC::Message* reply_message) {
4407   FullscreenExitBubbleType type =
4408       browser->fullscreen_controller()->GetFullscreenExitBubbleType();
4409   base::DictionaryValue dict;
4410   dict.SetBoolean("result",
4411                   type != FEB_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION);
4412   AutomationJSONReply(this, reply_message).SendSuccess(&dict);
4413 }
4414
4415 void TestingAutomationProvider::IsFullscreenBubbleDisplayingButtons(
4416     Browser* browser,
4417     base::DictionaryValue* args,
4418     IPC::Message* reply_message) {
4419   FullscreenExitBubbleType type =
4420       browser->fullscreen_controller()->GetFullscreenExitBubbleType();
4421   base::DictionaryValue dict;
4422   dict.SetBoolean("result", fullscreen_bubble::ShowButtonsForType(type));
4423   AutomationJSONReply(this, reply_message).SendSuccess(&dict);
4424 }
4425
4426 void TestingAutomationProvider::AcceptCurrentFullscreenOrMouseLockRequest(
4427     Browser* browser,
4428     base::DictionaryValue* args,
4429     IPC::Message* reply_message) {
4430   browser->fullscreen_controller()->OnAcceptFullscreenPermission();
4431   AutomationJSONReply(this, reply_message).SendSuccess(NULL);
4432 }
4433
4434 void TestingAutomationProvider::DenyCurrentFullscreenOrMouseLockRequest(
4435     Browser* browser,
4436     base::DictionaryValue* args,
4437     IPC::Message* reply_message) {
4438   browser->fullscreen_controller()->OnDenyFullscreenPermission();
4439   AutomationJSONReply(this, reply_message).SendSuccess(NULL);
4440 }
4441
4442 void TestingAutomationProvider::WaitForTabToBeRestored(
4443     base::DictionaryValue* args,
4444     IPC::Message* reply_message) {
4445   WebContents* web_contents;
4446   std::string error;
4447   if (!GetTabFromJSONArgs(args, &web_contents, &error)) {
4448     AutomationJSONReply(this, reply_message).SendError(error);
4449     return;
4450   }
4451   NavigationController& controller = web_contents->GetController();
4452   new NavigationControllerRestoredObserver(this, &controller, reply_message);
4453 }
4454
4455 void TestingAutomationProvider::RefreshPolicies(
4456     base::DictionaryValue* args,
4457     IPC::Message* reply_message) {
4458 #if !defined(ENABLE_CONFIGURATION_POLICY)
4459   AutomationJSONReply(this, reply_message).SendError(
4460       "Configuration Policy disabled");
4461 #else
4462   // Some policies (e.g. URLBlacklist) post tasks to other message loops
4463   // before they start enforcing updated policy values; make sure those tasks
4464   // have finished after a policy update.
4465   // Updates of the URLBlacklist are done on IO, after building the blacklist
4466   // on FILE, which is initiated from IO.
4467   base::Closure reply =
4468       base::Bind(SendSuccessReply, AsWeakPtr(), reply_message);
4469   g_browser_process->policy_service()->RefreshPolicies(
4470       base::Bind(PostTask, BrowserThread::IO,
4471           base::Bind(PostTask, BrowserThread::FILE,
4472               base::Bind(PostTask, BrowserThread::IO,
4473                   base::Bind(PostTask, BrowserThread::UI, reply)))));
4474 #endif
4475 }
4476
4477 static int AccessArray(const volatile int arr[], const volatile int *index) {
4478   return arr[*index];
4479 }
4480
4481 void TestingAutomationProvider::SimulateAsanMemoryBug(
4482     base::DictionaryValue* args, IPC::Message* reply_message) {
4483   // This array is volatile not to let compiler optimize us out.
4484   volatile int testarray[3] = {0, 0, 0};
4485
4486   // Send the reply while we can.
4487   AutomationJSONReply(this, reply_message).SendSuccess(NULL);
4488
4489   // Cause Address Sanitizer to abort this process.
4490   volatile int index = 5;
4491   AccessArray(testarray, &index);
4492 }
4493
4494 void TestingAutomationProvider::GetIndicesFromTab(
4495     base::DictionaryValue* args,
4496     IPC::Message* reply_message) {
4497   AutomationJSONReply reply(this, reply_message);
4498   int id_or_handle = 0;
4499   bool has_id = args->HasKey("tab_id");
4500   bool has_handle = args->HasKey("tab_handle");
4501   if (has_id && has_handle) {
4502     reply.SendError(
4503         "Both 'tab_id' and 'tab_handle' were specified. Only one is allowed");
4504     return;
4505   } else if (!has_id && !has_handle) {
4506     reply.SendError("Either 'tab_id' or 'tab_handle' must be specified");
4507     return;
4508   }
4509   if (has_id && !args->GetInteger("tab_id", &id_or_handle)) {
4510     reply.SendError("'tab_id' is invalid");
4511     return;
4512   }
4513   if (has_handle && (!args->GetInteger("tab_handle", &id_or_handle) ||
4514                      !tab_tracker_->ContainsHandle(id_or_handle))) {
4515     reply.SendError("'tab_handle' is invalid");
4516     return;
4517   }
4518   int id = id_or_handle;
4519   if (has_handle) {
4520     SessionTabHelper* session_tab_helper =
4521         SessionTabHelper::FromWebContents(
4522             tab_tracker_->GetResource(id_or_handle)->GetWebContents());
4523     id = session_tab_helper->session_id().id();
4524   }
4525   chrome::BrowserIterator it;
4526   int browser_index = 0;
4527   for (; !it.done(); it.Next(), ++browser_index) {
4528     Browser* browser = *it;
4529     for (int tab_index = 0;
4530          tab_index < browser->tab_strip_model()->count();
4531          ++tab_index) {
4532       WebContents* tab =
4533           browser->tab_strip_model()->GetWebContentsAt(tab_index);
4534       SessionTabHelper* session_tab_helper =
4535           SessionTabHelper::FromWebContents(tab);
4536       if (session_tab_helper->session_id().id() == id) {
4537         base::DictionaryValue dict;
4538         dict.SetInteger("windex", browser_index);
4539         dict.SetInteger("tab_index", tab_index);
4540         reply.SendSuccess(&dict);
4541         return;
4542       }
4543     }
4544   }
4545   reply.SendError("Could not find tab among current browser windows");
4546 }
4547
4548 void TestingAutomationProvider::NavigateToURL(
4549     base::DictionaryValue* args,
4550     IPC::Message* reply_message) {
4551   if (SendErrorIfModalDialogActive(this, reply_message))
4552     return;
4553
4554   int navigation_count;
4555   std::string url, error;
4556   Browser* browser;
4557   WebContents* web_contents;
4558   if (!GetBrowserAndTabFromJSONArgs(args, &browser, &web_contents, &error)) {
4559     AutomationJSONReply(this, reply_message).SendError(error);
4560     return;
4561   }
4562   if (!args->GetString("url", &url)) {
4563     AutomationJSONReply(this, reply_message)
4564         .SendError("'url' missing or invalid");
4565     return;
4566   }
4567   if (!args->GetInteger("navigation_count", &navigation_count)) {
4568     AutomationJSONReply(this, reply_message)
4569         .SendError("'navigation_count' missing or invalid");
4570     return;
4571   }
4572   if (navigation_count > 0) {
4573     new NavigationNotificationObserver(
4574         &web_contents->GetController(), this, reply_message,
4575         navigation_count, false, true);
4576   } else {
4577     AutomationJSONReply(this, reply_message).SendSuccess(NULL);
4578   }
4579   OpenURLParams params(
4580       GURL(url), content::Referrer(), CURRENT_TAB,
4581       content::PageTransitionFromInt(
4582           content::PAGE_TRANSITION_TYPED |
4583           content::PAGE_TRANSITION_FROM_ADDRESS_BAR),
4584       false);
4585   browser->OpenURLFromTab(web_contents, params);
4586 }
4587
4588 void TestingAutomationProvider::GetActiveTabIndexJSON(
4589     base::DictionaryValue* args,
4590     IPC::Message* reply_message) {
4591   AutomationJSONReply reply(this, reply_message);
4592   Browser* browser;
4593   std::string error_msg;
4594   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
4595     reply.SendError(error_msg);
4596     return;
4597   }
4598   int tab_index = browser->tab_strip_model()->active_index();
4599   scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
4600   return_value->SetInteger("tab_index", tab_index);
4601   reply.SendSuccess(return_value.get());
4602 }
4603
4604 void TestingAutomationProvider::AppendTabJSON(base::DictionaryValue* args,
4605                                               IPC::Message* reply_message) {
4606   TabAppendedNotificationObserver* observer = NULL;
4607   int append_tab_response = -1;
4608   Browser* browser;
4609   std::string error_msg, url;
4610   if (!GetBrowserFromJSONArgs(args, &browser, &error_msg)) {
4611     AutomationJSONReply(this, reply_message).SendError(error_msg);
4612     return;
4613   }
4614   if (!args->GetString("url", &url)) {
4615     AutomationJSONReply(this, reply_message)
4616         .SendError("'url' missing or invalid");
4617     return;
4618   }
4619   observer = new TabAppendedNotificationObserver(browser, this, reply_message,
4620                                                  true);
4621   WebContents* contents =
4622       chrome::AddSelectedTabWithURL(browser, GURL(url),
4623                                     content::PAGE_TRANSITION_TYPED);
4624   if (contents) {
4625     append_tab_response = GetIndexForNavigationController(
4626         &contents->GetController(), browser);
4627   }
4628
4629   if (!contents || append_tab_response < 0) {
4630     if (observer) {
4631       observer->ReleaseReply();
4632       delete observer;
4633     }
4634     AutomationJSONReply(this, reply_message).SendError("Failed to append tab.");
4635   }
4636 }
4637
4638 void TestingAutomationProvider::WaitUntilNavigationCompletes(
4639     base::DictionaryValue* args,
4640     IPC::Message* reply_message) {
4641   if (SendErrorIfModalDialogActive(this, reply_message))
4642     return;
4643
4644   std::string error;
4645   Browser* browser;
4646   WebContents* web_contents;
4647   if (!GetBrowserAndTabFromJSONArgs(args, &browser, &web_contents, &error)) {
4648     AutomationJSONReply(this, reply_message).SendError(error);
4649     return;
4650   }
4651   NavigationNotificationObserver* observer =
4652       new NavigationNotificationObserver(&web_contents->GetController(), this,
4653                                          reply_message, 1, true, true);
4654   if (!web_contents->IsLoading()) {
4655     observer->ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS);
4656     return;
4657   }
4658 }
4659
4660 void TestingAutomationProvider::ExecuteJavascriptJSON(
4661     base::DictionaryValue* args,
4662     IPC::Message* reply_message) {
4663   if (SendErrorIfModalDialogActive(this, reply_message))
4664     return;
4665
4666   base::string16 frame_xpath, javascript;
4667   std::string error;
4668   RenderViewHost* render_view;
4669   if (!GetRenderViewFromJSONArgs(args, profile(), &render_view, &error)) {
4670     AutomationJSONReply(this, reply_message).SendError(error);
4671     return;
4672   }
4673   if (!args->GetString("frame_xpath", &frame_xpath)) {
4674     AutomationJSONReply(this, reply_message)
4675         .SendError("'frame_xpath' missing or invalid");
4676     return;
4677   }
4678   if (!args->GetString("javascript", &javascript)) {
4679     AutomationJSONReply(this, reply_message)
4680         .SendError("'javascript' missing or invalid");
4681     return;
4682   }
4683
4684   new DomOperationMessageSender(this, reply_message, true);
4685   ExecuteJavascriptInRenderViewFrame(frame_xpath, javascript, reply_message,
4686                                      render_view);
4687 }
4688
4689 void TestingAutomationProvider::ExecuteJavascriptInRenderView(
4690     base::DictionaryValue* args,
4691     IPC::Message* reply_message) {
4692   base::string16 frame_xpath, javascript, extension_id, url_text;
4693   int render_process_id, render_view_id;
4694   if (!args->GetString("frame_xpath", &frame_xpath)) {
4695     AutomationJSONReply(this, reply_message)
4696         .SendError("'frame_xpath' missing or invalid");
4697     return;
4698   }
4699   if (!args->GetString("javascript", &javascript)) {
4700     AutomationJSONReply(this, reply_message)
4701         .SendError("'javascript' missing or invalid");
4702     return;
4703   }
4704   if (!args->GetInteger("view.render_process_id", &render_process_id)) {
4705     AutomationJSONReply(this, reply_message)
4706         .SendError("'view.render_process_id' missing or invalid");
4707     return;
4708   }
4709   if (!args->GetInteger("view.render_view_id", &render_view_id)) {
4710     AutomationJSONReply(this, reply_message)
4711         .SendError("'view.render_view_id' missing or invalid");
4712     return;
4713   }
4714
4715   RenderViewHost* rvh = RenderViewHost::FromID(render_process_id,
4716                                                render_view_id);
4717   if (!rvh) {
4718     AutomationJSONReply(this, reply_message).SendError(
4719             "A RenderViewHost object was not found with the given view ID.");
4720     return;
4721   }
4722
4723   new DomOperationMessageSender(this, reply_message, true);
4724   ExecuteJavascriptInRenderViewFrame(frame_xpath, javascript, reply_message,
4725                                      rvh);
4726 }
4727
4728 void TestingAutomationProvider::AddDomEventObserver(
4729     base::DictionaryValue* args,
4730     IPC::Message* reply_message) {
4731   if (SendErrorIfModalDialogActive(this, reply_message))
4732     return;
4733
4734   AutomationJSONReply reply(this, reply_message);
4735   std::string event_name;
4736   int automation_id;
4737   bool recurring;
4738   if (!args->GetString("event_name", &event_name)) {
4739     reply.SendError("'event_name' missing or invalid");
4740     return;
4741   }
4742   if (!args->GetInteger("automation_id", &automation_id)) {
4743     reply.SendError("'automation_id' missing or invalid");
4744     return;
4745   }
4746   if (!args->GetBoolean("recurring", &recurring)) {
4747     reply.SendError("'recurring' missing or invalid");
4748     return;
4749   }
4750
4751   if (!automation_event_queue_.get())
4752     automation_event_queue_.reset(new AutomationEventQueue);
4753
4754   int observer_id = automation_event_queue_->AddObserver(
4755       new DomEventObserver(automation_event_queue_.get(), event_name,
4756                            automation_id, recurring));
4757   scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
4758   return_value->SetInteger("observer_id", observer_id);
4759   reply.SendSuccess(return_value.get());
4760 }
4761
4762 void TestingAutomationProvider::RemoveEventObserver(
4763     base::DictionaryValue* args,
4764     IPC::Message* reply_message) {
4765   AutomationJSONReply reply(this, reply_message);
4766   int observer_id;
4767   if (!args->GetInteger("observer_id", &observer_id) ||
4768       !automation_event_queue_.get()) {
4769     reply.SendError("'observer_id' missing or invalid");
4770     return;
4771   }
4772   if (automation_event_queue_->RemoveObserver(observer_id)) {
4773     reply.SendSuccess(NULL);
4774     return;
4775   }
4776   reply.SendError("Invalid observer id.");
4777 }
4778
4779 void TestingAutomationProvider::ClearEventQueue(
4780     base::DictionaryValue* args,
4781     IPC::Message* reply_message) {
4782   automation_event_queue_.reset();
4783   AutomationJSONReply(this, reply_message).SendSuccess(NULL);
4784 }
4785
4786 void TestingAutomationProvider::GetNextEvent(
4787     base::DictionaryValue* args,
4788     IPC::Message* reply_message) {
4789   scoped_ptr<AutomationJSONReply> reply(
4790       new AutomationJSONReply(this, reply_message));
4791   int observer_id;
4792   bool blocking;
4793   if (!args->GetInteger("observer_id", &observer_id)) {
4794     reply->SendError("'observer_id' missing or invalid");
4795     return;
4796   }
4797   if (!args->GetBoolean("blocking", &blocking)) {
4798     reply->SendError("'blocking' missing or invalid");
4799     return;
4800   }
4801   if (!automation_event_queue_.get()) {
4802     reply->SendError(
4803         "No observers are attached to the queue. Did you create any?");
4804     return;
4805   }
4806
4807   // The reply will be freed once a matching event is added to the queue.
4808   automation_event_queue_->GetNextEvent(reply.release(), observer_id, blocking);
4809 }
4810
4811 void TestingAutomationProvider::GoForward(
4812     base::DictionaryValue* args,
4813     IPC::Message* reply_message) {
4814   if (SendErrorIfModalDialogActive(this, reply_message))
4815     return;
4816
4817   WebContents* web_contents;
4818   std::string error;
4819   if (!GetTabFromJSONArgs(args, &web_contents, &error)) {
4820     AutomationJSONReply(this, reply_message).SendError(error);
4821     return;
4822   }
4823   NavigationController& controller = web_contents->GetController();
4824   if (!controller.CanGoForward()) {
4825     base::DictionaryValue dict;
4826     dict.SetBoolean("did_go_forward", false);
4827     AutomationJSONReply(this, reply_message).SendSuccess(&dict);
4828     return;
4829   }
4830   new NavigationNotificationObserver(&controller, this, reply_message,
4831                                      1, false, true);
4832   controller.GoForward();
4833 }
4834
4835 void TestingAutomationProvider::ExecuteBrowserCommandAsyncJSON(
4836     base::DictionaryValue* args,
4837     IPC::Message* reply_message) {
4838   AutomationJSONReply reply(this, reply_message);
4839   int command;
4840   Browser* browser;
4841   std::string error;
4842   if (!GetBrowserFromJSONArgs(args, &browser, &error)) {
4843     reply.SendError(error);
4844     return;
4845   }
4846   if (!args->GetInteger("accelerator", &command)) {
4847     reply.SendError("'accelerator' missing or invalid.");
4848     return;
4849   }
4850   if (!chrome::SupportsCommand(browser, command)) {
4851     reply.SendError(base::StringPrintf("Browser does not support command=%d.",
4852                                        command));
4853     return;
4854   }
4855   if (!chrome::IsCommandEnabled(browser, command)) {
4856     reply.SendError(base::StringPrintf(
4857         "Browser command=%d not enabled.", command));
4858     return;
4859   }
4860   chrome::ExecuteCommand(browser, command);
4861   reply.SendSuccess(NULL);
4862 }
4863
4864 void TestingAutomationProvider::ExecuteBrowserCommandJSON(
4865     base::DictionaryValue* args,
4866     IPC::Message* reply_message) {
4867   int command;
4868   Browser* browser;
4869   std::string error;
4870   if (!GetBrowserFromJSONArgs(args, &browser, &error)) {
4871     AutomationJSONReply(this, reply_message).SendError(error);
4872     return;
4873   }
4874   if (!args->GetInteger("accelerator", &command)) {
4875     AutomationJSONReply(this, reply_message).SendError(
4876         "'accelerator' missing or invalid.");
4877     return;
4878   }
4879   if (!chrome::SupportsCommand(browser, command)) {
4880     AutomationJSONReply(this, reply_message).SendError(
4881         base::StringPrintf("Browser does not support command=%d.", command));
4882     return;
4883   }
4884   if (!chrome::IsCommandEnabled(browser, command)) {
4885     AutomationJSONReply(this, reply_message).SendError(
4886         base::StringPrintf("Browser command=%d not enabled.", command));
4887     return;
4888   }
4889   // First check if we can handle the command without using an observer.
4890   for (size_t i = 0; i < arraysize(kSynchronousCommands); i++) {
4891     if (command == kSynchronousCommands[i]) {
4892       chrome::ExecuteCommand(browser, command);
4893       AutomationJSONReply(this, reply_message).SendSuccess(NULL);
4894       return;
4895     }
4896   }
4897   // Use an observer if we have one, otherwise fail.
4898   if (ExecuteBrowserCommandObserver::CreateAndRegisterObserver(
4899       this, browser, command, reply_message, true)) {
4900     chrome::ExecuteCommand(browser, command);
4901     return;
4902   }
4903   AutomationJSONReply(this, reply_message).SendError(base::StringPrintf(
4904       "Unable to register observer for browser command=%d.", command));
4905 }
4906
4907 void TestingAutomationProvider::IsMenuCommandEnabledJSON(
4908     base::DictionaryValue* args,
4909     IPC::Message* reply_message) {
4910   int command;
4911   Browser* browser;
4912   std::string error;
4913   if (!GetBrowserFromJSONArgs(args, &browser, &error)) {
4914     AutomationJSONReply(this, reply_message).SendError(error);
4915     return;
4916   }
4917   if (!args->GetInteger("accelerator", &command)) {
4918     AutomationJSONReply(this, reply_message).SendError(
4919         "'accelerator' missing or invalid.");
4920     return;
4921   }
4922   base::DictionaryValue dict;
4923   dict.SetBoolean("enabled", chrome::IsCommandEnabled(browser, command));
4924   AutomationJSONReply(this, reply_message).SendSuccess(&dict);
4925 }
4926
4927 void TestingAutomationProvider::GetTabInfo(
4928     base::DictionaryValue* args,
4929     IPC::Message* reply_message) {
4930   AutomationJSONReply reply(this, reply_message);
4931   Browser* browser;
4932   WebContents* tab;
4933   std::string error;
4934   if (GetBrowserAndTabFromJSONArgs(args, &browser, &tab, &error)) {
4935     NavigationEntry* entry = tab->GetController().GetActiveEntry();
4936     if (!entry) {
4937       reply.SendError("Unable to get active navigation entry");
4938       return;
4939     }
4940     base::DictionaryValue dict;
4941     dict.SetString("title", entry->GetTitleForDisplay(std::string()));
4942     dict.SetString("url", entry->GetVirtualURL().spec());
4943     reply.SendSuccess(&dict);
4944   } else {
4945     reply.SendError(error);
4946   }
4947 }
4948
4949 void TestingAutomationProvider::GetTabCountJSON(
4950     base::DictionaryValue* args,
4951     IPC::Message* reply_message) {
4952   AutomationJSONReply reply(this, reply_message);
4953   Browser* browser;
4954   std::string error;
4955   if (!GetBrowserFromJSONArgs(args, &browser, &error)) {
4956     reply.SendError(error);
4957     return;
4958   }
4959   base::DictionaryValue dict;
4960   dict.SetInteger("tab_count", browser->tab_strip_model()->count());
4961   reply.SendSuccess(&dict);
4962 }
4963
4964 void TestingAutomationProvider::GoBack(
4965     base::DictionaryValue* args,
4966     IPC::Message* reply_message) {
4967   if (SendErrorIfModalDialogActive(this, reply_message))
4968     return;
4969
4970   WebContents* web_contents;
4971   std::string error;
4972   if (!GetTabFromJSONArgs(args, &web_contents, &error)) {
4973     AutomationJSONReply(this, reply_message).SendError(error);
4974     return;
4975   }
4976   NavigationController& controller = web_contents->GetController();
4977   if (!controller.CanGoBack()) {
4978     base::DictionaryValue dict;
4979     dict.SetBoolean("did_go_back", false);
4980     AutomationJSONReply(this, reply_message).SendSuccess(&dict);
4981     return;
4982   }
4983   new NavigationNotificationObserver(&controller, this, reply_message,
4984                                      1, false, true);
4985   controller.GoBack();
4986 }
4987
4988 void TestingAutomationProvider::ReloadJSON(
4989     base::DictionaryValue* args,
4990     IPC::Message* reply_message) {
4991   if (SendErrorIfModalDialogActive(this, reply_message))
4992     return;
4993
4994   WebContents* web_contents;
4995   std::string error;
4996   if (!GetTabFromJSONArgs(args, &web_contents, &error)) {
4997     AutomationJSONReply(this, reply_message).SendError(error);
4998     return;
4999   }
5000   NavigationController& controller = web_contents->GetController();
5001   new NavigationNotificationObserver(&controller, this, reply_message,
5002                                      1, false, true);
5003   controller.Reload(false);
5004 }
5005
5006 void TestingAutomationProvider::GetCookiesJSON(
5007     base::DictionaryValue* args, IPC::Message* reply_message) {
5008   automation_util::GetCookiesJSON(this, args, reply_message);
5009 }
5010
5011 void TestingAutomationProvider::DeleteCookieJSON(
5012     base::DictionaryValue* args, IPC::Message* reply_message) {
5013   automation_util::DeleteCookieJSON(this, args, reply_message);
5014 }
5015
5016 void TestingAutomationProvider::SetCookieJSON(
5017     base::DictionaryValue* args, IPC::Message* reply_message) {
5018   automation_util::SetCookieJSON(this, args, reply_message);
5019 }
5020
5021 void TestingAutomationProvider::GetCookiesInBrowserContext(
5022     base::DictionaryValue* args,
5023     IPC::Message* reply_message) {
5024   AutomationJSONReply reply(this, reply_message);
5025   WebContents* web_contents;
5026   std::string value, url_string;
5027   int windex, value_size;
5028   if (!args->GetInteger("windex", &windex)) {
5029     reply.SendError("'windex' missing or invalid.");
5030     return;
5031   }
5032   web_contents = automation_util::GetWebContentsAt(windex, 0);
5033   if (!web_contents) {
5034     reply.SendError("'windex' does not refer to a browser window.");
5035     return;
5036   }
5037   if (!args->GetString("url", &url_string)) {
5038     reply.SendError("'url' missing or invalid.");
5039     return;
5040   }
5041   GURL url(url_string);
5042   if (!url.is_valid()) {
5043     reply.SendError("Invalid url.");
5044     return;
5045   }
5046   automation_util::GetCookies(url, web_contents, &value_size, &value);
5047   if (value_size == -1) {
5048     reply.SendError(
5049         base::StringPrintf("Unable to retrieve cookies for url=%s.",
5050                            url_string.c_str()));
5051     return;
5052   }
5053   base::DictionaryValue dict;
5054   dict.SetString("cookies", value);
5055   reply.SendSuccess(&dict);
5056 }
5057
5058 void TestingAutomationProvider::DeleteCookieInBrowserContext(
5059     base::DictionaryValue* args,
5060     IPC::Message* reply_message) {
5061   AutomationJSONReply reply(this, reply_message);
5062   WebContents* web_contents;
5063   std::string cookie_name, url_string;
5064   int windex;
5065   bool success = false;
5066   if (!args->GetInteger("windex", &windex)) {
5067     reply.SendError("'windex' missing or invalid.");
5068     return;
5069   }
5070   web_contents = automation_util::GetWebContentsAt(windex, 0);
5071   if (!web_contents) {
5072     reply.SendError("'windex' does not refer to a browser window.");
5073     return;
5074   }
5075   if (!args->GetString("cookie_name", &cookie_name)) {
5076     reply.SendError("'cookie_name' missing or invalid.");
5077     return;
5078   }
5079   if (!args->GetString("url", &url_string)) {
5080     reply.SendError("'url' missing or invalid.");
5081     return;
5082   }
5083   GURL url(url_string);
5084   if (!url.is_valid()) {
5085     reply.SendError("Invalid url.");
5086     return;
5087   }
5088   automation_util::DeleteCookie(url, cookie_name, web_contents, &success);
5089   if (!success) {
5090     reply.SendError(
5091         base::StringPrintf("Failed to delete cookie with name=%s for url=%s.",
5092                            cookie_name.c_str(), url_string.c_str()));
5093     return;
5094   }
5095   reply.SendSuccess(NULL);
5096 }
5097
5098 void TestingAutomationProvider::SetCookieInBrowserContext(
5099     base::DictionaryValue* args,
5100     IPC::Message* reply_message) {
5101   AutomationJSONReply reply(this, reply_message);
5102   WebContents* web_contents;
5103   std::string value, url_string;
5104   int windex, response_value = -1;
5105   if (!args->GetInteger("windex", &windex)) {
5106     reply.SendError("'windex' missing or invalid.");
5107     return;
5108   }
5109   web_contents = automation_util::GetWebContentsAt(windex, 0);
5110   if (!web_contents) {
5111     reply.SendError("'windex' does not refer to a browser window.");
5112     return;
5113   }
5114   if (!args->GetString("value", &value)) {
5115     reply.SendError("'value' missing or invalid.");
5116     return;
5117   }
5118   if (!args->GetString("url", &url_string)) {
5119     reply.SendError("'url' missing or invalid.");
5120     return;
5121   }
5122   GURL url(url_string);
5123   if (!url.is_valid()) {
5124     reply.SendError("Invalid url.");
5125     return;
5126   }
5127   automation_util::SetCookie(url, value, web_contents, &response_value);
5128   if (response_value != 1) {
5129     reply.SendError(base::StringPrintf(
5130         "Unable set cookie for url=%s.", url_string.c_str()));
5131     return;
5132   }
5133   reply.SendSuccess(NULL);
5134 }
5135
5136 void TestingAutomationProvider::GetTabIds(
5137     base::DictionaryValue* args, IPC::Message* reply_message) {
5138   base::ListValue* id_list = new base::ListValue();
5139   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
5140     Browser* browser = *it;
5141     for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
5142       int id = SessionTabHelper::FromWebContents(
5143           browser->tab_strip_model()->GetWebContentsAt(i))->session_id().id();
5144       id_list->Append(base::Value::CreateIntegerValue(id));
5145     }
5146   }
5147   base::DictionaryValue dict;
5148   dict.Set("ids", id_list);
5149   AutomationJSONReply(this, reply_message).SendSuccess(&dict);
5150 }
5151
5152 void TestingAutomationProvider::IsTabIdValid(
5153     base::DictionaryValue* args, IPC::Message* reply_message) {
5154   AutomationJSONReply reply(this, reply_message);
5155   int id;
5156   if (!args->GetInteger("id", &id)) {
5157     reply.SendError("'id' missing or invalid");
5158     return;
5159   }
5160   bool is_valid = false;
5161   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
5162     Browser* browser = *it;
5163     for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
5164       WebContents* tab = browser->tab_strip_model()->GetWebContentsAt(i);
5165       SessionTabHelper* session_tab_helper =
5166           SessionTabHelper::FromWebContents(tab);
5167       if (session_tab_helper->session_id().id() == id) {
5168         is_valid = true;
5169         break;
5170       }
5171     }
5172   }
5173   base::DictionaryValue dict;
5174   dict.SetBoolean("is_valid", is_valid);
5175   reply.SendSuccess(&dict);
5176 }
5177
5178 void TestingAutomationProvider::CloseTabJSON(
5179     base::DictionaryValue* args, IPC::Message* reply_message) {
5180   Browser* browser;
5181   WebContents* tab;
5182   std::string error;
5183   bool wait_until_closed = false;  // ChromeDriver does not use this.
5184   args->GetBoolean("wait_until_closed", &wait_until_closed);
5185   // Close tabs synchronously.
5186   if (GetBrowserAndTabFromJSONArgs(args, &browser, &tab, &error)) {
5187     if (wait_until_closed) {
5188       new TabClosedNotificationObserver(this, wait_until_closed, reply_message,
5189                                         true);
5190     }
5191     chrome::CloseWebContents(browser, tab, false);
5192     if (!wait_until_closed)
5193       AutomationJSONReply(this, reply_message).SendSuccess(NULL);
5194     return;
5195   }
5196   // Close other types of views asynchronously.
5197   RenderViewHost* view;
5198   if (!GetRenderViewFromJSONArgs(args, profile(), &view, &error)) {
5199     AutomationJSONReply(this, reply_message).SendError(error);
5200     return;
5201   }
5202   view->ClosePage();
5203   AutomationJSONReply(this, reply_message).SendSuccess(NULL);
5204 }
5205
5206 void TestingAutomationProvider::SetViewBounds(
5207     base::DictionaryValue* args,
5208     IPC::Message* reply_message) {
5209   AutomationJSONReply reply(this, reply_message);
5210   int x, y, width, height;
5211   if (!args->GetInteger("bounds.x", &x) ||
5212       !args->GetInteger("bounds.y", &y) ||
5213       !args->GetInteger("bounds.width", &width) ||
5214       !args->GetInteger("bounds.height", &height)) {
5215     reply.SendError("Missing or invalid 'bounds'");
5216     return;
5217   }
5218   Browser* browser;
5219   std::string error;
5220   if (!GetBrowserFromJSONArgs(args, &browser, &error)) {
5221     reply.SendError(error);
5222     return;
5223   }
5224   BrowserWindow* browser_window = browser->window();
5225   if (browser_window->IsMaximized()) {
5226     browser_window->Restore();
5227   }
5228   browser_window->SetBounds(gfx::Rect(x, y, width, height));
5229   reply.SendSuccess(NULL);
5230 }
5231
5232 void TestingAutomationProvider::MaximizeView(
5233     base::DictionaryValue* args,
5234     IPC::Message* reply_message) {
5235   Browser* browser;
5236   std::string error;
5237   if (!GetBrowserFromJSONArgs(args, &browser, &error)) {
5238     AutomationJSONReply(this, reply_message).SendError(error);
5239     return;
5240   }
5241
5242 #if defined(OS_LINUX)
5243   // Maximization on Linux is asynchronous, so create an observer object to be
5244   // notified upon maximization completion.
5245   new WindowMaximizedObserver(this, reply_message);
5246 #endif  // defined(OS_LINUX)
5247
5248   browser->window()->Maximize();
5249
5250 #if !defined(OS_LINUX)
5251   // Send success reply right away for OS's with synchronous maximize command.
5252   AutomationJSONReply(this, reply_message).SendSuccess(NULL);
5253 #endif  // !defined(OS_LINUX)
5254 }
5255
5256 void TestingAutomationProvider::ActivateTabJSON(
5257     base::DictionaryValue* args,
5258     IPC::Message* reply_message) {
5259   if (SendErrorIfModalDialogActive(this, reply_message))
5260     return;
5261
5262   AutomationJSONReply reply(this, reply_message);
5263   Browser* browser;
5264   WebContents* web_contents;
5265   std::string error;
5266   if (!GetBrowserAndTabFromJSONArgs(args, &browser, &web_contents, &error)) {
5267     reply.SendError(error);
5268     return;
5269   }
5270   TabStripModel* tab_strip = browser->tab_strip_model();
5271   tab_strip->ActivateTabAt(tab_strip->GetIndexOfWebContents(web_contents),
5272                            true);
5273   reply.SendSuccess(NULL);
5274 }
5275
5276 void TestingAutomationProvider::IsPageActionVisible(
5277     base::DictionaryValue* args,
5278     IPC::Message* reply_message) {
5279   AutomationJSONReply reply(this, reply_message);
5280
5281   WebContents* tab;
5282   std::string error;
5283   if (!GetTabFromJSONArgs(args, &tab, &error)) {
5284     reply.SendError(error);
5285     return;
5286   }
5287   Browser* browser = automation_util::GetBrowserForTab(tab);
5288   if (!browser) {
5289     reply.SendError("Tab does not belong to an open browser");
5290     return;
5291   }
5292   const Extension* extension;
5293   if (!GetEnabledExtensionFromJSONArgs(
5294           args, "extension_id", browser->profile(), &extension, &error)) {
5295     reply.SendError(error);
5296     return;
5297   }
5298   ExtensionAction* page_action =
5299       ExtensionActionManager::Get(browser->profile())->
5300       GetPageAction(*extension);
5301   if (!page_action) {
5302     reply.SendError("Extension doesn't have any page action");
5303     return;
5304   }
5305   EnsureTabSelected(browser, tab);
5306
5307   bool is_visible = false;
5308   LocationBarTesting* loc_bar =
5309       browser->window()->GetLocationBar()->GetLocationBarForTesting();
5310   size_t page_action_visible_count =
5311       static_cast<size_t>(loc_bar->PageActionVisibleCount());
5312   for (size_t i = 0; i < page_action_visible_count; ++i) {
5313     if (loc_bar->GetVisiblePageAction(i) == page_action) {
5314       is_visible = true;
5315       break;
5316     }
5317   }
5318   base::DictionaryValue dict;
5319   dict.SetBoolean("is_visible", is_visible);
5320   reply.SendSuccess(&dict);
5321 }
5322
5323 void TestingAutomationProvider::CreateNewAutomationProvider(
5324     base::DictionaryValue* args,
5325     IPC::Message* reply_message) {
5326   AutomationJSONReply reply(this, reply_message);
5327   std::string channel_id;
5328   if (!args->GetString("channel_id", &channel_id)) {
5329     reply.SendError("'channel_id' missing or invalid");
5330     return;
5331   }
5332
5333   AutomationProvider* provider = new TestingAutomationProvider(profile_);
5334   provider->DisableInitialLoadObservers();
5335   // TODO(kkania): Remove this when crbug.com/91311 is fixed.
5336   // Named server channels should ideally be created and closed on the file
5337   // thread, within the IPC channel code.
5338   base::ThreadRestrictions::ScopedAllowIO allow_io;
5339   if (!provider->InitializeChannel(
5340           automation::kNamedInterfacePrefix + channel_id)) {
5341     reply.SendError("Failed to initialize channel: " + channel_id);
5342     return;
5343   }
5344   DCHECK(g_browser_process);
5345   g_browser_process->GetAutomationProviderList()->AddProvider(provider);
5346   reply.SendSuccess(NULL);
5347 }
5348
5349 void TestingAutomationProvider::WaitForTabCountToBecome(
5350     int browser_handle,
5351     int target_tab_count,
5352     IPC::Message* reply_message) {
5353   if (!browser_tracker_->ContainsHandle(browser_handle)) {
5354     AutomationMsg_WaitForTabCountToBecome::WriteReplyParams(reply_message,
5355                                                             false);
5356     Send(reply_message);
5357     return;
5358   }
5359
5360   Browser* browser = browser_tracker_->GetResource(browser_handle);
5361
5362   // The observer will delete itself.
5363   new TabCountChangeObserver(this, browser, reply_message, target_tab_count);
5364 }
5365
5366 void TestingAutomationProvider::WaitForInfoBarCount(
5367     int tab_handle,
5368     size_t target_count,
5369     IPC::Message* reply_message) {
5370   if (!tab_tracker_->ContainsHandle(tab_handle)) {
5371     AutomationMsg_WaitForInfoBarCount::WriteReplyParams(reply_message_, false);
5372     Send(reply_message_);
5373     return;
5374   }
5375
5376   NavigationController* controller = tab_tracker_->GetResource(tab_handle);
5377   if (!controller) {
5378     AutomationMsg_WaitForInfoBarCount::WriteReplyParams(reply_message_, false);
5379     Send(reply_message_);
5380     return;
5381   }
5382
5383   // The delegate will delete itself.
5384   new InfoBarCountObserver(this, reply_message,
5385                            controller->GetWebContents(), target_count);
5386 }
5387
5388 void TestingAutomationProvider::WaitForProcessLauncherThreadToGoIdle(
5389     IPC::Message* reply_message) {
5390   new WaitForProcessLauncherThreadToGoIdleObserver(this, reply_message);
5391 }
5392
5393 void TestingAutomationProvider::OnRemoveProvider() {
5394   if (g_browser_process)
5395     g_browser_process->GetAutomationProviderList()->RemoveProvider(this);
5396 }
5397
5398 void TestingAutomationProvider::EnsureTabSelected(Browser* browser,
5399                                                   WebContents* tab) {
5400   TabStripModel* tab_strip = browser->tab_strip_model();
5401   if (tab_strip->GetActiveWebContents() != tab)
5402     tab_strip->ActivateTabAt(tab_strip->GetIndexOfWebContents(tab), true);
5403 }