Home | History | Annotate | Download | only in automation
      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 
    167 using automation_util::SendErrorIfModalDialogActive;
    168 using content::BrowserChildProcessHostIterator;
    169 using content::BrowserContext;
    170 using content::BrowserThread;
    171 using content::ChildProcessHost;
    172 using content::DownloadItem;
    173 using content::DownloadManager;
    174 using content::InterstitialPage;
    175 using content::NativeWebKeyboardEvent;
    176 using content::NavigationController;
    177 using content::NavigationEntry;
    178 using content::OpenURLParams;
    179 using content::PluginService;
    180 using content::Referrer;
    181 using content::RenderViewHost;
    182 using content::SSLStatus;
    183 using content::WebContents;
    184 using extensions::Extension;
    185 using extensions::ExtensionActionManager;
    186 using extensions::ExtensionList;
    187 using extensions::Manifest;
    188 
    189 namespace {
    190 
    191 // Helper to reply asynchronously if |automation| is still valid.
    192 void 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.
    199 void 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.
    215 void PostTask(BrowserThread::ID id, const base::Closure& callback) {
    216   BrowserThread::PostTask(id, FROM_HERE, callback);
    217 }
    218 
    219 class 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 
    240 const int TestingAutomationProvider::kSynchronousCommands[] = {
    241   IDC_HOME,
    242   IDC_SELECT_NEXT_TAB,
    243   IDC_SELECT_PREVIOUS_TAB,
    244   IDC_SHOW_BOOKMARK_MANAGER,
    245 };
    246 
    247 TestingAutomationProvider::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 
    257 TestingAutomationProvider::~TestingAutomationProvider() {
    258 #if defined(OS_CHROMEOS)
    259   RemoveChromeosObservers();
    260 #endif
    261   BrowserList::RemoveObserver(this);
    262 }
    263 
    264 IPC::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 
    276 void TestingAutomationProvider::OnBrowserAdded(Browser* browser) {
    277 }
    278 
    279 void 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 
    297 void 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 
    308 bool 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 
    387 void 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 
    395 void 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 
    405 void 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 
    418 void 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 
    449 void 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 
    479 void 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 
    488 void 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 
    506 void 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 
    514 void 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 
    545 void 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 
    572 void 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 
    590 void 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 
    596 void TestingAutomationProvider::GetNormalBrowserWindowCount(int* window_count) {
    597   *window_count = static_cast<int>(chrome::GetTabbedBrowserCount(
    598                       profile_, chrome::HOST_DESKTOP_TYPE_NATIVE));
    599 }
    600 
    601 void 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 
    608 void 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 
    629 void 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 
    658 void 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 
    707 void 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 
    733 void 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 
    783 void 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 
    811 void 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 
    839 void 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 
    877 void 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 
    934 void 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 
    943 void 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 
    952 void 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 
    966 void 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 
    982 void 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 
    994 void 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 
   1006 void 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 
   1022 void 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: {}
   1042 void 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.
   1052 void 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 
   1073 void 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 
   1086 void 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 
   1104 void 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 
   1115 void 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 
   1129 void 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 
   1160 void 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 
   1175 void 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 
   1182 void 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 
   1192 void 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|.
   1205 void 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 
   1227 void 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 
   1256 void 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 
   1276 void 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 
   1298 void 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 
   1355 void 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 
   1397 void 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 
   1431 void 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 
   1464 void 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 
   1498 void 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 
   1515 void 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 
   1533 void 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 
   1551 void 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 
   1873 scoped_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 
   1895 void 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 
   1910 void 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 
   1923 void 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 
   1961 void 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
   1980 void 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 
   1998 ListValue* 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: {}
   2055 void 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 
   2111 namespace {
   2112 
   2113 // Gets info about BrowserChildProcessHost. Must run on IO thread to
   2114 // honor the semantics of BrowserChildProcessHostIterator.
   2115 // Used by AutomationProvider::GetBrowserInfo().
   2116 void 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.
   2136 void 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.
   2352 void 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.
   2364 void 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.
   2418 void 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.
   2443 void 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 
   2469 void 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.
   2499 void 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 
   2598 void 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 
   2621 void 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 
   2636 void 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" }
   2656 void 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.
   2673 void 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.
   2706 void 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 }
   2748 void 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.
   2785 void 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 }
   2797 void 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.
   2826 void 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 }
   2848 void 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.
   2882 void 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" }
   2926 void 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.
   2951 void 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" }
   2971 void 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.
   2989 void 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.
   3005 void 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 
   3014 void 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" }
   3065 void 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" }
   3088 void 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 //    {}
   3114 void 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 
   3152 namespace {
   3153 
   3154 // Translates a dictionary password to a PasswordForm struct.
   3155 autofill::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 }
   3215 void 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: {}
   3261 void 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.
   3304 void 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 
   3323 namespace {
   3324 
   3325 // Get the WebContents from a dictionary of arguments.
   3326 WebContents* 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 
   3346 void 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 
   3390 void 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 
   3404 void 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 
   3423 void 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 
   3485 namespace {
   3486 
   3487 ListValue* GetHostPermissions(const Extension* ext, bool effective_perm) {
   3488   extensions::URLPatternSet pattern_set;
   3489   if (effective_perm) {
   3490     pattern_set =
   3491         extensions::PermissionsData::GetEffectiveHostPermissions(ext);
   3492   } else {
   3493     pattern_set = ext->GetActivePermissions()->explicit_hosts();
   3494   }
   3495 
   3496   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 
   3505 ListValue* 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.
   3521 void 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: {}
   3592 void 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.
   3623 void 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.
   3693 void 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.
   3748 void 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 
   3812 void 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 
   3847 void 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: {}
   3869 void 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 
   3913 namespace {
   3914 
   3915 void 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 
   3924 void 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.
   3949 void 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.
   3959 void 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.
   3993 void 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.
   4009 void 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 
   4017 void 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 
   4036 void 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 
   4050 void 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 
   4077 bool 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 
   4160 void 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 
   4185 namespace {
   4186 
   4187 // Gets the active JavaScript modal dialog, or NULL if none.
   4188 JavaScriptAppModalDialog* 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 
   4201 void 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 
   4215 void 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: {}
   4244 void 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: {}
   4292 void 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.
   4349 void 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.
   4382 void 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 
   4413 void 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 
   4422 void 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 
   4431 void 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 
   4440 void 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 
   4453 void 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 
   4466 void 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 
   4477 void 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 
   4488 void 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 
   4496 void 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 
   4504 void 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 
   4517 void 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 
   4539 static int AccessArray(const volatile int arr[], const volatile int *index) {
   4540   return arr[*index];
   4541 }
   4542 
   4543 void 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 
   4556 void 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 
   4610 void 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 
   4650 void 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 
   4666 void 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 
   4700 void 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 
   4722 void 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 
   4751 void 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 
   4790 void 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 
   4824 void 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 
   4841 void TestingAutomationProvider::ClearEventQueue(
   4842     DictionaryValue* args,
   4843     IPC::Message* reply_message) {
   4844   automation_event_queue_.reset();
   4845   AutomationJSONReply(this, reply_message).SendSuccess(NULL);
   4846 }
   4847 
   4848 void 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 
   4873 void 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 
   4897 void 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 
   4926 void 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 
   4969 void 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 
   4989 void 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 
   5011 void 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 
   5026 void 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 
   5050 void 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 
   5068 void TestingAutomationProvider::GetCookiesJSON(
   5069     DictionaryValue* args, IPC::Message* reply_message) {
   5070   automation_util::GetCookiesJSON(this, args, reply_message);
   5071 }
   5072 
   5073 void TestingAutomationProvider::DeleteCookieJSON(
   5074     DictionaryValue* args, IPC::Message* reply_message) {
   5075   automation_util::DeleteCookieJSON(this, args, reply_message);
   5076 }
   5077 
   5078 void TestingAutomationProvider::SetCookieJSON(
   5079     DictionaryValue* args, IPC::Message* reply_message) {
   5080   automation_util::SetCookieJSON(this, args, reply_message);
   5081 }
   5082 
   5083 void 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 
   5120 void 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 
   5160 void 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 
   5198 void 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 
   5214 void 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 
   5240 void 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 
   5268 void 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 
   5294 void 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 
   5318 void 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 
   5338 void 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 
   5385 void 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 
   5411 void 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 
   5428 void 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 
   5450 void TestingAutomationProvider::WaitForProcessLauncherThreadToGoIdle(
   5451     IPC::Message* reply_message) {
   5452   new WaitForProcessLauncherThreadToGoIdleObserver(this, reply_message);
   5453 }
   5454 
   5455 void TestingAutomationProvider::OnRemoveProvider() {
   5456   if (g_browser_process)
   5457     g_browser_process->GetAutomationProviderList()->RemoveProvider(this);
   5458 }
   5459 
   5460 void 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