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