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