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